Legt das Fundament der Lern-Bar an: - Next.js 15 (App Router), React 19, TypeScript, ESLint, Vitest - SQLite ueber Drizzle hinter einer Repository-Schicht - Eigener Migrationslauf mit Pruefsummen, beim Start angewendet - Argon2id, serverseitige Sitzungen, HttpOnly-Cookie, SameSite=Lax - Rollen Ausbilder und Azubi, Rechteregeln als reine Funktionen - Verwaltung: Azubikonto und Ausbildung anlegen, Passwort, Sperren - Gestaltung Petrol/Bernstein, helle und dunkle Darstellung - Dockerfile, docker-compose, Lebenszeichen, update.sh mit Rueckfall - Sicherung und Wiederherstellung als Shell-Skripte - 29 Tests fuer Rechte, Zeitrechnung und Migrationslauf Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
85 lines
3.6 KiB
Markdown
85 lines
3.6 KiB
Markdown
# Entscheidungen
|
||
|
||
Kurze Notizen zu Festlegungen, die im Auftrag offen waren. Jede Notiz nennt
|
||
die Lage, die Wahl und den Grund.
|
||
|
||
---
|
||
|
||
## E1 · Stack: Next.js 15 mit App Router
|
||
|
||
Der Auftrag verweist auf eine Stackvorgabe im „Kopf des Prompts", die nicht
|
||
mitgeliefert wurde. Auf Rückfrage: **Next.js 15 (App Router), React 19,
|
||
TypeScript**. Datenzugriff über Drizzle ORM auf better-sqlite3.
|
||
|
||
Serveraktionen ersetzen eine eigene REST-Schicht. Das hält die Rechteprüfung
|
||
dort, wo die Daten geholt werden, statt sie über eine API zu verteilen.
|
||
|
||
## E2 · Zeitstempel als ISO-8601 in UTC
|
||
|
||
Der Auftrag verlangt „alle Zeitstempel mit Zeitzone". Gespeichert wird
|
||
`2026-09-10T07:15:00.000Z`, also ein eindeutiger Zeitpunkt in UTC. Angezeigt
|
||
wird durchgehend in Europe/Berlin.
|
||
|
||
Grund: UTC-Strings sortieren sich lexikografisch richtig, was bei SQLite ohne
|
||
echten Zeitstempeltyp entscheidend ist. Ein gespeicherter Ortsversatz
|
||
(`+02:00`) würde beim Wechsel zwischen Sommer- und Winterzeit falsch sortieren.
|
||
|
||
Reine Kalenderdaten – Berichtszeitraum, Tagesnotiz, Ausbildungsbeginn – sind
|
||
`YYYY-MM-DD` ohne Uhrzeit, weil sie auch keine haben.
|
||
|
||
## E3 · Eigener Migrationslauf statt drizzle-kit
|
||
|
||
Migrationen sind handgeschriebene SQL-Dateien unter
|
||
`src/lib/db/migrationen/`, die beim Start der Reihe nach genau einmal
|
||
angewendet und mit SHA-256-Prüfsumme vermerkt werden.
|
||
|
||
Grund: volle Kontrolle über Indizes und `CHECK`-Bedingungen (Statuswerte sind
|
||
Aufzählungen, keine freien Zeichenketten), und beim Start ist im Protokoll
|
||
sichtbar, was passiert ist. Wird eine bereits angewendete Datei nachträglich
|
||
verändert, bricht der Start mit klarer Meldung ab.
|
||
|
||
Der Preis: das Drizzle-Schema muss von Hand zu den SQL-Dateien passen. Dagegen
|
||
läuft ein Test, der die migrierte Datenbank gegen das Schema abgleicht.
|
||
|
||
## E4 · Schlüssel sind UUIDs, keine laufenden Nummern
|
||
|
||
Textspalten mit `crypto.randomUUID()`. Läuft auf SQLite und PostgreSQL
|
||
gleichermaßen, und beim Zurückspielen einer Sicherung gibt es keine Kollisionen
|
||
mit zwischenzeitlich vergebenen Nummern.
|
||
|
||
## E5 · Debian-slim als Grundlage des Abbilds, nicht Alpine
|
||
|
||
`better-sqlite3` und `@node-rs/argon2` bringen für glibc fertige Binärdateien
|
||
mit, für musl nicht. Auf Alpine müsste im Abbild übersetzt werden – längerer
|
||
Bau, größere Bauschicht, mehr Fehlerquellen. Der Größenunterschied im
|
||
Laufzeitabbild ist bei einem selbst gehosteten Dienst ohne Belang.
|
||
|
||
## E6 · Passwortprüfung mit gleichbleibender Antwortzeit
|
||
|
||
Bei unbekannter E-Mail-Adresse wird trotzdem einmal gegen einen festen
|
||
Argon2-Wert gerechnet. Sonst verrät die Antwortzeit, welche Adressen es gibt.
|
||
Die Fehlermeldung ist in beiden Fällen dieselbe.
|
||
|
||
## E7 · Anhänge liegen im Dateisystem, nicht in der Datenbank
|
||
|
||
Unter `<daten>/anhaenge/`, in der Datenbank steht nur der Verweis. Hält die
|
||
Datenbankdatei klein und die Sicherung schnell; das Archiv nimmt beides mit.
|
||
|
||
## E8 · Sicherung als Shell-Skript, nicht als Node-Werkzeug
|
||
|
||
`scripts/im-container/sicherung.sh` läuft im Laufzeitabbild, das kein
|
||
TypeScript kennt, und auf dem Entwicklungsrechner unter Git Bash. Eine zweite
|
||
Fassung in TypeScript wäre eine zweite Fehlerquelle. Die konsistente Kopie der
|
||
Datenbank entsteht über die Sicherungsschnittstelle von SQLite – ein `cp` wäre
|
||
bei aktivem WAL-Modus nicht verlässlich.
|
||
|
||
## E9 · Kein `main`-Zweig ohne Freigabe
|
||
|
||
Die verbindliche Arbeitsweise verbietet Commits direkt auf `main`. Die
|
||
Erstfassung entsteht deshalb vollständig auf `feat/lernbar-erstfassung`.
|
||
`main` bleibt leer, bis eine Freigabe erfolgt.
|
||
|
||
---
|
||
|
||
<!-- Weitere Entscheidungen werden unten angefügt und fortlaufend nummeriert. -->
|