(function () { const fallbackTimeoutMs = 10 * 60 * 1000; const syncIntervalMs = 15 * 1000; let authenticated = false; let timeoutMs = fallbackTimeoutMs; let idleTimer = null; let pendingSyncTimer = null; let lastSyncAt = 0; const nativeFetch = window.fetch ? window.fetch.bind(window) : null; function scheduleLogout(remainingMs = timeoutMs) { if (!authenticated) return; window.clearTimeout(idleTimer); idleTimer = window.setTimeout(() => { window.location.assign("/auth/logout?reason=idle"); }, Math.max(remainingMs, 1000)); } async function syncActivity() { if (!authenticated) return; window.clearTimeout(pendingSyncTimer); pendingSyncTimer = null; lastSyncAt = Date.now(); try { const response = await nativeFetch("/api/auth/activity", { method: "POST", cache: "no-store", credentials: "include", headers: {"X-Requested-With": "idle-activity"}, }); if (response.status === 401) { authenticated = false; window.location.assign("/auth/logout?reason=idle"); } } catch (error) { console.debug("Session activity sync deferred.", error); } } function onActualActivity() { if (!authenticated) return; scheduleLogout(); const elapsed = Date.now() - lastSyncAt; if (elapsed >= syncIntervalMs) { void syncActivity(); return; } if (!pendingSyncTimer) { pendingSyncTimer = window.setTimeout(syncActivity, syncIntervalMs - elapsed); } } async function initialize() { try { const response = await nativeFetch("/api/auth/me", {cache: "no-store", credentials: "include"}); if (!response.ok) return; const data = await response.json(); authenticated = Boolean(data.user); if (!authenticated) return; timeoutMs = Math.max(Number(data.idle_timeout_seconds || 600) * 1000, 1000); const remainingMs = Math.max( Number(data.idle_remaining_seconds || data.idle_timeout_seconds || 600) * 1000, 1000, ); lastSyncAt = Date.now(); scheduleLogout(remainingMs); } catch (error) { console.debug("Session idle timer unavailable.", error); } } if (nativeFetch) { window.fetch = async (...args) => { const response = await nativeFetch(...args); const url = String(args[0]?.url || args[0] || ""); if (authenticated && response.ok && !url.includes("/api/auth/activity")) { onActualActivity(); } return response; }; } ["mousemove", "pointerdown", "click", "keydown", "wheel", "touchstart"].forEach((eventName) => { window.addEventListener(eventName, onActualActivity, {passive: true}); }); document.addEventListener("visibilitychange", () => { if (!document.hidden) onActualActivity(); }); void initialize(); })();