(opus assessment) Fix auth wall flash, broken multi-user write API, and single-user redirect loop
Auth wall (Base.astro): set data-auth-pending on <body> at SSG time and hide
it with inline CSS before any JS runs; remove the attribute after /api/me
resolves. Eliminates the flash of protected content on private instances.
Multi-user write API (serve/server.py): the previous _apply_sidecar_edit and
strava_sync imports from bincio.edit.server were broken (those names don't
exist as module-level exports) and the Strava sync mutated a global data_dir,
making concurrent requests from different users racy. Fix: extract both
operations into bincio/edit/ops.py as pure functions that take data_dir
explicitly. Both edit/server.py and serve/server.py now import from there.
Security: add rate limiting to POST /api/register (5 attempts / 15 min / IP,
separate bucket from login). Add _check_id() activity ID validation to both
GET and POST /api/activity/{id} in serve/server.py.
Single-user mode: _write_root_manifest now forces instance.private=false when
no instance.db exists, even if a previous run wrote true. Prevents the auth
wall from firing and redirecting to /login/ when bincio serve isn't running.
ActivityFeed: skip filterHandle when profileIndexUrl is set (per-user profile
pages load the right shard directly; activities have no handle tag at that
point, so the filter was producing an empty feed). Fix handle links to point
to /u/{handle}/ instead of /{handle}/. Fix <a>-inside-<a> Svelte warning by
converting the inner handle link to a <button>.
This commit is contained in:
@@ -66,7 +66,13 @@
|
||||
: `${base}data/index.json`;
|
||||
const index = await loadIndex(base, indexUrl);
|
||||
let activities = index.activities.filter(a => a.privacy !== 'private');
|
||||
if (filterHandle) activities = activities.filter(a => a.handle === filterHandle);
|
||||
// filterHandle only applies when loading the root manifest (multi-user feed).
|
||||
// When profileIndexUrl is set we already loaded the right user's shard directly —
|
||||
// activities from a direct shard fetch have no handle tag, so the filter would
|
||||
// remove everything.
|
||||
if (filterHandle && !profileIndexUrl) {
|
||||
activities = activities.filter(a => a.handle === filterHandle);
|
||||
}
|
||||
all = activities;
|
||||
} catch (e: any) {
|
||||
error = e.message;
|
||||
@@ -130,7 +136,7 @@
|
||||
<div class="flex items-start justify-between gap-2 mb-3">
|
||||
<div class="flex-1 min-w-0">
|
||||
<p class="text-xs text-zinc-500 mb-0.5">
|
||||
{formatDate(a.started_at)}{#if a.handle} · <a href={`${import.meta.env.BASE_URL}${a.handle}/`} class="hover:text-zinc-300 transition-colors" on:click|stopPropagation>@{a.handle}</a>{/if}
|
||||
{formatDate(a.started_at)}{#if a.handle} · <button class="hover:text-zinc-300 transition-colors" on:click|stopPropagation={() => window.location.href = `${import.meta.env.BASE_URL}u/${a.handle}/`}>@{a.handle}</button>{/if}
|
||||
</p>
|
||||
<h3 class="font-semibold text-white truncate group-hover:text-[--accent] transition-colors">
|
||||
{a.title}
|
||||
|
||||
@@ -52,12 +52,21 @@ try {
|
||||
});
|
||||
</script>
|
||||
|
||||
<!-- Auth wall: redirect to /login/ on private instances when not authenticated -->
|
||||
<!-- Auth wall: redirect to /login/ on private instances when not authenticated.
|
||||
body[data-auth-pending] is hidden by CSS (below) before the check resolves,
|
||||
eliminating the flash of protected content. -->
|
||||
{instancePrivate && !isPublicPage && (
|
||||
<style is:inline>[data-auth-pending]{visibility:hidden}</style>
|
||||
<script is:inline>
|
||||
fetch('/api/me', { credentials: 'include' })
|
||||
.then(r => { if (r.status === 401 || r.status === 404) window.location.replace('/login/'); })
|
||||
.catch(() => {});
|
||||
.then(r => {
|
||||
if (r.status === 401 || r.status === 404) {
|
||||
window.location.replace('/login/');
|
||||
} else {
|
||||
document.body.removeAttribute('data-auth-pending');
|
||||
}
|
||||
})
|
||||
.catch(() => { document.body.removeAttribute('data-auth-pending'); });
|
||||
</script>
|
||||
)}
|
||||
|
||||
@@ -132,6 +141,7 @@ try {
|
||||
<body
|
||||
class="font-sans antialiased min-h-screen"
|
||||
style="background-color: var(--bg-base); color: var(--text-primary)"
|
||||
data-auth-pending={instancePrivate && !isPublicPage ? '' : undefined}
|
||||
>
|
||||
<nav
|
||||
class="border-b border-zinc-800 sticky top-0 z-50 bg-zinc-950/90 backdrop-blur"
|
||||
|
||||
Reference in New Issue
Block a user