fix: prevent stale session warning redirect loops
Some checks failed
i18n / compile-translations (push) Has been cancelled
CI / python-validation (push) Has been cancelled
CI / docker-release-gate (push) Has been cancelled

This commit is contained in:
Md Bayazid Bostame
2026-04-08 14:30:05 +02:00
parent 5b1fd6dc14
commit 9911cc5f82

View File

@@ -18,6 +18,8 @@
let lastConfirmedAt = Date.now(); let lastConfirmedAt = Date.now();
let warningVisible = false; let warningVisible = false;
let keepaliveInFlight = false; let keepaliveInFlight = false;
let timeoutCheckInFlight = false;
let redirectInFlight = false;
function getCsrfToken() { function getCsrfToken() {
const cookie = document.cookie const cookie = document.cookie
@@ -44,11 +46,22 @@
status.textContent = ""; status.textContent = "";
} }
function redirectToLogin() {
if (redirectInFlight) return;
redirectInFlight = true;
window.location.href = config.loginUrl;
}
function readStoredConfirmedAt() { function readStoredConfirmedAt() {
try { try {
const raw = window.localStorage.getItem(storageKey); const raw = window.localStorage.getItem(storageKey);
const parsed = raw ? Number.parseInt(raw, 10) : NaN; const parsed = raw ? Number.parseInt(raw, 10) : NaN;
return Number.isFinite(parsed) ? parsed : null; if (!Number.isFinite(parsed)) return null;
const maxAgeMs = config.idleTimeoutSeconds * 1000;
if (Date.now() - parsed >= maxAgeMs) {
return null;
}
return parsed;
} catch (_error) { } catch (_error) {
return null; return null;
} }
@@ -95,8 +108,9 @@
credentials: "same-origin", credentials: "same-origin",
body: JSON.stringify({ keepalive: true }), body: JSON.stringify({ keepalive: true }),
}); });
if (!response.ok) { const contentType = (response.headers.get("content-type") || "").toLowerCase();
window.location.href = config.loginUrl; if (!response.ok || response.redirected || !contentType.includes("application/json")) {
redirectToLogin();
return; return;
} }
syncConfirmedAt(Date.now(), "self"); syncConfirmedAt(Date.now(), "self");
@@ -108,12 +122,46 @@
hideStatus(); hideStatus();
}, 1200); }, 1200);
} catch (_error) { } catch (_error) {
window.location.href = config.loginUrl; redirectToLogin();
} finally { } finally {
keepaliveInFlight = false; keepaliveInFlight = false;
} }
} }
async function confirmSessionOrRedirect() {
if (timeoutCheckInFlight || keepaliveInFlight || redirectInFlight) return;
timeoutCheckInFlight = true;
try {
const response = await fetch(config.keepaliveUrl, {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-CSRFToken": getCsrfToken(),
"X-Requested-With": "XMLHttpRequest",
},
credentials: "same-origin",
body: JSON.stringify({ keepalive: true, timeout_check: true }),
});
const contentType = (response.headers.get("content-type") || "").toLowerCase();
if (!response.ok || response.redirected || !contentType.includes("application/json")) {
try {
window.localStorage.removeItem(storageKey);
} catch (_error) {
// Ignore storage cleanup failures.
}
redirectToLogin();
return;
}
syncConfirmedAt(Date.now(), "self");
hideWarning();
hideStatus();
} catch (_error) {
redirectToLogin();
} finally {
timeoutCheckInFlight = false;
}
}
const storedConfirmedAt = readStoredConfirmedAt(); const storedConfirmedAt = readStoredConfirmedAt();
if (storedConfirmedAt) { if (storedConfirmedAt) {
lastConfirmedAt = storedConfirmedAt; lastConfirmedAt = storedConfirmedAt;
@@ -149,7 +197,7 @@
const elapsedSeconds = Math.floor((Date.now() - lastConfirmedAt) / 1000); const elapsedSeconds = Math.floor((Date.now() - lastConfirmedAt) / 1000);
const secondsLeft = config.idleTimeoutSeconds - elapsedSeconds; const secondsLeft = config.idleTimeoutSeconds - elapsedSeconds;
if (secondsLeft <= 0) { if (secondsLeft <= 0) {
window.location.href = config.loginUrl; confirmSessionOrRedirect();
return; return;
} }
if (secondsLeft <= warningLeadSeconds) { if (secondsLeft <= warningLeadSeconds) {