fix(mail-ingest): the settings card makes the person's own address on first look

The own address was made only when asked for, and the card had no way to ask
before one existed, so it showed none. Once a template is set and the
person's import is on, the first look provisions it (idempotent; read after).
This commit is contained in:
Maurice
2026-11-13 20:32:31 +01:00
parent 6789ee1cb6
commit 4337969c2c
3 changed files with 20 additions and 2 deletions
@@ -43,6 +43,18 @@ export class MailIngestAddressService {
return this.view(await this.addresses.findLive(userId, null));
}
/**
* The person's own address as the settings card shows it: made on the first
* look once an address template is set and their import is on, so nobody has
* to ask for one before forwarding; read as it is otherwise.
*
* @txStandalone an idempotent lazy provision: one row, made once, then only read.
*/
async ownAddressForCard(userId: number, importOn: boolean): Promise<MailIngestAddress | null> {
if (importOn && (await this.settings.addressTemplate())) return this.view(await this.ensureOwn(userId));
return this.ownAddress(userId);
}
/** A new own address for the person, the old one stops working. */
async renewOwn(userId: number): Promise<MailIngestAddress | null> {
await this.requireTemplate();
@@ -69,7 +69,7 @@ export class MailIngestInboxService {
}
return {
available: true,
address: await this.addresses.ownAddress(userId),
address: await this.addresses.ownAddressForCard(userId, prefs.enabled),
prefs,
// Whether AI reading can work here at all (the AI Parsing addon and a connection), not the person's own
// switch: with it folded in, switching AI off greyed the switch out and it could never come back on.
+7 -1
View File
@@ -164,7 +164,12 @@ describe('/api/mail-ingest e2e (real guards + snapshot SQLite)', () => {
it('MAIL-E2E-002: the settings card, its switches and a renewed own address', async () => {
const card = await request(server).get('/api/mail-ingest/settings').set(as(USER));
expect(card.status).toBe(200);
expect(card.body).toMatchObject({ available: true, address: null, ai_available: false, pending: 0 });
expect(card.body).toMatchObject({ available: true, ai_available: false, pending: 0 });
// The first look makes the person's own address, so nobody has to ask for one before forwarding.
const first = card.body.address.address as string;
expect(first).toMatch(/^trek\+[a-z2-7]{24}@mail\.example\.com$/);
const again = await request(server).get('/api/mail-ingest/settings').set(as(USER));
expect(again.body.address.address).toBe(first);
expect((await request(server).patch('/api/mail-ingest/settings').set(as(USER)).send({})).status).toBe(400);
const off = await request(server).patch('/api/mail-ingest/settings').set(as(USER)).send({ auto_add: false });
@@ -174,6 +179,7 @@ describe('/api/mail-ingest e2e (real guards + snapshot SQLite)', () => {
const renewed = await request(server).post('/api/mail-ingest/address/renew').set(as(USER));
expect(renewed.status).toBe(200);
expect(renewed.body.address.address).toMatch(/^trek\+[a-z2-7]{24}@mail\.example\.com$/);
expect(renewed.body.address.address).not.toBe(first);
});
it('MAIL-E2E-003: an uploaded mail waits in the inbox, is added by hand and undone, for its person', async () => {