feat: add session expiry warning
Some checks failed
CI / python-validation (push) Has been cancelled
CI / docker-release-gate (push) Has been cancelled
i18n / compile-translations (push) Has been cancelled

This commit is contained in:
Md Bayazid Bostame
2026-04-01 22:04:31 +02:00
parent 5fab01d57a
commit e47b1b3110
7 changed files with 188 additions and 1 deletions

View File

@@ -0,0 +1,82 @@
(function () {
const config = window.WorkdockSessionConfig;
if (!config || !config.idleTimeoutSeconds) return;
const warningLeadSeconds = Math.min(300, Math.max(60, Math.floor(config.idleTimeoutSeconds / 6)));
const modal = document.getElementById("app-session-warning-modal");
const countdown = document.getElementById("app-session-warning-countdown");
const extendButton = document.getElementById("app-session-warning-extend");
if (!modal || !countdown || !extendButton) return;
let lastConfirmedAt = Date.now();
let warningVisible = false;
let keepaliveInFlight = false;
function getCsrfToken() {
const cookie = document.cookie
.split(";")
.map((item) => item.trim())
.find((item) => item.startsWith("csrftoken="));
return cookie ? decodeURIComponent(cookie.split("=")[1]) : "";
}
function hideWarning() {
if (!warningVisible) return;
modal.hidden = true;
modal.setAttribute("aria-hidden", "true");
warningVisible = false;
}
function showWarning(secondsLeft) {
countdown.textContent = `Noch etwa ${secondsLeft} Sekunden bis zur automatischen Abmeldung.`;
if (warningVisible) return;
modal.hidden = false;
modal.setAttribute("aria-hidden", "false");
warningVisible = true;
}
async function sendKeepalive() {
if (keepaliveInFlight) return;
keepaliveInFlight = 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 }),
});
if (!response.ok) {
window.location.href = config.loginUrl;
return;
}
lastConfirmedAt = Date.now();
hideWarning();
} catch (_error) {
window.location.href = config.loginUrl;
} finally {
keepaliveInFlight = false;
}
}
function tick() {
const elapsedSeconds = Math.floor((Date.now() - lastConfirmedAt) / 1000);
const secondsLeft = config.idleTimeoutSeconds - elapsedSeconds;
if (secondsLeft <= 0) {
window.location.href = config.loginUrl;
return;
}
if (secondsLeft <= warningLeadSeconds) {
showWarning(secondsLeft);
}
}
extendButton.addEventListener("click", function () {
sendKeepalive();
});
setInterval(tick, 1000);
})();