Files
puttaalu 94859fb0d6 Show management links and a signed-in indicator when logged in
Add GET /api/session, an unguarded probe that reports whether the request
carries valid credentials. It returns 200 with {"authenticated":bool} and never
sends WWW-Authenticate, so the static public pages can check login state without
triggering the browser's Basic-auth dialog. To keep it from being a
password-guessing oracle that bypasses the /upload limiter, a request with no
credentials is answered cheaply as false, while wrong credentials go through the
same failed-attempt limiter (429 once drained).

static/session.js, loaded by every page template, calls it and, only when
authenticated, builds a fixed toolbar: Upload / Manage links, an "Edit this"
link on photo pages (via <body data-slug>, deep-linking to /manage?edit=<slug>,
which scrolls to and focuses that row), a signed-in indicator, and a log-out
control. Logged-out visitors see nothing and get no dialog.

Log out is best-effort: HTTP Basic has no reliable sign-out (the browser caches
credentials until the window closes), so it overwrites the cached credential and
reloads, which works in some browsers but not all. A dependable logout would
need cookie sessions.
2026-10-18 22:52:59 +02:00

66 lines
2.3 KiB
JavaScript

// Shows a management toolbar + signed-in indicator on every page when the
// browser holds valid credentials. The public gallery is static and carries no
// markup for this; the bar is built here only after /api/session confirms the
// viewer is authenticated, so logged-out visitors see nothing and get no login
// dialog.
//
// A page may set <body data-slug="..."> (the photo pages do) to get an
// "Edit this" link straight to that post in /manage.
(function () {
fetch("/api/session", { headers: { Accept: "application/json" }, cache: "no-store" })
.then((r) => (r.ok ? r.json() : { authenticated: false }))
.then((s) => {
if (s && s.authenticated) buildBar();
})
.catch(() => {});
function el(tag, cls, text) {
const e = document.createElement(tag);
if (cls) e.className = cls;
if (text != null) e.textContent = text;
return e;
}
function buildBar() {
document.body.classList.add("authed");
const bar = el("div", "session-bar");
const path = location.pathname.replace(/\/$/, "");
const link = (href, label) => {
const a = el("a", null, label);
a.href = href;
bar.appendChild(a);
};
if (path !== "/upload") link("/upload", "Upload");
if (path !== "/manage") link("/manage", "Manage");
const slug = document.body.dataset.slug;
if (slug) link("/manage?edit=" + encodeURIComponent(slug), "Edit this");
bar.appendChild(el("span", "session-dot", "● signed in"));
// Best-effort logout. HTTP Basic has no real sign-out — the browser caches
// credentials and clears them reliably only when the window closes — so
// this overwrites the cached credential with a throwaway one and reloads.
// It works in some browsers and not others; a dependable logout would need
// cookie sessions.
const out = el("a", "session-out", "log out");
out.href = "#";
out.title = "Best-effort — HTTP Basic auth has no reliable logout";
out.addEventListener("click", (e) => {
e.preventDefault();
fetch("/api/session", {
headers: { Authorization: "Basic " + btoa("logout:" + Date.now()) },
cache: "no-store",
})
.catch(() => {})
.finally(() => setTimeout(() => location.reload(), 150));
});
bar.appendChild(out);
document.body.appendChild(bar);
}
})();