Root cause of the 404: _trigger_rebuild was firing bincio render (= full astro build), but:

1. The build took minutes → 404 during that window
  2. Even after the build, the output lands in site/dist/ — nginx serves from /var/www/bincio/ which is only updated by the rsync in the post-receive hook, not by the server process

  Fixes applied:

  1. bincio/render/cli.py: Added --no-build flag — merges sidecars and updates manifests but skips astro build. This is fast (~1 second).
  2. bincio/serve/server.py _trigger_rebuild: Now passes --no-build. After an upload, _merged/ and root index.json are updated immediately, so the feed reflects the new activity. The static Astro pages are
  only rebuilt on git push.
  3. site/src/components/ActivityDetailLoader.svelte (new): Svelte component that reads the activity ID from the URL, calls loadIndex to resolve the shard tree, then renders ActivityDetail dynamically — no
  pre-built page needed.
  4. site/src/pages/activity/index.astro (new): Generic Astro shell page that renders ActivityDetailLoader. Gets compiled to dist/activity/index.html.
  5. docs/deployment/vps.md: Added location /activity/ { try_files $uri $uri/ /activity/index.html; } to the nginx config. When a request arrives for /activity/2026-04-06T153345Z/ and no pre-built file
  exists, nginx serves the shell, which loads the data dynamically from /data/ (which nginx already serves live from disk).
This commit is contained in:
Davide Scaini
2026-04-10 17:48:23 +02:00
parent 61349e6292
commit eeed3fe3b2
5 changed files with 69 additions and 2 deletions
@@ -0,0 +1,40 @@
<script lang="ts">
import { onMount } from 'svelte';
import { loadIndex } from '../lib/dataloader';
import ActivityDetail from './ActivityDetail.svelte';
import type { ActivitySummary } from '../lib/types';
export let base: string = '/';
let activity: ActivitySummary | null = null;
let notFound = false;
let loading = true;
onMount(async () => {
// Extract activity ID from the URL path: /activity/{id}/
const match = window.location.pathname.match(/\/activity\/([^/]+)/);
const id = match?.[1];
if (!id) { notFound = true; loading = false; return; }
try {
const index = await loadIndex(base);
activity = index.activities.find(a => a.id === id) ?? null;
if (!activity) notFound = true;
} catch {
notFound = true;
}
loading = false;
});
</script>
{#if loading}
<p class="text-zinc-500 text-sm mt-8 text-center">Loading activity…</p>
{:else if notFound}
<div class="text-center mt-16">
<p class="text-zinc-400 text-sm mb-2">Activity not found.</p>
<p class="text-zinc-600 text-xs">It may still be processing — try refreshing in a moment.</p>
<a href={base} class="mt-4 inline-block text-blue-400 hover:text-blue-300 text-sm">← Back to feed</a>
</div>
{:else if activity}
<ActivityDetail {activity} {base} />
{/if}
+7
View File
@@ -0,0 +1,7 @@
---
import Base from '../../layouts/Base.astro';
import ActivityDetailLoader from '../../components/ActivityDetailLoader.svelte';
---
<Base title="Activity — BincioActivity">
<ActivityDetailLoader base={import.meta.env.BASE_URL} client:only="svelte" />
</Base>