fix(mail): forceFullSweep on domain-add + 30s idle tick
Domain/display-name adds now force a full re-scan so newly-added gambling senders are caught immediately instead of waiting for the incremental UID window. IMAP-idle NOOP tick lowered 2min -> 30s to close the Junk-folder gap faster (Outlook drops straight into Junk, which IDLE does not watch). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
d31e45e2a8
commit
b757486579
@ -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.
|
// Trade-off: alle 10min full reconnect-cycle. Vertretbar.
|
||||||
const IDLE_RENEW_INTERVAL_MS = 10 * 60 * 1000; // 10 min
|
const IDLE_RENEW_INTERVAL_MS = 10 * 60 * 1000; // 10 min
|
||||||
|
|
||||||
// NOOP-heartbeat alle 2min während IDLE: detect silent-drops (GMX-pattern).
|
// NOOP-heartbeat alle 30s während IDLE: detect silent-drops (GMX-pattern) +
|
||||||
// Wenn NOOP fehlschlägt → close → loop iteriert → reconnect.
|
// Junk-Folder-Sweep-Frequenz. Vertretbar seit Phase-2-Inkremental-Scan:
|
||||||
const IDLE_NOOP_INTERVAL_MS = 2 * 60 * 1000; // 2 min
|
// 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
|
// Token-Refresh-Schwelle: wenn Access-Token in weniger als 5min abläuft, vor
|
||||||
// dem IMAP-Connect refreshen. Verhindert Mid-Session-Expiry.
|
// dem IMAP-Connect refreshen. Verhindert Mid-Session-Expiry.
|
||||||
|
|||||||
@ -249,7 +249,11 @@ export default defineEventHandler(async (event) => {
|
|||||||
$fetch("/api/mail/scan-internal", {
|
$fetch("/api/mail/scan-internal", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: { "x-admin-secret": adminSecret },
|
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) => {
|
}).catch((err: unknown) => {
|
||||||
// Fire-and-forget: Fehler loggen, aber POST-Response nicht blockieren.
|
// Fire-and-forget: Fehler loggen, aber POST-Response nicht blockieren.
|
||||||
// Der Scan ist best-effort — nächster Cron holt nach.
|
// Der Scan ist best-effort — nächster Cron holt nach.
|
||||||
|
|||||||
@ -32,7 +32,7 @@ export default defineEventHandler(async (event) => {
|
|||||||
throw createError({ statusCode: 401, message: "Unauthorized" });
|
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;
|
const userId = body?.userId;
|
||||||
if (!userId)
|
if (!userId)
|
||||||
throw createError({ statusCode: 400, message: "userId missing" });
|
throw createError({ statusCode: 400, message: "userId missing" });
|
||||||
@ -102,7 +102,12 @@ export default defineEventHandler(async (event) => {
|
|||||||
// zustellen dass Blocklist-Updates auch ältere Mails erfassen.
|
// zustellen dass Blocklist-Updates auch ältere Mails erfassen.
|
||||||
// Wichtig: wir behandeln lastUid temporär als 0 — NICHT persistieren.
|
// Wichtig: wir behandeln lastUid temporär als 0 — NICHT persistieren.
|
||||||
// Nach dem Sweep wird der echte maxUid gespeichert + lastFullSweepAt=NOW().
|
// 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 =
|
const needsFullSweep =
|
||||||
|
body.forceFullSweep === true ||
|
||||||
!connection.lastFullSweepAt ||
|
!connection.lastFullSweepAt ||
|
||||||
Date.now() - new Date(connection.lastFullSweepAt).getTime() > 24 * 3_600_000;
|
Date.now() - new Date(connection.lastFullSweepAt).getTime() > 24 * 3_600_000;
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user