// CentralUI-020: client-side helper for the SessionExpiry component's // idle-logout check. Pings the given URL and reports the HTTP status code so // the Blazor component can redirect to /login once the server reports 401. // // `redirect: "manual"` ensures a 302 (should the endpoint ever start // redirecting) is reported as an opaque status rather than being followed // transparently — the component only ever wants to see the real outcome. export async function ping(url) { try { const resp = await fetch(url, { method: "GET", credentials: "same-origin", cache: "no-store", redirect: "manual", headers: { "X-Requested-With": "XMLHttpRequest" } }); return resp.status; } catch { // Network failure: report 0 so the caller treats it as inconclusive // and retries on the next poll rather than logging the user out. return 0; } } // SignalR event traffic and automatic pings are not user activity. Only trusted // browser input can schedule this antiforgery-protected request. let lastReported = 0; let reporting = false; async function recordInput(event) { if (!event.isTrusted || reporting || Date.now() - lastReported < 15000) return; const token = document.querySelector('input[name="__RequestVerificationToken"]')?.value; if (!token) return; reporting = true; try { const response = await fetch('/auth/activity', { method: 'POST', credentials: 'same-origin', cache: 'no-store', headers: { 'RequestVerificationToken': token } }); if (response.ok) lastReported = Date.now(); } catch { /* A failed request never advances the server's idle clock. */ } finally { reporting = false; } } for (const name of ['pointerdown', 'keydown', 'wheel', 'touchstart']) document.addEventListener(name, recordInput, { passive: true });