Commit Graph
4 Commits
Author SHA1 Message Date
unknownandClaude Opus 5 61b1249afd build: dockerfile, compose-stack, entrypoint und update-skript
Multi-Stage-Build mit standalone-Output, non-root Nutzer node (UID 1000)
und Healthcheck auf /api/health. Der Entrypoint prueft zuerst, ob /data
beschreibbar ist, und sagt bei einem Rechteproblem genau, was zu tun
ist; danach laufen die Migrationen und erst dann startet die App.

Der Migrations-Runner wird beim Build mit esbuild zu einer einzelnen
CJS-Datei gebuendelt. Damit braucht das Runtime-Image weder drizzle-kit
noch tsx - nur das native better-sqlite3, das ohnehin dabei ist.

update.sh sichert data und .env, holt den neuen Stand, baut, startet und
wartet auf den Healthcheck. Wird der Container nicht gesund, rollt es
Code und Container auf den vorherigen Commit zurueck und nennt den Pfad
der Sicherung.

.gitattributes erzwingt LF: eine entrypoint.sh mit CRLF scheitert im
Container mit "no such file or directory" auf der Shebang-Zeile.

Verifiziert: Image gebaut (358 MB), Container healthy, Migration laeuft,
Registrierung des ersten Nutzers funktioniert, sharp arbeitet unter musl.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 00:09:03 +02:00
unknownandClaude Opus 5 b0152b8667 test: unit-tests der kernlogik und playwright-smoke-test
50 Unit-Tests mit Vitest fuer die Stellen, an denen ein Fehler teuer
waere: Besitzstatus, Mehrfachauswahl inklusive Ueberspringen von
Dubletten und bereits besessenen Sets, Token-Erzeugung und -Vergleich,
Cent-Umrechnung und Slugs.

Der Playwright-Smoke-Test laeuft den ganzen Weg: Registrierung des
ersten Nutzers, Hersteller anlegen, Set anlegen, auf eine neue
Wunschliste setzen, Link holen und als Besucher ohne Konto reservieren.
Er nutzt ein eigenes Datenverzeichnis (./data-e2e), das vor jedem Lauf
geleert wird.

Dabei aufgefallen und behoben: eine spontan aus dem Katalog angelegte
Wunschliste war nicht teilbar, weil das Feld isPublic ohne Formular
auf false fiel. Ausserdem ist das Sternchen an Pflichtfeldern jetzt
aria-hidden, damit der zugaengliche Name des Feldes sauber bleibt.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 23:31:21 +02:00
unknownandClaude Opus 5 856800b4e4 feat(katalog): set-katalog mit filtern, detailseite und uploads
Kachelliste mit Suche ueber Setnummer, Name und EAN sowie Filtern nach
Hersteller, Thema, Jahr und Besitzstatus. Besitz kommt aus genau einer
Stelle: src/lib/ownership.ts definiert "hat er nicht" als Katalogeintrag
ohne collection_item mit Status ungleich sold, als korrelierte
Subquery, damit mehrere Exemplare die Liste nicht verdoppeln.

Detailseite mit Metadaten, Anleitungen (PDF-Upload plus pdf.js-Viewer
ohne CDN, oder externer Link), Fotos, Sammlungseintraegen und den
Aktionen "Zur Sammlung" und "Auf Wunschliste".

Upload-Pipeline prueft Magic Bytes statt des gemeldeten MIME-Typs,
erzeugt Dateinamen serverseitig, wandelt Bilder in WebP inklusive
Thumbnail und liefert Dateien ueber /api/files aus - Set-Bilder
oeffentlich, Fotos und PDFs nur mit Session.

Dazu das Seed-Skript mit 3 Herstellern, 6 Themen, 10 Sets und
4 Sammlungseintraegen.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 22:59:26 +02:00
unknownandClaude Opus 5 06268bcfd6 feat(db): drizzle-schema, db-client und initiale migration
Vollstaendiges Datenmodell: Katalog (manufacturer, theme, set, tag,
instruction), Besitz (collection_item, location, missing_part, photo),
Wunschlisten (wishlist, wishlist_item) und die Auth-Tabellen von
better-auth inklusive passkey und invite.

Kernregel: ein set ist ein Katalogeintrag, ein collection_item die
Verknuepfung mit dem tatsaechlichen Besitz. Preise liegen als Cent in
INTEGER-Spalten, damit Summen exakt bleiben.

SQLite laeuft mit WAL, foreign_keys=ON und busy_timeout.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 22:25:22 +02:00