Diese Adresse führte bis September 2026 eine Anwendung: Zeitnahme, Kader,
Trails, Renntage. Die heißt jetzt Kurbeler, liegt unter kurbeler.de und führt
den Stammtisch als eines von mehreren Teams. Was hier bleibt, ist die Adresse
— und die soll niemanden ins Leere laufen lassen.
Übrig sind drei vorgerenderte Seiten: Wappen, ein Satz, ein Knopf, dazu
Impressum und Datenschutz. Kein Konto, keine Datenbank, kein Formular, keine
Cookies, keine Schriften oder Karten von fremden Servern. Die
Datenschutzerklärung ist deshalb kurz und nicht die von Kurbeler mit
Streichungen — eine Erklärung, die von Konten und Benachrichtigungen spricht,
wäre auf dieser Seite schlicht falsch.
`adapter-static` statt `adapter-node`, ausgeliefert von nginx: Für drei Seiten
ohne eine dynamische Angabe einen JavaScript-Prozess laufen zu lassen, der auf
Anfragen wartet, um jedes Mal dieselbe Datei herauszugeben, wäre Beschäftigung
ohne Arbeit. Von den Abhängigkeiten bleiben die zwölf, die zum Bauen nötig
sind; zur Laufzeit keine einzige. Das Bild ist damit 492 kB statt elf
Megabyte — zwei alte Grafiken von je 2,4 MB lagen ungenutzt in `static/`.
Das Backend bleibt unangetastet. Es wird vom Wegweiser nicht benutzt, aber
darin liegen die Daten des alten Deployments.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LTw6xVYgjMy9AQtfs1Gdyu
Das Team-Submenue stand auf einem DropdownMenu, und alles, was ein
Menuepunkt braucht und ein Dropdown nicht kann, stand hier als
Handarbeit daneben: ein Zeitgeber fuers Aufklappen, ueberschriebene
Zeigerhandler gegen das Flackern beim Klick, unterdrueckter Fokus gegen
den Ring nach dem Zeigerbesuch.
NavigationMenu kann das von sich aus. Es hat openOnHover samt
delayDuration, es haelt seinen eigenen onclick zurueck, wenn per Zeiger
aufgeklappt wurde ("if opened via pointer move, we prevent the click
event"), es faengt keinen Fokus und sperrt kein Scrollen. Der Trigger
bleibt ueber das child-Snippet ein Link auf die Teamseite.
viewport={false}: Das Panel haengt unter seinem Menuepunkt, statt als
animierte Flaeche ueber die ganze Leiste zu fahren. Bei einem einzigen
aufklappbaren Punkt waere das grosses Kino ohne Stueck.
Das mobile Menue bekommt die Teams stattdessen eingerueckt und
aufgeklappt untergestellt. Auf dem Touchscreen gibt es kein
Ueberfahren — mit dem Menue allein waere seit dem Wegfall der Liste
"Meine Teams" gar kein Wechsel mehr moeglich gewesen.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P32KoesVtABd6xWsqMKzhr
npm ci brach beim Deployment mit EBADENGINE ab: jsdom 30 verlangt
^22.22.2 || ^24.15.0 || >=26.0.0, Nixpacks liefert aber Node 24.10.0.
24.10 ist aelter als 24.15 und faellt damit durch; zusammen mit
engine-strict=true in .npmrc bricht die Installation hart ab.
Anders als bei frueheren Node-Problemen liegt es nicht an unserer eigenen
engines-Angabe (^20.19 || ^22.12 || >=24) — die ist erfuellt. Die
Anforderung kommt allein aus jsdom, und zwar erst seit Version 30; jsdom 29
verlangt nur >=24.0.0. Ein NIXPACKS_NODE_VERSION haette nicht geholfen, weil
Nixpacks nur Major-Versionen aufloest und damit ohnehin bei 24.10 landet.
jsdom ist eine devDependency und wird ausschliesslich als Testumgebung fuer
Vitest genutzt (vite.config.ts). Fuer den Produktionsbuild ist sie
irrelevant, wird aber mitinstalliert, weil Nixpacks NPM_CONFIG_PRODUCTION
auf false setzt.
Verifiziert: npm ci laeuft in einem node:24.10-slim-Container durch, also
auf exakt der Version, an der das Deployment gescheitert ist. Alle 19 Tests
in src/lib/gpx.test.ts bestehen unter jsdom 29.1.1, npm run build erzeugt
das build-Verzeichnis.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
DOMPurify filtert trail.description vor {@html}, da nur Paten/Admins das
editor-Feld setzen dürfen, ungefiltert aber Skriptcode bei allen
Team-Mitgliedern ausführen könnte. Der Sanitizer läuft browser-gated, da
DOMPurify serverseitig kein DOM hat. Zusätzlich Key in der
Status-Buttons-Schleife ergänzt.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Das Coolify-Deployment scheiterte an zwei Stellen:
1. npm ci brach mit EBADENGINE ab. Coolify baute mit Node 22.11.0, vite und
@sveltejs/vite-plugin-svelte verlangen aber >=22.12; engine-strict=true
aus der .npmrc macht daraus einen Abbruch statt einer Warnung. Das
engines-Feld schreibt die Anforderung jetzt im Repo fest, statt sie einer
Einstellung in der Coolify-UI zu überlassen.
2. Selbst nach erfolgreichem npm ci wäre der Start gescheitert: adapter-auto
erkennt Coolify nicht ("Could not detect a supported production
environment") und erzeugt gar kein build/ — der Startbefehl "node build"
findet dann nichts. Jetzt adapter-node, adapter-auto entfällt.
Verifiziert in einer frischen Kopie ohne node_modules, mit derselben Kette
wie im Deployment: npm ci läuft durch, npm run build erzeugt build/index.js,
node build antwortet auf / und /login mit HTTP 200.
Dabei fiel ein dritter Punkt auf, der keine Code-Änderung braucht, aber im
Deployment gesetzt sein muss: PUBLIC_PB_URL wird zur Buildzeit eingesetzt.
Fehlt sie, bricht der Build mit "PUBLIC_PB_URL is not exported by
virtual:env/static/public" ab. Steht jetzt in frontend/README.md und CLAUDE.md.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
npm run generate-pocketbase-types liest das Collections-Array jetzt direkt
aus backend/pb_migrations/ (scripts/schema-to-json.mjs) und speist es über
pocketbase-typegen --json ein. Keine laufende Instanz, kein Token, kein
Netzwerkzugriff mehr nötig.
Damit stammen Schema und Typen aus derselben versionierten Quelle und können
nicht auseinanderlaufen. Vorher zeigten sie auf eine Remote-Instanz, deren
Stand niemand garantieren konnte — nach dem Neu-Deployment lieferte sie
prompt Typen ohne die fachlichen Collections.
frontend/.env enthält dadurch nur noch PUBLIC_PB_URL. PB_TYPEGEN_URL und
PB_TYPEGEN_TOKEN entfallen (letztere wurde ohnehin nirgends gelesen),
PB_SUPERUSER_TOKEN wird nicht mehr gebraucht. Die Datei enthält damit kein
Geheimnis mehr.
Verifiziert: ohne .env, ohne Token und ohne laufende Instanz entstehen alle
sechs Collections, svelte-check meldet 0 Fehler und 0 Warnungen.
Die System-Collections (_superusers, _mfas, ...) fehlen in den Typen, weil
sie bewusst nicht in der Migration stehen. Die Anwendung verwendet sie nicht.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Alle vier Scripts setzten auf einem Schema auf, das es nicht mehr gibt:
- seed-test-data.ts schrieb in stages und results (heute: runs und times)
und kannte users.role, riders.email/birthday sowie events.created_by/
timekeepers -- alles nicht mehr vorhanden. Teams kannte es gar nicht,
ohne Team-Zuordnung greifen sämtliche API-Rules und die Daten wären
unsichtbar geblieben.
- migrate-schema.ts und migrate-teams.ts waren Einmal-Migrationen. Ihr
Ergebnis steckt längst im Schema (events.status, riders.number, times.
correction, die teams-Collection, die team-Felder). Ein erneuter Lauf
gegen die produktive Instanz wäre bestenfalls wirkungslos.
- test-token.ts war ein Wegwerf-Prüfscript für den Superuser-Token.
Das Schema ist jetzt in backend/pb_migrations/ versioniert; das ist der
Weg für Änderungen. tsx entfällt als Abhängigkeit, es wurde nur von
diesen Scripts benutzt.
PB_SUPERUSER_TOKEN bleibt in .env -- generate-pocketbase-types braucht ihn.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Die SvelteKit-App liegt unter frontend/, das Backend unter backend/ als
eigenständige PocketBase-Instanz mit Dockerfile, docker-compose und
versioniertem Schema.
- PocketBase-URL über PUBLIC_PB_URL konfigurierbar, Default bleibt die
produktive Instanz https://api.stammtisch-hersbruck.de
- Schema als Snapshot-Migration der sechs fachlichen Collections
(users, teams, events, runs, riders, times), abgezogen von der
produktiven Instanz. Verifiziert: ein Erststart gegen leere pb_data
legt alle sechs an, Felder und API-Rules stimmen überein.
- pb_data und pb_migrations als Bind-Mounts, damit Daten persistieren und
im Admin-UI erzeugte Migrationen im Repo landen
- Veraltete Dokumentation entfernt: pocketbase_schema.json nannte
Collections (stages, results, organizers), die es nicht gibt
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>