web UI: on 401, reload to the login page instead of a cryptic error toast

Sessions are in-memory, so a server restart (or a session TTL expiry) leaves
a still-open SPA tab holding a stale cookie: cached views keep rendering, but
every mutating call 401s and surfaced only as an "authentication required"
toast (e.g. saving Settings) — a confusing dead end.

Make api() treat 401 as "session gone": flash a notice and location.reload(),
which the server answers with the login page. No reload loop (the login page
isn't the SPA), and authed sessions are unaffected.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Luna 2026-06-17 18:09:49 -07:00
parent 615c088b43
commit dd48e899fa

View file

@ -549,7 +549,19 @@ function toggleSidebar(){document.getElementById('sidebar').classList.toggle('op
function closeSidebar(){document.getElementById('sidebar').classList.remove('open');document.getElementById('sidebar-overlay').classList.remove('open')} function closeSidebar(){document.getElementById('sidebar').classList.remove('open');document.getElementById('sidebar-overlay').classList.remove('open')}
/* ── API ────────────────────────────────────────────────────────── */ /* ── API ────────────────────────────────────────────────────────── */
async function api(path,opts){const r=await fetch(path,opts);if(!r.ok)throw new Error(await r.text());return r} async function api(path,opts){
const r=await fetch(path,opts);
// Session gone (expired, or the server restarted — sessions are in-memory):
// the cached SPA would otherwise just throw cryptic "authentication required"
// toasts on every action. Reload instead so the server serves the login page.
if(r.status===401){
try{setStatus('Session expired — reloading to sign in…')}catch{}
location.reload();
throw new Error('session expired');
}
if(!r.ok)throw new Error(await r.text());
return r;
}
/* ── Library ────────────────────────────────────────────────────── */ /* ── Library ────────────────────────────────────────────────────── */
// Cache the ETag of the most-recent library response so subsequent polls // Cache the ETag of the most-recent library response so subsequent polls