diff --git a/backend/imap-idle/index.mjs b/backend/imap-idle/index.mjs index 42e5281..5860d67 100644 --- a/backend/imap-idle/index.mjs +++ b/backend/imap-idle/index.mjs @@ -35,7 +35,14 @@ const ADMIN_SECRET = process.env.NUXT_ADMIN_SECRET || process.env.ADMIN_SECRET || ""; const DB_REFRESH_INTERVAL_MS = 5 * 60 * 1000; // 5 min — neue Connections entdecken -const IDLE_RENEW_INTERVAL_MS = 25 * 60 * 1000; // 25 min — RFC 3501 max = 29min +// IDLE_RENEW von 25min → 10min: GMX dropped IDLE-connections silent vor 25min +// → exists-events kommen nie an + ImapFlow.idle() hängt ohne reject. 10min +// deckt alle bekannten Provider-Timeouts ab (GMX ~10-15min, Gmail ~29min, +// iCloud ~29min, Outlook ~29min). Trade-off: alle 10min full reconnect-cycle. +const IDLE_RENEW_INTERVAL_MS = 10 * 60 * 1000; // 10 min (war 25) +// NOOP-heartbeat alle 2min während IDLE: defensive check ob connection wirklich +// alive ist. Wenn NOOP fehlschlägt → close + reconnect-loop. +const IDLE_NOOP_INTERVAL_MS = 2 * 60 * 1000; // 2 min — silent-drop early-detection const RECONNECT_DELAYS_MS = [1000, 5000, 30_000]; // exponential backoff, danach 60s loop const RECONNECT_LOOP_DELAY_MS = 60 * 1000; @@ -205,15 +212,29 @@ async function runSession(conn) { }; imap.on("exists", onExists); - // IDLE nach 25min erneuern (RFC 3501: Server darf nach 29min droppen) + // IDLE nach 10min erneuern (war 25; GMX dropped silent vor 25min) const renewTimer = setTimeout(() => { - log(conn.email, "idle renewing (25min threshold)"); + log(conn.email, "idle renewing (10min threshold)"); imap.close(); // Unterbricht idle() → Loop iteriert → reconnect }, IDLE_RENEW_INTERVAL_MS); + // NOOP-heartbeat alle 2min: detect silent-IDLE-drops (GMX-pattern). + // Wenn NOOP fehlschlägt → close → loop iteriert → reconnect. + const noopTimer = setInterval(async () => { + try { + await imap.noop(); + // Optional: verbose-log für debugging — aktuell silent + // log(conn.email, "noop ok"); + } catch (err) { + logError(conn.email, "noop failed — connection dead, force reconnect", err); + imap.close(); + } + }, IDLE_NOOP_INTERVAL_MS); + try { await idlePromise; } finally { + clearInterval(noopTimer); clearTimeout(renewTimer); imap.removeListener("exists", onExists); }