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.
This commit is contained in:
puttaalu committed 2026-10-18 22:52:59 +02:00
1 parent c4aa345914
commit 94859fb0d6
13 files changed
+163 -1

No files matched your search

+17
View File
@@ -83,6 +83,7 @@
#toast.ok { display: block; background: #1d2e1d; color: #b8bb26; border: 1px solid #344d28; }
#toast.err { display: block; background: #2e1d1d; color: #fb4934; border: 1px solid #5a2828; }
#empty { color: var(--fg4); font-family: var(--serif); padding: 40px 0; }
.row-hl { outline: 2px solid var(--aqua, #8ec07c); outline-offset: 4px; transition: outline-color .4s; }
</style>
</head>
<body>
@@ -185,12 +186,27 @@
countEl.textContent = photos.length + (photos.length === 1 ? ' post' : ' posts');
emptyEl.style.display = photos.length ? 'none' : 'block';
refreshBulk();
maybeFocusEdit();
} catch (err) {
countEl.textContent = '';
flash('Could not load posts: ' + err.message, false);
}
}
// Deep link from a photo page's "Edit this": /manage?edit=<slug> scrolls to
// that row, focuses its caption and highlights it briefly.
function maybeFocusEdit() {
const slug = new URLSearchParams(location.search).get('edit');
if (!slug) return;
const row = [...list.querySelectorAll('.row')].find(r => r.dataset.slug === slug);
if (!row) return;
row.scrollIntoView({ behavior: 'smooth', block: 'center' });
const cap = row.querySelector('.f-caption');
if (cap) cap.focus();
row.classList.add('row-hl');
setTimeout(() => row.classList.remove('row-hl'), 2000);
}
async function post(url, body) {
const res = await fetch(url, { method: 'POST', body });
if (!res.ok) throw new Error((await res.text()).trim() || ('HTTP ' + res.status));
@@ -345,5 +361,6 @@
load();
</script>
<script src="/static/session.js"></script>
</body>
</html>