From 4337969c2cb6fa4299ac9a8cd363be75d1f8c1fe Mon Sep 17 00:00:00 2001 From: Maurice Date: Fri, 13 Nov 2026 20:32:31 +0100 Subject: [PATCH] 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). --- .../nest/mail-ingest/mail-ingest-address.service.ts | 12 ++++++++++++ .../nest/mail-ingest/mail-ingest-inbox.service.ts | 2 +- server/tests/e2e/mail-ingest.e2e.test.ts | 8 +++++++- 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/server/src/nest/mail-ingest/mail-ingest-address.service.ts b/server/src/nest/mail-ingest/mail-ingest-address.service.ts index 9f751b229..974ab39a2 100644 --- a/server/src/nest/mail-ingest/mail-ingest-address.service.ts +++ b/server/src/nest/mail-ingest/mail-ingest-address.service.ts @@ -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 { + 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 { await this.requireTemplate(); diff --git a/server/src/nest/mail-ingest/mail-ingest-inbox.service.ts b/server/src/nest/mail-ingest/mail-ingest-inbox.service.ts index 146095d1c..0e0eaa681 100644 --- a/server/src/nest/mail-ingest/mail-ingest-inbox.service.ts +++ b/server/src/nest/mail-ingest/mail-ingest-inbox.service.ts @@ -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. diff --git a/server/tests/e2e/mail-ingest.e2e.test.ts b/server/tests/e2e/mail-ingest.e2e.test.ts index 9432396d0..7bc6814d1 100644 --- a/server/tests/e2e/mail-ingest.e2e.test.ts +++ b/server/tests/e2e/mail-ingest.e2e.test.ts @@ -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 () => {