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:
1 parent
c4aa345914
commit
94859fb0d6
13 files changed
+163
-1
No files matched your search
@@ -92,5 +92,6 @@
|
||||
io.observe(btn);
|
||||
})();
|
||||
</script>
|
||||
<script src="/static/session.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -50,5 +50,6 @@
|
||||
<footer class="site-footer">
|
||||
© 2000–{{.Year}} {{.Author}}. All rights reserved.
|
||||
</footer>
|
||||
<script src="/static/session.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -46,5 +46,6 @@
|
||||
<footer class="site-footer">
|
||||
© 2000–{{.Year}} {{.Author}}. All rights reserved.
|
||||
</footer>
|
||||
<script src="/static/session.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -103,5 +103,6 @@
|
||||
io.observe(btn);
|
||||
})();
|
||||
</script>
|
||||
<script src="/static/session.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -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>
|
||||
@@ -35,7 +35,7 @@
|
||||
{{if .Older}}<link rel="prefetch" href="/photo/{{.Older.Slug}}/">
|
||||
<link rel="prefetch" as="image" href="/{{.Older.Medium}}">{{end}}
|
||||
</head>
|
||||
<body>
|
||||
<body data-slug="{{.Photo.Slug}}">
|
||||
<div class="photo-page">
|
||||
<nav class="photo-nav">
|
||||
<a href="/" class="back-btn">← Back</a>
|
||||
@@ -73,5 +73,6 @@
|
||||
</footer>
|
||||
|
||||
<script src="/static/photo-nav.js" defer></script>
|
||||
<script src="/static/session.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -111,5 +111,6 @@
|
||||
io.observe(btn);
|
||||
})();
|
||||
</script>
|
||||
<script src="/static/session.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -291,5 +291,6 @@
|
||||
submitBtn.textContent = 'Upload photo';
|
||||
});
|
||||
</script>
|
||||
<script src="/static/session.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in new issue
Block a user