Status: erledigt Meilenstein: v0.1.0 Labels: phase-1, backend Geschätzt: 5 h Tatsächlich: 1 h 10 min
Ziel
Das vollständige Schema in deutscher Benennung, sodass alle folgenden Meilensteine nur noch Verhalten ergänzen und keine Strukturänderung mehr brauchen.
Aufgaben
Prisma 7: Generator prisma-client mit output = "../src/generated/prisma", datasource ohne url, @prisma/adapter-pg
prisma.config.ts mit migrations.seed = "tsx prisma/seed.ts"
src/lib/db.ts: Lazy-Singleton-Proxy, damit next build ohne DATABASE_URL läuft, Dev-Instanz an globalThis
Unique-Constraints: Geraet.tagCode, (laufId, vorlagenSchrittId) auf Schrittergebnis, (quelleId, externeId) auf Mitarbeiter, (kundeId, name) auf Abteilung, (kundeId) partiell für isteigeneOrganisation
pg_trgm-Erweiterung und GIN-Indizes für die Archivsuche über Lauf.titel, Schrittergebnis.notiz, Kommentar.rumpf
src/lib/geheimnisse.ts: AES-256-GCM aus APP_SECRET (scrypt-abgeleitet), für bindPasswortVerschluesselt
Erste Migration, prisma/seed.ts als idempotenter Rumpf: Admin aus SEED_ADMIN_EMAIL/SEED_ADMIN_PASSWORD mit @node-rs/argon2, eigene Organisation, Einstellung mit Betriebsmodus.INTERN
tests/setup-env.ts, tests/global-setup.ts, tests/helpers/db.ts nach ProxLedger-Muster
Akzeptanzkriterien
prisma migrate deploy läuft auf leerer Datenbank durch
pnpm db:seed zweimal hintereinander ohne Fehler und ohne Datenänderung
Tests: tagCode benutzt nur das vorgegebene Alphabet ABCDEFGHJKLMNPQRSTUVWXYZ23456789 und enthält damit nie 0, O, 1 oder I; eindeutig über 10 000 Ziehungen
Tests: verschlüsseln und entschlüsseln ist verlustfrei, zwei Verschlüsselungen desselben Klartexts unterscheiden sich (eigener IV), ein manipulierter Geheimtext wirft
next build gelingt ohne gesetzte DATABASE_URL
Beim Bauen aufgefallen
Widerspruch im Auftrag beim tagCode-Alphabet, aufgelöst. Das wörtlich vorgegebene Alphabet ABCDEFGHJKLMNPQRSTUVWXYZ23456789 enthält ein L, obwohl die Klammer daneben „ohne 0/O,
1/I/L" sagt. Beides zugleich geht nicht: ohne L blieben 31 Zeichen. Wir folgen dem wörtlichen
Alphabet, weil die Klammer die Verwechslungsgruppen benennt und keine Ausschlussliste ist:
aus {0, O} sind beide weg, aus {1, I, L} sind 1 und I weg — dadurch ist L eindeutig.
Das Präfix LZ- enthält ohnehin in jedem Code ein L. Das Akzeptanzkriterium oben ist
entsprechend richtiggestellt; ein grüner Test über einem unerfüllten Kriterium wäre schlimmer
als ein korrigiertes Kriterium.
Der unabhängige Review hat drei Lücken gefunden, die alle behoben sind: der geforderten
Suchspalte Schrittergebnis.wert fehlte der Trigramm-Index; die im Lauf gewählte Abteilung war
nur ein Wert in variablen statt ein Bezug, wodurch die Druckansicht eines archivierten Laufs
nicht reproduzierbar gewesen wäre; und für die Kundenbindung eines Benutzers (#008) gab es
keine Struktur, sie hätte also nur im Token stehen können — gegen die eigene Regel, Rechte pro
Anfrage aus der Datenbank zu lesen.
Ein DELETE auf einen Lauf hätte Schrittergebnisse und Kommentare stillschweigend
mitgenommen. Jetzt Restrict; ein Lauf lässt sich nur noch bewusst und in der richtigen
Reihenfolge entfernen. Dasselbe für das Protokoll, das seinen Urheber nicht mehr verlieren
kann.
tagCode wäre nach dem Löschen eines Geräts wieder frei gewesen. Deshalb TagCodeVergabe
als Vergabebuch, aus dem nie gelöscht wird.
prisma migrate reset --force hat den Bestätigungsdialog trotzdem gezeigt und die
Advisory-Sperre gehalten. Schneller und verlässlicher: DROP SCHEMA public CASCADE per psql,
dann migrate dev --create-only.
Quelle: docs/issues/006-datenmodell.md — dort gepflegt, hierher gespiegelt.
**Status:** erledigt
**Meilenstein:** v0.1.0
**Labels:** phase-1, backend
**Geschätzt:** 5 h **Tatsächlich:** 1 h 10 min
## Ziel
Das vollständige Schema in deutscher Benennung, sodass alle folgenden Meilensteine nur noch Verhalten ergänzen und keine Strukturänderung mehr brauchen.
## Aufgaben
- [x] Prisma 7: Generator `prisma-client` mit `output = "../src/generated/prisma"`, `datasource` ohne `url`, `@prisma/adapter-pg`
- [x] `prisma.config.ts` mit `migrations.seed = "tsx prisma/seed.ts"`
- [x] `src/lib/db.ts`: Lazy-Singleton-Proxy, damit `next build` ohne `DATABASE_URL` läuft, Dev-Instanz an `globalThis`
- [x] Modelle: `Benutzer`, `Kunde`, `Einstellung`, `Verzeichnisquelle`, `Mitarbeiter`, `Abteilung`, `Geraet`, `GeraetetypVorlage`, `Vorlage`, `VorlagenVersion`, `VorlagenVariable`, `VorlagenAbschnitt`, `VorlagenSchritt`, `Lauf`, `Schrittergebnis`, `Kommentar`, `Protokoll`, `AbgleichSperre`
- [x] Enums: `Rolle`, `Betriebsmodus`, `LaufStatus`, `SchrittZustand`, `SchrittTyp`, `GeraeteTyp`, `VersionStatus`, `Herkunft`
- [x] Unique-Constraints: `Geraet.tagCode`, `(laufId, vorlagenSchrittId)` auf `Schrittergebnis`, `(quelleId, externeId)` auf `Mitarbeiter`, `(kundeId, name)` auf `Abteilung`, `(kundeId)` partiell für `isteigeneOrganisation`
- [x] `pg_trgm`-Erweiterung und GIN-Indizes für die Archivsuche über `Lauf.titel`, `Schrittergebnis.notiz`, `Kommentar.rumpf`
- [x] Jede kundenbezogene Zeile trägt `kundeId` als Pflichtfeld: `Geraet`, `Mitarbeiter`, `Abteilung`, `Verzeichnisquelle`, `Lauf`
- [x] `src/lib/tagcode.ts`: Alphabet `ABCDEFGHJKLMNPQRSTUVWXYZ23456789`, Präfix `LZ-`, 6 Zeichen, Kollisionsprüfung serverseitig
- [x] `src/lib/geheimnisse.ts`: AES-256-GCM aus `APP_SECRET` (scrypt-abgeleitet), für `bindPasswortVerschluesselt`
- [x] Erste Migration, `prisma/seed.ts` als idempotenter Rumpf: Admin aus `SEED_ADMIN_EMAIL`/`SEED_ADMIN_PASSWORD` mit `@node-rs/argon2`, eigene Organisation, `Einstellung` mit `Betriebsmodus.INTERN`
- [x] `tests/setup-env.ts`, `tests/global-setup.ts`, `tests/helpers/db.ts` nach ProxLedger-Muster
## Akzeptanzkriterien
- [x] `prisma migrate deploy` läuft auf leerer Datenbank durch
- [x] `pnpm db:seed` zweimal hintereinander ohne Fehler und ohne Datenänderung
- [x] Tests: `tagCode` benutzt nur das vorgegebene Alphabet `ABCDEFGHJKLMNPQRSTUVWXYZ23456789` und enthält damit nie `0`, `O`, `1` oder `I`; eindeutig über 10 000 Ziehungen
- [x] Tests: verschlüsseln und entschlüsseln ist verlustfrei, zwei Verschlüsselungen desselben Klartexts unterscheiden sich (eigener IV), ein manipulierter Geheimtext wirft
- [x] `next build` gelingt ohne gesetzte `DATABASE_URL`
## Beim Bauen aufgefallen
- **Widerspruch im Auftrag beim tagCode-Alphabet, aufgelöst.** Das wörtlich vorgegebene Alphabet
`ABCDEFGHJKLMNPQRSTUVWXYZ23456789` enthält ein `L`, obwohl die Klammer daneben „ohne 0/O,
1/I/L" sagt. Beides zugleich geht nicht: ohne `L` blieben 31 Zeichen. Wir folgen dem wörtlichen
Alphabet, weil die Klammer die *Verwechslungsgruppen* benennt und keine Ausschlussliste ist:
aus `{0, O}` sind beide weg, aus `{1, I, L}` sind `1` und `I` weg — dadurch ist `L` eindeutig.
Das Präfix `LZ-` enthält ohnehin in jedem Code ein `L`. Das Akzeptanzkriterium oben ist
entsprechend richtiggestellt; ein grüner Test über einem unerfüllten Kriterium wäre schlimmer
als ein korrigiertes Kriterium.
- **Der unabhängige Review hat drei Lücken gefunden, die alle behoben sind:** der geforderten
Suchspalte `Schrittergebnis.wert` fehlte der Trigramm-Index; die im Lauf gewählte Abteilung war
nur ein Wert in `variablen` statt ein Bezug, wodurch die Druckansicht eines archivierten Laufs
nicht reproduzierbar gewesen wäre; und für die Kundenbindung eines Benutzers (#008) gab es
keine Struktur, sie hätte also nur im Token stehen können — gegen die eigene Regel, Rechte pro
Anfrage aus der Datenbank zu lesen.
- **Ein `DELETE` auf einen Lauf hätte Schrittergebnisse und Kommentare stillschweigend
mitgenommen.** Jetzt `Restrict`; ein Lauf lässt sich nur noch bewusst und in der richtigen
Reihenfolge entfernen. Dasselbe für das `Protokoll`, das seinen Urheber nicht mehr verlieren
kann.
- **`tagCode` wäre nach dem Löschen eines Geräts wieder frei gewesen.** Deshalb `TagCodeVergabe`
als Vergabebuch, aus dem nie gelöscht wird.
- **`prisma migrate reset --force` hat den Bestätigungsdialog trotzdem gezeigt** und die
Advisory-Sperre gehalten. Schneller und verlässlicher: `DROP SCHEMA public CASCADE` per psql,
dann `migrate dev --create-only`.
---
Quelle: `docs/issues/006-datenmodell.md` — dort gepflegt, hierher gespiegelt.
florian.wilde
added this to the v0.1.0 milestone 2026-09-24 20:44:39 +00:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Status: erledigt
Meilenstein: v0.1.0
Labels: phase-1, backend
Geschätzt: 5 h Tatsächlich: 1 h 10 min
Ziel
Das vollständige Schema in deutscher Benennung, sodass alle folgenden Meilensteine nur noch Verhalten ergänzen und keine Strukturänderung mehr brauchen.
Aufgaben
prisma-clientmitoutput = "../src/generated/prisma",datasourceohneurl,@prisma/adapter-pgprisma.config.tsmitmigrations.seed = "tsx prisma/seed.ts"src/lib/db.ts: Lazy-Singleton-Proxy, damitnext buildohneDATABASE_URLläuft, Dev-Instanz anglobalThisBenutzer,Kunde,Einstellung,Verzeichnisquelle,Mitarbeiter,Abteilung,Geraet,GeraetetypVorlage,Vorlage,VorlagenVersion,VorlagenVariable,VorlagenAbschnitt,VorlagenSchritt,Lauf,Schrittergebnis,Kommentar,Protokoll,AbgleichSperreRolle,Betriebsmodus,LaufStatus,SchrittZustand,SchrittTyp,GeraeteTyp,VersionStatus,HerkunftGeraet.tagCode,(laufId, vorlagenSchrittId)aufSchrittergebnis,(quelleId, externeId)aufMitarbeiter,(kundeId, name)aufAbteilung,(kundeId)partiell füristeigeneOrganisationpg_trgm-Erweiterung und GIN-Indizes für die Archivsuche überLauf.titel,Schrittergebnis.notiz,Kommentar.rumpfkundeIdals Pflichtfeld:Geraet,Mitarbeiter,Abteilung,Verzeichnisquelle,Laufsrc/lib/tagcode.ts: AlphabetABCDEFGHJKLMNPQRSTUVWXYZ23456789, PräfixLZ-, 6 Zeichen, Kollisionsprüfung serverseitigsrc/lib/geheimnisse.ts: AES-256-GCM ausAPP_SECRET(scrypt-abgeleitet), fürbindPasswortVerschluesseltprisma/seed.tsals idempotenter Rumpf: Admin ausSEED_ADMIN_EMAIL/SEED_ADMIN_PASSWORDmit@node-rs/argon2, eigene Organisation,EinstellungmitBetriebsmodus.INTERNtests/setup-env.ts,tests/global-setup.ts,tests/helpers/db.tsnach ProxLedger-MusterAkzeptanzkriterien
prisma migrate deployläuft auf leerer Datenbank durchpnpm db:seedzweimal hintereinander ohne Fehler und ohne DatenänderungtagCodebenutzt nur das vorgegebene AlphabetABCDEFGHJKLMNPQRSTUVWXYZ23456789und enthält damit nie0,O,1oderI; eindeutig über 10 000 Ziehungennext buildgelingt ohne gesetzteDATABASE_URLBeim Bauen aufgefallen
ABCDEFGHJKLMNPQRSTUVWXYZ23456789enthält einL, obwohl die Klammer daneben „ohne 0/O,1/I/L" sagt. Beides zugleich geht nicht: ohne
Lblieben 31 Zeichen. Wir folgen dem wörtlichenAlphabet, weil die Klammer die Verwechslungsgruppen benennt und keine Ausschlussliste ist:
aus
{0, O}sind beide weg, aus{1, I, L}sind1undIweg — dadurch istLeindeutig.Das Präfix
LZ-enthält ohnehin in jedem Code einL. Das Akzeptanzkriterium oben istentsprechend richtiggestellt; ein grüner Test über einem unerfüllten Kriterium wäre schlimmer
als ein korrigiertes Kriterium.
Suchspalte
Schrittergebnis.wertfehlte der Trigramm-Index; die im Lauf gewählte Abteilung warnur ein Wert in
variablenstatt ein Bezug, wodurch die Druckansicht eines archivierten Laufsnicht reproduzierbar gewesen wäre; und für die Kundenbindung eines Benutzers (#008) gab es
keine Struktur, sie hätte also nur im Token stehen können — gegen die eigene Regel, Rechte pro
Anfrage aus der Datenbank zu lesen.
DELETEauf einen Lauf hätte Schrittergebnisse und Kommentare stillschweigendmitgenommen. Jetzt
Restrict; ein Lauf lässt sich nur noch bewusst und in der richtigenReihenfolge entfernen. Dasselbe für das
Protokoll, das seinen Urheber nicht mehr verlierenkann.
tagCodewäre nach dem Löschen eines Geräts wieder frei gewesen. DeshalbTagCodeVergabeals Vergabebuch, aus dem nie gelöscht wird.
prisma migrate reset --forcehat den Bestätigungsdialog trotzdem gezeigt und dieAdvisory-Sperre gehalten. Schneller und verlässlicher:
DROP SCHEMA public CASCADEper psql,dann
migrate dev --create-only.Quelle:
docs/issues/006-datenmodell.md— dort gepflegt, hierher gespiegelt.