QUICKSTART fuehrt von null zur laufenden Instanz und enthaelt die vollstaendige Nginx-Proxy-Manager-Konfiguration - inklusive der beiden Stellen, an denen es sonst klemmt: client_max_body_size fuer grosse Anleitungs-PDFs und X-Forwarded-For fuer das Rate-Limit. Die Anleitung ist bebildert. Die 16 Screenshots entstehen automatisiert mit `pnpm screenshots` gegen das Produktions-Image im Container, nicht gegen den Dev-Server: der brach unter Last reproduzierbar mit ChunkLoadError ab, und die Bilder zeigen so ohnehin genau das, was nach `docker compose up` zu sehen ist. Nebenbei behoben: in der Mehrfachauswahl verdeckte die Aktionsleiste die letzte Kachelreihe. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
99 lines
6.0 KiB
Markdown
99 lines
6.0 KiB
Markdown
# Devlog
|
||
|
||
Zeiterfassung und Nachvollziehbarkeit der Entwicklung. Eine Zeile je
|
||
abgeschlossenem Arbeitsschritt, Zeiten in `HH:mm` (Europe/Berlin). Die
|
||
Commit-Spalte verweist auf den Stand am Ende des Schritts.
|
||
|
||
## Phase 1 – MVP (v0.1.0)
|
||
|
||
### 06.09.2026
|
||
|
||
| Von | Bis | Dauer | Schritt | Ergebnis | Commit |
|
||
|---|---|---|---|---|---|
|
||
| 22:06 | 22:12 | 0:06 | Planung | Phase-1-Plan und Drizzle-Schema zur Freigabe vorgelegt; fünf Entscheidungsfragen geklärt (Cent statt Float, Migrator statt drizzle-kit im Image, Bilder öffentlich / PDFs geschützt, Token gehasht, Branch pro Phase) | – |
|
||
| 22:12 | 22:20 | 0:08 | Scaffold | pnpm installiert, Next 15.5.25 + Tailwind 4 + shadcn/ui (radix-nova) mit 20 Komponenten, Touch-Größen auf 44 px angehoben | `0de49a9` |
|
||
| 22:20 | 22:25 | 0:05 | Datenmodell | 19 Tabellen in Drizzle, CHECK-Constraints, Unique-Index `(wishlist_id, set_id)`, Migration erzeugt und angewendet, WAL verifiziert | `06268bc` |
|
||
| 22:25 | 22:26 | 0:01 | Dokumentation | CLAUDE.md, Roadmap, Changelog, Devlog | `b74cecb` |
|
||
| 22:26 | 22:40 | 0:14 | Auth und App-Shell | better-auth mit Passwort und Passkeys, erster Nutzer wird Admin, Rate-Limit in der Datenbank, Konto-Seite, Bottom-Navigation, `/api/health` | `4c78c56` |
|
||
| 22:40 | 22:46 | 0:06 | Stammdaten | CRUD für Hersteller, Themen und Standorte über eine gemeinsame CrudSection; zod-Schemata, ActionResult, Slug, Geld- und Datumshelfer | `af0ae03` |
|
||
| 22:46 | 22:59 | 0:13 | Katalog | Kachelliste mit Suche und vier Filtern, Besitzlogik in `ownership.ts`, Set-Formular, Detailseite, Upload-Pipeline mit Magic-Byte-Prüfung, pdf.js-Viewer, Seed-Skript | `856800b` |
|
||
| 22:59 | 23:06 | 0:07 | Wunschlisten und Sammlung | Mehrfachauswahl mit Dubletten-Skip, Wunschlisten-Verwaltung, Teilen, Überraschungsmodus, öffentliche Ansicht mit Reservierung und Storno, Sammlungsübersicht mit Statistik | `79b0133` |
|
||
| 23:06 | 23:31 | 0:25 | Tests | 50 Vitest-Unit-Tests, Playwright-Smoke-Test über den ganzen Weg bis zur Reservierung durch einen Besucher ohne Konto | `b0152b8` |
|
||
|
||
### 07.09.2026
|
||
|
||
| Von | Bis | Dauer | Schritt | Ergebnis | Commit |
|
||
|---|---|---|---|---|---|
|
||
| 23:31 | 00:09 | 0:38 | Docker | Multi-Stage-Build, Entrypoint mit Rechteprüfung und Migration, Compose-Stack, Healthcheck, `update.sh` mit Rollback; Image gebaut und Container verifiziert | `61b1249` |
|
||
| 00:09 | 00:50 | 0:41 | Dokumentation und Screenshots | README, QUICKSTART mit Nginx-Proxy-Manager-Konfiguration, bebilderte Anleitung, 16 automatisiert erzeugte Screenshots, Changelog 0.1.0 | folgt |
|
||
|
||
**Summe Phase 1: 2:44**
|
||
|
||
---
|
||
|
||
## Notizen und Entscheidungen
|
||
|
||
### Umgebung
|
||
|
||
- **pnpm**: `corepack enable` scheitert auf diesem Rechner an fehlenden
|
||
Schreibrechten in `C:\Program Files\nodejs`. Stattdessen
|
||
`npm install -g pnpm@10`; pnpm liegt unter `%APPDATA%\npm` und ist im PATH.
|
||
- **Port 3000** ist auf dem Entwicklungsrechner von einer anderen Anwendung
|
||
belegt. Der Dev-Server läuft deshalb lokal auf 3100, die Testläufe auf
|
||
3210 und 3220.
|
||
- **`pnpm build` unter Windows** scheitert mit `EPERM … symlink`: Der
|
||
Standalone-Output legt Symlinks in `.next/standalone` an, wofür Windows den
|
||
Entwicklermodus oder administrative Rechte braucht. Der Docker-Build unter
|
||
Linux ist davon nicht betroffen; lokal prüft man mit
|
||
`docker build -t bricktrack:test .`.
|
||
|
||
### Bibliotheken
|
||
|
||
- **shadcn-CLI 4.21** arbeitet mit Presets (`-p nova`) und Base-Libraries
|
||
(`-b radix`) statt der früheren Style-Auswahl. Sie zieht `cn` und `radix-ui`
|
||
als Pakete; `clsx` und `tailwind-merge` werden dadurch nicht mehr direkt
|
||
gebraucht und wurden wieder entfernt. Die Voreinstellungen sind auf
|
||
Desktop-Dichte ausgelegt (Buttons `h-8`) – für die Bedienung am Handy auf
|
||
44 px angehoben.
|
||
- **better-auth 1.7.3**: Das Passkey-Plugin liegt nicht mehr unter
|
||
`better-auth/plugins/passkey`, sondern im eigenen Paket
|
||
`@better-auth/passkey`. Der Drizzle-Adapter löst Modelle über die
|
||
TypeScript-Property-Namen auf, nicht über die Spaltennamen – deshalb können
|
||
auch die Auth-Tabellen durchgehend `snake_case` verwenden.
|
||
- **Drizzle**: Selbstbezügliche Fremdschlüssel (`parent_theme_id`) brauchen
|
||
`AnySQLiteColumn` als Rückgabetyp, nicht `AnyColumn`.
|
||
- **pdf.js** wird lokal gebündelt, inklusive Worker. Kein CDN, damit der
|
||
Browser des Nutzers keine fremden Server kontaktiert.
|
||
|
||
### Stolpersteine
|
||
|
||
- **CRLF im Entrypoint**: Eine unter Windows geschriebene `entrypoint.sh` mit
|
||
CRLF scheitert im Container mit dem irreführenden
|
||
`exec /app/entrypoint.sh: no such file or directory` – der Kernel sucht
|
||
dann `/bin/sh\r`. Behoben mit einer `.gitattributes`, die für Shell-Skripte
|
||
LF erzwingt.
|
||
- **Migrations-Bundle**: Der mit esbuild als ESM gebündelte Migrations-Runner
|
||
scheiterte an `Dynamic require of "fs" is not supported` – mitgebündelte
|
||
CJS-Abhängigkeiten (dotenv) rufen `require()` für Node-Builtins auf. Als
|
||
CommonJS gebündelt läuft es.
|
||
- **Playwright und Reihenfolge**: Playwright startet den Web-Server **vor**
|
||
dem `globalSetup`. Das Löschen des Testdatenverzeichnisses im globalSetup
|
||
scheiterte deshalb unter Windows an der bereits geöffneten SQLite-Datei;
|
||
das Aufräumen läuft jetzt im npm-Skript davor.
|
||
- **Screenshots gegen den Dev-Server** waren nicht reproduzierbar: unter Last
|
||
brach der Stream mit `ChunkLoadError` beziehungsweise
|
||
`Unexpected end of JSON input` ab. Die Bilder entstehen jetzt gegen das
|
||
Produktions-Image im Container – verlässlich und zugleich näher an dem, was
|
||
Nutzer wirklich sehen.
|
||
|
||
### Beim Testen gefunden und behoben
|
||
|
||
- Eine spontan aus dem Katalog angelegte Wunschliste war **nicht teilbar**:
|
||
Das Feld `isPublic` kommt aus einer Checkbox und fiel beim programmatischen
|
||
Anlegen auf `false`. Die Schnellanlage setzt es jetzt ausdrücklich.
|
||
- Das Sternchen an Pflichtfeldern gehörte zum zugänglichen Namen des Feldes
|
||
(„Name *"). Jetzt `aria-hidden` – der Pflichtstatus steckt ohnehin im
|
||
`required` des Eingabefeldes.
|
||
- In der Mehrfachauswahl verdeckte die Aktionsleiste die letzte Kachelreihe;
|
||
das Raster bekommt im Auswahlmodus zusätzliches Polster nach unten.
|