diff --git a/devlog/DEVLOG.md b/devlog/DEVLOG.md new file mode 100644 index 0000000..f488341 --- /dev/null +++ b/devlog/DEVLOG.md @@ -0,0 +1,118 @@ +# Entwicklungstagebuch + +Neuester Eintrag oben. Die Dauer je Sitzung stammt aus `devlog/zeiten.csv`, +die Übersicht aus `scripts/zeit.sh summe`. + +## Zeit je Meilenstein + +| Meilenstein | Art | Minuten | Stunden | +| ----------- | ------ | ----------------------------------- | ------- | +| M1 | claude | _(wird beim Abschluss eingetragen)_ | | + +--- + +## 2026-09-10 – M1: Fundament + +**Ziel der Sitzung:** Das Projekt von Grund auf aufsetzen: Gerüst, +Datenbank, Anmeldung, Rollen, Audit, Instanzsteuerung, Betrieb und +Dokumentation. + +### Erledigt + +- Projektgerüst mit Next 16, React 19, TypeScript strict, Tailwind 4, + alle Versionen exakt gepinnt +- `npm run pruefen` als Sammelprüfung: Prettier, ESLint, `tsc`, Vitest, + ShellCheck +- Datenmodell und erste Migration: Benutzer mit Rolle, OIDC-Konten, + Passkeys, WebAuthn-Herausforderungen, Bereiche, Audit +- Anmeldung über OIDC, Passkey und Passwort, je Instanz zuschaltbar +- Rollenprüfung in Seite **und** Server Action, Audit je Schreibaktion +- Instanzsteuerung mit 404 statt Hinweis +- Bereiche verwalten, Protokollseite, Startseite, Einstellungen +- `/api/health`, Dockerfile, Compose-Stapel mit Migrationsdienst +- `backup.sh`, `update.sh`, `scripts/release.sh`, `scripts/zeit.sh` +- Unit-, Integrations-, Migrations- und E2E-Tests +- Screenshot-Kette mit eigener Wegwerf-Instanz +- README, Schnellstart, Handbuch, ROADMAP, CHANGELOG + +### Entscheidungen mit Begründung + +**Prisma 7.10.0 statt der notierten Fassung 6.** Fassung 6 ist überholt, +Fassung 8 liegt nur als RC auf `latest`. Prisma 7 nimmt die Verbindung +nicht mehr im Schema entgegen — sie steht in `prisma.config.ts`, der +Client bekommt den pg-Treiberadapter. + +**TypeScript 6.0.3 statt 7.0.2.** TS 7 ist zwar `latest`, aber +`typescript-eslint@8.70` verlangt `typescript >=4.8.4 <6.1.0`. Mit TS 7 +wäre die Lint-Kette kaputt gewesen. + +**ESLint 9.39.5 statt 10.** Das in `eslint-config-next@16` gebündelte +`eslint-plugin-react` ruft noch `context.getFilename()`, das ESLint 10 +entfernt hat. Der Lint-Lauf brach reproduzierbar ab. + +**Eigener Auth.js-Adapter.** Der mitgelieferte `@auth/prisma-adapter` +greift fest auf `prisma.user` und `prisma.account` zu; unsere Modelle +heißen `Benutzer` und `Konto`. Der Adapter ist schmal und ersparte es, +die Fachbegriffe der Konvention zu opfern. + +**Passkeys direkt über SimpleWebAuthn 14.** Der WebAuthn-Provider von +Auth.js ist als experimentell markiert und auf SimpleWebAuthn 9 +festgelegt. Direkt angebunden bleibt die Fassung aktuell und der Ablauf +nachvollziehbar. Für die npm-Auflösung brauchte es `overrides`, weil +next-auth die älteren Fassungen als optionale Peers deklariert. + +**Sitzungen als JWT, Rolle trotzdem aus der Datenbank.** Credentials- +Provider verlangen JWT. Damit eine Sperre nicht bis zum Ablauf des Tokens +wirkungslos bleibt, liest der `session`-Callback Rolle und Status bei +jeder Anfrage frisch. Die Tabellen `sitzung` und `verifizierungs_token` +wären damit tot und wurden wieder entfernt, solange es nur eine einzige +Migration gab. + +**Migrationen als eigener Compose-Dienst.** Der Versuch, nur die +Prisma-CLI ins schlanke Laufzeitabbild zu kopieren, scheiterte an ihren +transitiven Modulen (`effect` über `@prisma/config`). Alle mitzunehmen +hätte das Abbild aufgebläht. Ein Einmaldienst aus der Bau-Stufe läuft vor +der App und muss sauber durchlaufen. + +**Aufgabe, Zeitblock und Kalenderkonto bewusst nicht modelliert.** Sie +kommen mit M2, M4 und M3 — dann mit den Feldern, die der jeweilige +Provider wirklich braucht, statt heute geraten. + +### Probleme und Lösungen + +| Problem | Lösung | +| ------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `prisma validate` lehnte `url` im Schema ab | Prisma 7 erwartet die Verbindung in `prisma.config.ts` und einen Treiberadapter im Client. | +| Integrationstest brach nach der ersten erwarteten Verletzung ab | In PostgreSQL bricht ein fehlgeschlagener Befehl die ganze Transaktion ab. Der Test läuft jetzt ohne Sammeltransaktion; alle Testdaten tragen eine Kennung und werden am Ende entfernt. | +| `prisma migrate diff` kannte `--from-url` nicht | In Prisma 7 heißen die Flaggen anders. Der Test vergleicht jetzt die eingespielte Wegwerf-Datenbank über `--from-config-datasource` gegen das Schema — und braucht damit keine Schattendatenbank. | +| Nach der Anmeldung ging es nicht zum ursprünglichen Ziel zurück | Das Layout leitet um, bevor die Seite drankommt, und kennt seinen Pfad nicht. Ein schlanker Proxy reicht ihn als Kopfzeile durch — ohne Anmeldelogik, weil er im Edge-Umfeld läuft. | +| Der Docker-Bau brach beim Sammeln der Seitendaten ab | Prisma-Client und Auth.js-Konfiguration wurden beim Laden des Moduls ausgewertet und brauchten Umgebungsvariablen, die es zur Bauzeit nicht gibt. Beide entstehen jetzt faul. | +| Ein Typfehler in `scripts/screenshots.ts` blockierte den Abbild-Bau | Entwicklungsskripte gehören nicht in den Docker-Kontext. `.dockerignore` schließt sie aus; geprüft werden sie von `npm run pruefen`. | +| ESLint beanstandete `setState` im Effekt | Der Hydratations-Trick im Farbschema-Knopf war unnötig: `next-themes` setzt die `dark`-Klasse vor der Hydratation, das erledigt CSS ohne Zustand. | + +### Eigene Fehler, die ich korrigiert habe + +- Die ersten vier Commits lagen direkt auf `main` statt auf einem + Meilenstein-Zweig und ohne Attributionszeile. Historie umgebaut: + `main` trägt nur noch einen leeren Wurzel-Commit, die Arbeit liegt auf + `m1-fundament`. +- Ein `git add -A` zog die Proxy-Umbenennung in den Doku-Commit. Wieder + getrennt. +- `scripts/screenshots.ts` wurde geschrieben, ohne danach `npm run +pruefen` laufen zu lassen; der Typfehler fiel erst im Docker-Bau auf. + +### Offene Punkte + +- Die `origin`-URL fehlt noch. Push und Merge stehen ohnehin erst nach + der Freigabe an. +- Der Schnellstart endet beim Passkey statt beim ersten Kalenderkonto und + der ersten Aufgabe — beides gibt es erst mit M3 und M2. +- Der Demo-Seed enthält noch keine erfundenen Termine; einen + Demo-Kalenderprovider gibt es erst mit M3. +- Lokal weichen die Ports ab (Datenbank 5435, App 3001), weil 5432 und + 3000 auf diesem Rechner belegt sind. In `.env.example` stehen die + üblichen Werte. + +### Commits + +Siehe `git log --oneline main..m1-fundament`. diff --git a/src/app/(app)/page.tsx b/src/app/(app)/page.tsx index 5d1b92a..5ca34da 100644 --- a/src/app/(app)/page.tsx +++ b/src/app/(app)/page.tsx @@ -10,10 +10,11 @@ import { wochenKapazitaet } from '@/lib/bereich' import { alsDatum, alsDauer } from '@/lib/zeit' import { verlangeAnmeldung } from '@/server/berechtigung' import { db } from '@/server/db' +import { jetzt } from '@/server/zeitgeber' export default async function StartSeite() { const benutzer = await verlangeAnmeldung('/') - const jetzt = new Date() + const heute = jetzt() const bereiche = await db.bereich.findMany({ where: { aktiv: true }, @@ -26,7 +27,7 @@ export default async function StartSeite() {

Guten Tag, {anrede}

-

{alsDatum(jetzt)}

+

{alsDatum(heute)}

diff --git a/src/components/kopfleiste.tsx b/src/components/kopfleiste.tsx index c863e1a..7cff907 100644 --- a/src/components/kopfleiste.tsx +++ b/src/components/kopfleiste.tsx @@ -35,12 +35,20 @@ export function Kopfleiste({ return (
-
- + {/* + Bewusst eine einzige Zeile auf allen Breiten: Wird es eng, scrollt die + Navigation waagerecht, statt umzubrechen und den Namen der Anwendung in + die zweite Zeile zu drängen. + */} +
+ Planer -