diff --git a/backend/imap-idle/index.mjs b/backend/imap-idle/index.mjs index d41af49..b5ce820 100644 --- a/backend/imap-idle/index.mjs +++ b/backend/imap-idle/index.mjs @@ -59,9 +59,12 @@ const DB_REFRESH_INTERVAL_MS = 5 * 60 * 1000; // 5 min — neue Connections en // Trade-off: alle 10min full reconnect-cycle. Vertretbar. const IDLE_RENEW_INTERVAL_MS = 10 * 60 * 1000; // 10 min -// NOOP-heartbeat alle 2min während IDLE: detect silent-drops (GMX-pattern). -// Wenn NOOP fehlschlägt → close → loop iteriert → reconnect. -const IDLE_NOOP_INTERVAL_MS = 2 * 60 * 1000; // 2 min +// NOOP-heartbeat alle 30s während IDLE: detect silent-drops (GMX-pattern) + +// Junk-Folder-Sweep-Frequenz. Vertretbar seit Phase-2-Inkremental-Scan: +// leere SEARCH-Response (keine neuen UIDs) → skip ohne IMAP-Fetch, günstig. +// Server-Headroom (CPX42) deckt die höhere Trigger-Rate ab. +// Kein Konflikt mit IDLE_RENEW_INTERVAL_MS (10min) — beide laufen unabhängig. +const IDLE_NOOP_INTERVAL_MS = 30 * 1000; // 30 s // Token-Refresh-Schwelle: wenn Access-Token in weniger als 5min abläuft, vor // dem IMAP-Connect refreshen. Verhindert Mid-Session-Expiry. diff --git a/backend/server/api/custom-domains/index.post.ts b/backend/server/api/custom-domains/index.post.ts index c5d4e3b..3680ad1 100644 --- a/backend/server/api/custom-domains/index.post.ts +++ b/backend/server/api/custom-domains/index.post.ts @@ -249,7 +249,11 @@ export default defineEventHandler(async (event) => { $fetch("/api/mail/scan-internal", { method: "POST", headers: { "x-admin-secret": adminSecret }, - body: { userId: user.id }, + // forceFullSweep: Alt-Mails der gerade hinzugefügten Domain werden sofort + // erfasst — unabhängig davon ob lastFullSweepAt < 24h. Ohne dieses Flag + // würde der inkrementelle Pfad greifen (UIDs > lastUid) und bereits vor- + // handene Mails dieser Domain übersehen. + body: { userId: user.id, forceFullSweep: true }, }).catch((err: unknown) => { // Fire-and-forget: Fehler loggen, aber POST-Response nicht blockieren. // Der Scan ist best-effort — nächster Cron holt nach. diff --git a/backend/server/api/mail/scan-internal.post.ts b/backend/server/api/mail/scan-internal.post.ts index 4f64815..801e2f2 100644 --- a/backend/server/api/mail/scan-internal.post.ts +++ b/backend/server/api/mail/scan-internal.post.ts @@ -32,7 +32,7 @@ export default defineEventHandler(async (event) => { throw createError({ statusCode: 401, message: "Unauthorized" }); } - const body = (await readBody(event)) as { userId?: string }; + const body = (await readBody(event)) as { userId?: string; forceFullSweep?: boolean }; const userId = body?.userId; if (!userId) throw createError({ statusCode: 400, message: "userId missing" }); @@ -102,7 +102,12 @@ export default defineEventHandler(async (event) => { // zustellen dass Blocklist-Updates auch ältere Mails erfassen. // Wichtig: wir behandeln lastUid temporär als 0 — NICHT persistieren. // Nach dem Sweep wird der echte maxUid gespeichert + lastFullSweepAt=NOW(). + // + // forceFullSweep: explizit erzwungener Full-Sweep (z.B. nach Custom-Domain-Add). + // Damit werden Alt-Mails der neuen Domain sofort erfasst, unabhängig davon + // ob der letzte Full-Sweep < 24h her ist. const needsFullSweep = + body.forceFullSweep === true || !connection.lastFullSweepAt || Date.now() - new Date(connection.lastFullSweepAt).getTime() > 24 * 3_600_000;