Florian Wilde f8d7752c27 Eigene Vorlagen fuer Anschreiben
Wer zum dritten Mal denselben Brief tippt, tippt ihn beim vierten Mal
falsch. Neu unter Einstellungen -> Schreibvorlagen: eigene Texte
hinterlegen, beim freien Schreiben auswaehlen, Platzhalter fuellen sich
aus den Daten, die ohnehin im System stehen.

Aufbau:
- Die Ersetzung als reine Funktionen in schreiben/platzhalter.ts. Drei
  Faelle, keiner davon still: Wert einsetzen, sichtbare Luecke
  [Aktenzeichen des Gerichts], unbekannter Platzhalter markiert. Ein
  Tippfehler in der Vorlage darf das Schreiben nicht verhindern - aber
  er muss ins Auge springen.
- Die Bestellungsabsatz-Warnung ist zweistufig: Der erste Speicherversuch
  ohne ihn kommt mit einer Rueckfrage zurueck, erst die Bestaetigung
  speichert. Ohne den Nachweis der Vertretungsmacht kommt keine Auskunft,
  und das faellt erst nach vier Wochen auf. Ein ausformulierter Absatz
  zaehlt so viel wie der Platzhalter, ein halber Nachweis ("Beschluss der
  Eigentuemerversammlung") nicht.
- Vier mitgelieferte Vorlagen bleiben Code und damit unveraenderlich;
  "Als Kopie uebernehmen" legt eine eigene Fassung an. Fuer Rueckkaufswert
  und Kuendigung fuehrt die Oberflaeche weiter zuerst zu den
  fallbezogenen Erzeugern - die rechnen Kuendigungstermine aus, was eine
  Vorlage nicht kann.
- Vorlagen haengen an der betreuten Person, nicht am Benutzer: Sie
  beziehen sich auf ihren Fall, liegen im Papierkorb wie alles andere und
  sind im Vollexport ihrer Akte enthalten.

Der Editor haelt Betreff und Text als eigenen State. Beim Wechsel der
Vorlage ueberlebte der alte Entwurf die Navigation, die Vorlage kam nie
im Textfeld an - ein key auf dem Editor setzt ihn neu auf. Gefunden hat
das erst der End-to-End-Test.

Geprueft: dreizehn Einheitstests fuer die Ersetzung, sechs
End-to-End-Tests fuer Warnung, Kopie, Aufloesung und Papierkorb.

Refs #7
2026-08-23 13:41:51 +02:00
2026-08-23 13:41:51 +02:00
2026-08-23 13:41:51 +02:00
2026-08-23 13:41:51 +02:00
2026-08-23 13:41:51 +02:00
2026-08-23 13:41:51 +02:00
2026-08-23 02:42:26 +02:00
2026-08-23 13:41:51 +02:00
2026-08-22 18:44:56 +02:00
2026-08-23 13:41:51 +02:00

Betreuungs-Cockpit

Arbeits- und Dokumentationswerkzeug für die ehrenamtliche rechtliche Betreuung eines Angehörigen: Fristen, Vorgänge, Belege, Vermögen, Sozialhilfe, Berichte.

Keine Rechtsberatung. Die Anwendung rechnet Fristen aus, sortiert Unterlagen und erzeugt Berichte. Sie kennt den Einzelfall nicht. Jeder Hinweis mit Paragraphen-Bezug trägt den sichtbaren Vermerk, dass er die Rückfrage bei Betreuungsgericht, Sozialhilfeträger oder Anwalt nicht ersetzt. Wer sich darauf verlässt, statt beim Rechtspfleger nachzufragen, kann Geld und Ansprüche verlieren.


Inhalt


Was die Anwendung kann

Bereich Kurz gesagt
Heute Eine Seite, die morgens die Frage beantwortet, was ansteht. Fristen-Ampel, wer am Zug ist, offene Nachforderungen des Bezirks, ungeprüfter Posteingang, Kennzahlen.
Fristen Die gesetzlichen Pflichtfristen entstehen von selbst aus dem Bestellungsdatum. Liste, Kalender, ICS-Feed zum Abonnieren.
Vorgänge Fallakte je Angelegenheit, mit Vorlagen (Heimeinzug, Hausverkauf, Widerspruch …) und der festen Ablaufkette für genehmigungspflichtige Geschäfte.
Dokumente Foto oder Scan hochladen, Texterkennung, Volltextsuche über alle Schreiben, Posteingangsbuch, Belegmappe als eine PDF.
Vermögen & Kasse Kassenbuch mit Belegzwang, Konten, Vermögensposten mit Wert-Historie, Vermögensverzeichnis und Rechnungslegung als PDF.
Sozialhilfe Antragsakte, Nachforderungs-Tracker, Bescheid-Prüfung mit Rechtsbehelfsfrist, Heimkosten-Gegenüberstellung, Schonvermögens-Bewertung.
Immobilie Stammdaten, Belastungen, Wert-Historie, Status im Sozialhilfeverfahren, Pflichten-Checkliste bei Leerstand.
Verträge Versicherungen und laufende Verträge mit Kündigungsfristen, Rückkaufswert-Anfrage, Kündigungs-Assistent.
Heim & Pflege Heimvertrag, Leistungszuschläge, Pflegegrad-Historie, Gesundheitsverlauf mit COPD-Bezug.
Kontakte Verzeichnis aller Stellen, Ein-Klick-Kontaktprotokoll, Auswertung für den Jahresbericht.
Berichte Vermögensverzeichnis, Jahresbericht, Rechnungslegung, Belegmappe, Unterlagenpaket für den Bezirk, Vollexport.

Die drei Dokumente, die du wirklich brauchst

Wofür Wo
Die Anwendung aufsetzen — auf einem Server, der Docker und einen Proxy schon hat, mit Kontrolle nach jedem Schritt docs/INSTALLATION.md
Täglich damit arbeiten — die zwölf Handgriffe, der Jahresrhythmus, was tun wenn etwas schiefgeht docs/ANLEITUNG.md, in der Anwendung unter /anleitung
Wissen, was sich ändert — was neu ist, was du vor einem Update beachten musst CHANGELOG.md

Dazu: docs/ROADMAP.md für das, was als Nächstes kommt, und docs/BAUPROTOKOLL.md für die Frage, wie das hier entstanden ist.

Der Rest dieser Datei ist die Kurzfassung für alle, die schon wissen, was sie tun.


Schnellstart zum Ausprobieren

Voraussetzungen: Node.js ≥ 20.11, Docker und Git.

git clone https://git.wilde-server.de/florian.wilde/betreuungs-cockpit.git
cd betreuungs-cockpit
npm install

1. Datenbank und Objektspeicher starten. Beide horchen nur auf 127.0.0.1, gehen also nicht ins Netz hinaus:

docker compose -f docker-compose.dev.yml up -d

2. Konfiguration anlegen. Die Geheimnisse erzeugst du dir:

cp .env.example .env
npm run schluessel        # gibt AUTH_SECRET, ENC_KEY_V1, INDEX_KEY aus

Trage die ausgegebenen Werte in die .env ein und setze:

DATABASE_URL="postgresql://cockpit:entwicklung@localhost:5433/betreuungs_cockpit?schema=public"

3. Datenbank einrichten:

npx prisma migrate dev --name fundament
npm run db:sql            # Volltextsuche und Audit-Schutz
npm run db:seed

4. Starten:

npm run dev

Dann http://localhost:3000 öffnen. Beim ersten Aufruf führt dich ein Assistent in drei Schritten durch Zugang, zweiten Faktor und die Stammdaten der betreuten Person.

Mit Beispieldaten ansehen

Um zu sehen, wie die Anwendung mit Inhalt aussieht:

echo "SEED_DEMO=true" >> .env
npm run db:seed

Das legt den Fall „Erika Musterfrau" an — COPD GOLD III, Pflegegrad 3, Heimeinzug Januar 2026, leerstehendes Haus in Bindlach, zwei Versicherungen, laufender Antrag beim Bezirk Oberfranken mit acht Nachforderungen. Der Demofall wird in der Produktion nicht angelegt und lässt sich später über den Papierkorb vollständig entfernen.


Produktivbetrieb mit Docker Compose

Zum ersten Mal? Nimm die Schritt-für-Schritt-Anleitung. Sie fängt beim leeren Server an, hat nach jedem Abschnitt eine Kontrolle und endet mit einer Abnahmeliste. Was hier steht, ist die Kurzfassung.

Das Compose-Setup bringt drei Dienste mit: die Anwendung, PostgreSQL und MinIO als S3-kompatiblen Objektspeicher, dazu einen Sicherungsdienst.

Einen Reverse Proxy enthält es bewusst nicht — er ist auf dem Zielserver bereits vorhanden. Die Anwendung horcht nur auf 127.0.0.1:3000. Die Datenbank hat keine Portfreigabe und ist ausschließlich über das interne Docker-Netz erreichbar.

# 1. Konfiguration
cp .env.example .env
npm run schluessel

# In der .env mindestens setzen:
#   POSTGRES_PASSWORD, AUTH_SECRET, AUTH_URL (die echte öffentliche Adresse),
#   ENC_KEY_V1, INDEX_KEY,
#   S3_ACCESS_KEY_ID, S3_SECRET_ACCESS_KEY, SICHERUNG_PASSWORT

# 2. Bauen und starten
docker compose up -d --build

# 3. Datenbank vorbereiten
#    (die Migrationen laufen beim Start der Anwendung automatisch;
#     die beiden SQL-Skripte müssen einmalig dazu)
docker compose exec app node node_modules/prisma/build/index.js db execute \
  --schema prisma/schema.prisma --file prisma/sql/volltextsuche.sql
docker compose exec app node node_modules/prisma/build/index.js db execute \
  --schema prisma/schema.prisma --file prisma/sql/audit-schutz.sql

# 4. Bucket im Objektspeicher anlegen
docker compose exec minio mc alias set lokal http://localhost:9000 \
  "$S3_ACCESS_KEY_ID" "$S3_SECRET_ACCESS_KEY"
docker compose exec minio mc mb lokal/betreuungs-cockpit

Danach im Browser über die eigene Domain auf /ersteinrichtung gehen und den ersten Zugang anlegen. Die Seite sperrt sich selbst, sobald ein Zugang existiert.

Aktualisieren

./scripts/update.sh

Kommst du von 1.1.0 oder älter? Dort lag update.sh ohne Ausführungsrecht im Repository — ./scripts/update.sh endet dann in „Permission denied". Einmalig hilft:

bash scripts/update.sh

Ab 1.1.1 ist das Recht gesetzt und der Punktschrägstrich funktioniert.

Das Skript sichert zuerst, zeigt den Changelog zwischen altem und neuem Stand, fragt nach, schaltet um, baut neu und führt danach die beiden SQL-Skripte aus. Schlägt die Sicherung fehl, wird nicht aktualisiert.

--pruefen sieht nur nach, ohne etwas zu ändern. Eigene Änderungen an versionierten Dateien werden vor dem Umschalten beiseite gelegt (git stash), die .env bleibt unangetastet.

Einzelheiten und der Weg von Hand: docs/INSTALLATION.md.


Reverse Proxy

Die Anwendung beendet selbst kein TLS und bringt keinen Proxy mit. Sie horcht standardmäßig auf 127.0.0.1:3000.

Läuft der Proxy auf demselben Server, ist damit alles gesagt: Er zeigt auf 127.0.0.1:3000, fertig.

Läuft er auf einem anderen Server, setz in der .env die Adresse, über die er herankommt — am besten die eines privaten Netzes oder eines WireGuard-Tunnels:

APP_BIND=10.0.0.5
APP_PORT=3000

0.0.0.0 nur, wenn eine Firewall den Port ausschließlich für die IP des Proxy-Servers öffnet. Zwischen Proxy und Anwendung läuft unverschlüsseltes HTTP — dieser Weg darf nie durchs offene Netz gehen.

Was der Proxy leisten muss

Anforderung Was ohne sie passiert
TLS beenden Der Browser lehnt Cookies mit Secure-Kennzeichen ab, die Anmeldung schlägt stumm fehl
Host durchreichen Auth.js vergleicht ihn mit AUTH_URL und weist die Anmeldung ab
X-Forwarded-Proto: https Die Anwendung baut Adressen mit http
X-Forwarded-For Die Login-Sperre zählt alle Zugriffe als denselben Anschluss
Uploads ≥ 30 MB Fotos mehrseitiger Bescheide werden abgeschnitten
Zeitgrenze ≥ 10 Minuten Belegmappe, Rechnungslegung und Vollexport brechen ab
Antwort nicht puffern Der Vollexport kommt als Strom

Keine eigenen Sicherheitskopfzeilen setzen. CSP, X-Frame-Options, nosniff und HSTS kommen aus der Anwendung. Die CSP enthält eine Nonce, die pro Anfrage neu ist — eine feste CSP aus dem Proxy blockiert die eigenen Skripte.

Fertige Konfigurationen für Caddy, nginx und Traefik, mit Zeitgrenzen und den Gegenproben, stehen in docs/INSTALLATION.md, Schritt 7.


Alternative: Vercel und Neon

Technisch möglich, hier aber nicht empfohlen. Der Grund ist nicht technischer Natur:

Vercel Inc. und Neon Inc. sind US-Unternehmen. Auch bei Wahl einer EU-Region unterliegen sie potenziell dem US-CLOUD-Act. Für Gesundheitsdaten einer nicht selbst entscheidungsfähigen Person — also besondere Kategorien nach Art. 9 DSGVO — ist das eine Abwägung, die man bewusst treffen und dokumentieren muss. Ein eigener Server in Deutschland nimmt einem diese Abwägung ab.

Wenn du es dennoch willst:

  1. Neon-Projekt in der Region eu-central-1 (Frankfurt) anlegen, Verbindungszeichenfolge als DATABASE_URL hinterlegen.
  2. In Vercel unter Settings → Functions die Region fra1 festlegen.
  3. Alle Umgebungsvariablen aus .env.example als Vercel-Secrets eintragen. ENC_KEY_V1 und INDEX_KEY gehören zwingend dazu.
  4. Objektspeicher: nicht Vercel Blob, sondern ein EU-Anbieter, z. B. Hetzner Object Storage (Falkenstein oder Nürnberg). SPEICHER_TREIBER=s3.
  5. OCR und PDF laufen nicht auf Vercel. Tesseract braucht ein System- Binary, das es in der Serverless-Umgebung nicht gibt. Setze OCR_AKTIV=false oder betreibe einen kleinen Worker daneben. PDF-Erzeugung mit @react-pdf/renderer funktioniert, kann bei großen Belegmappen aber ins Zeitlimit laufen.
  6. Migrationen laufen nicht automatisch: npx prisma migrate deploy als Build-Schritt oder von Hand.
  7. Mit beiden Anbietern ist ein Auftragsverarbeitungsvertrag nach Art. 28 DSGVO abzuschließen.

Sicherheit und Datenschutz

Hier liegen Gesundheits- und Finanzdaten einer Person, die nicht selbst entscheiden kann. Was dafür umgesetzt ist:

Verschlüsselung

  • Transport über TLS (durch den Reverse Proxy).
  • Zusätzlich anwendungsseitige Feldverschlüsselung mit AES-256-GCM für Gesundheitsdaten, Diagnosen, den ermittelten Willen, IBANs, Versicherungsnummern, Renten- und Steuernummern sowie alle Freitext-Notizen. Der Schlüssel liegt in der Umgebung, nicht in der Datenbank — ein Datenbank-Dump allein nützt niemandem etwas.
  • Der Feldname wird mitauthentifiziert: ein Geheimtext lässt sich nicht von „Diagnosen" in „Notizen" umhängen.
  • Blindindex (HMAC-SHA256 mit eigenem Schlüssel) für die IBAN, damit die Frage „kenne ich dieses Konto schon?" beantwortbar bleibt, ohne sie im Klartext zu speichern.

Zugang

  • Passwörter mit Argon2id (19 MiB, t=2, p=1 — OWASP-Empfehlung).
  • Zweiter Faktor ist Pflicht und lässt sich nicht abschalten. TOTP nach RFC 6238, kompatibel mit Aegis, 2FAS, Bitwarden, 1Password.
  • Passkeys (WebAuthn/FIDO2) als zweiter Anmeldeweg — auch für die erneute Bestätigung vor sensiblen Aktionen. Sie ersetzen den zweiten Faktor, statt zu ihm hinzuzukommen: Nutzerverifikation wird verlangt (userVerification: 'required') und serverseitig nachgeprüft, ein Schlüssel ohne sie wird abgewiesen. Damit sind es dieselben zwei Faktoren — Besitz des Geräts und dessen Entsperrung —, nur an die Domain gebunden und deshalb nicht abphishbar. TOTP muss trotzdem eingerichtet sein, bevor sich ein Passkey anlegen lässt: Er hängt an einem Gerät und darf nicht der einzige Weg hinein sein.
  • Rate-Limiting am Login, nach E-Mail und nach Anschluss. Die Sperre wird geprüft, bevor Argon2 überhaupt rechnet.
  • Die Antwortzeit ist auf eine Mindestdauer gestreckt, damit sie nicht verrät, welche E-Mail-Adressen existieren.
  • Sitzungsablauf nach 30 Minuten Leerlauf. Für Export, endgültiges Löschen und die Benutzerverwaltung ist eine erneute Anmeldung nötig.
  • Passwortwechsel beendet alle offenen Sitzungen, auch auf fremden Geräten.

Rollen

  • betreuer — voller Zugriff auf die zugeordneten Betreuten.
  • mitleser — nur lesend, und nur die ausdrücklich je Modul freigegebenen Bereiche (z. B. Geschwister, die den Kontaktverlauf sehen dürfen, aber nicht die Finanzen).
  • admin — Benutzerverwaltung.

Geprüft wird immer serverseitig gegen die Betreuten-ID, nie nur im UI ausgeblendet.

Audit-Log

Jede schreibende Aktion und jeder Dokumentzugriff wird protokolliert: wer, wann, was, alter Wert → neuer Wert. Die Änderungen liegen verschlüsselt.

Das Log ist unveränderlich: ein Datenbank-Trigger weist UPDATE und DELETE ab — auch bei direktem Zugriff mit psql. Damit ist es der Nachweis gegenüber Betreuungsgericht und möglichen Miterben. Nachprüfen lässt sich das so:

docker compose exec db psql -U cockpit betreuungs_cockpit \
  -c "UPDATE audit_log SET entitaet='x';"
# ERROR: Das Audit-Log ist unveraenderlich. UPDATE auf audit_log ist nicht zulaessig.

Content-Security-Policy

Streng gesetzt, mit einer Nonce pro Anfrage. Keine externen Schriften, kein CDN, keine Analytics, keine Tracker. Der QR-Code für den zweiten Faktor wird serverseitig erzeugt und als Data-URI eingebettet, nicht von einem fremden Dienst geholt.

Uploads

Der Dateityp wird an den Magic Bytes geprüft, nicht an der Endung und nicht am gemeldeten MIME-Typ. Erlaubt sind PDF, JPEG, PNG, WEBP, TIFF, HEIC. Ausgeliefert wird nur über kurzlebige signierte URLs, immer mit Content-Disposition: attachment und X-Content-Type-Options: nosniff — ein hochgeladenes HTML-Dokument kann also nie als Seite ausgeführt werden.

Löschen

Papierkorb mit 30 Tagen, danach echtes Löschen. Endgültiges Löschen erfordert eine frische Anmeldung.


Was rechtlich zu tun ist

Das erledigt keine Software für dich:

  1. Auftragsverarbeitungsvertrag nach Art. 28 DSGVO mit dem Hoster und mit dem Betreiber des Objektspeichers. Bei Hetzner findest du ihn im Kundenkonto unter Verträge → AV-Vertrag, bei Netcup und IONOS ähnlich. Ohne AV-Vertrag ist der Betrieb formal rechtswidrig.
  2. Verarbeitungsverzeichnis nach Art. 30 DSGVO. Ein Textbaustein liegt in docs/verarbeitungsverzeichnis.md und muss nur ergänzt werden.
  3. Datenschutzhinweise für die betreute Person und für Mitleser: docs/datenschutzhinweise.md.
  4. Server in Deutschland oder mindestens in der EU. Keine US-Dienste im Datenpfad.
  5. Bei einer Datenpanne: Meldepflicht binnen 72 Stunden (Art. 33 DSGVO) an die zuständige Aufsichtsbehörde — in Bayern das BayLDA.

Sicherung und Wiederherstellung

Was automatisch läuft

Der Dienst sicherung legt täglich um 03:15 Uhr einen Stand nach ./backups:

  • pg_dump im eigenen Format, damit auch einzelne Tabellen zurückholbar sind
  • verschlüsselt mit AES-256 und PBKDF2 aus SICHERUNG_PASSWORT
  • SHA-256-Prüfsumme daneben
  • Selbstprüfung: direkt nach dem Schreiben wird geprüft, ob sich die Datei wieder entschlüsseln lässt. Schlägt das fehl, endet der Lauf mit Fehler statt mit einem stillen Scheinerfolg.
  • Aufbewahrung 30 Tage, der jeweils erste Stand eines Monats bleibt dauerhaft

Von Hand anstoßen:

docker compose exec sicherung /bin/sh /sicherung.sh

Zurückspielen

docker compose exec sicherung /bin/sh /wiederherstellung.sh \
  /backups/cockpit_2026-08-22_0315.dump.enc

Das Skript prüft die Prüfsumme, fragt nach und spielt zurück. Danach:

docker compose exec app npm run db:sql   # Trigger und Indizes erneut anlegen

Wiederherstellung üben

Ein Backup, das nie zurückgespielt wurde, ist kein Backup. Nimm dir einmal im Jahr zwanzig Minuten:

# 1. Testdatenbank neben der echten anlegen
docker compose exec db createdb -U cockpit probe

# 2. Sicherung dorthin zurückspielen
docker compose exec -e PGDATABASE=probe -e SICHERUNG_OHNE_RUECKFRAGE=ja \
  sicherung /bin/sh /wiederherstellung.sh /backups/cockpit_<datum>.dump.enc

# 3. Stichprobe: sind die Daten da?
docker compose exec db psql -U cockpit probe \
  -c "SELECT vorname, nachname FROM betreute;" \
  -c "SELECT count(*) FROM audit_log;"

# 4. Aufräumen
docker compose exec db dropdb -U cockpit probe

Wichtig ist der letzte Schritt in der Anwendung selbst: eine betreute Person öffnen und nachsehen, ob die Diagnosen im Klartext erscheinen. Erst dann weißt du, dass nicht nur die Datenbank, sondern auch der Schlüssel stimmt.

Was das Backup NICHT enthält

Den Verschlüsselungsschlüssel. ENC_KEY_V1 liegt in der .env, nicht in der Datenbank. Das ist Absicht: ein gestohlenes Backup ist damit wertlos. Für dich bedeutet es aber: ohne diesen Schlüssel ist auch dein eigenes Backup halb blind. Diagnosen, IBANs, Versicherungsnummern und Notizen bleiben unlesbar.

Ebenfalls nicht enthalten: die Dateien im Objektspeicher. Die sichere gesondert, zum Beispiel:

docker compose exec minio mc mirror lokal/betreuungs-cockpit /backups/dokumente

Oder du benutzt regelmäßig den Vollexport in der Anwendung — der packt Dateien, Daten und Berichte in ein ZIP.


Schlüsselverwaltung

ENC_KEY_V1 ist der wichtigste Wert der ganzen Installation.

Sichere ihn getrennt vom Backup. Passwortmanager und ausgedruckt bei den Betreuungsunterlagen. Wenn er verloren geht, sind die verschlüsselten Felder unwiederbringlich weg — es gibt keine Hintertür.

Schlüssel wechseln

Die Anwendung kann mehrere Schlüsselversionen gleichzeitig lesen:

# 1. Neuen Schlüssel erzeugen und als ENC_KEY_V2 in die .env eintragen
npm run schluessel

# 2. Umschalten - ab jetzt wird mit V2 verschlüsselt
ENC_KEY_AKTIV=2

# 3. Neu starten
docker compose up -d

ENC_KEY_V1 bleibt in der .env stehen, solange noch Daten damit verschlüsselt sind — sonst sind ältere Datensätze nicht mehr lesbar. Erst wenn alle Felder auf die neue Version umgeschrieben wurden, darf der alte Schlüssel weg.


Entwicklung

npm run dev              # Entwicklungsserver
npm run typecheck        # TypeScript ohne Ausgabe prüfen
npm run lint
npm test                 # Vitest: Fachlogik
npm run test:e2e         # Playwright: kritische Abläufe
npm run db:studio        # Prisma Studio
npm run protokoll        # Bauprotokoll aus der Git-Historie
npm run version -- patch # Version anheben, Changelog vorbereiten

Konventionen — verbindlich für alle Beiträge: docs/KONVENTIONEN.md. Schnittstellen zwischen den Modulen: docs/SCHNITTSTELLEN.md.

Bildschirmfotos erzeugen

node scripts/screenshots.mjs /heute /fristen /vorgaenge

Meldet sich mit dem Entwicklungszugang an und fotografiert die Seiten am Schreibtisch und am Handy, hell und dunkel. Setzt voraus, dass SEED_DEV_TOTP_SECRET in der .env steht — das gilt nur außerhalb der Produktion.

Betriebswissen, das man einmal gebraucht hat

  • Nicht gleichzeitig npm run dev und npm run build laufen lassen. Beide schreiben nach .next; das Ergebnis ist ein Build, dem einzelne Chunks fehlen, und der Fehler (Cannot find module './638.js') zeigt nicht auf die Ursache. Im Zweifel rm -rf .next und neu bauen.
  • AUTH_URL muss exakt der Adresse entsprechen, unter der du die Anwendung aufrufst — bis auf Port und Schreibweise. Steht dort localhost und du rufst 127.0.0.1 auf, wird das Sitzungs-Cookie für die falsche Herkunft gesetzt und die Anmeldung landet stumm wieder auf dem Formular.
  • Der Standalone-Server (node server.js, so startet der Container) liest keine .env. Alle Variablen kommen von außen — im Compose-Setup über den Abschnitt environment:.
  • Beim ersten Start eines Containers laufen die Migrationen automatisch mit. Die beiden SQL-Skripte (Volltextsuche, Audit-Schutz) sind einmalig von Hand anzustoßen, siehe oben.

Technische Grundlage

Next.js 15 (App Router) · TypeScript · Tailwind CSS · shadcn/ui-Bausteine · PostgreSQL 17 mit Prisma · Auth.js · Zod · Server Actions · @react-pdf/renderer · Tesseract (deutsch) · Vitest · Playwright

Bewusste Abweichungen von shadcn/ui im Original: Bedienelemente sind 44 px hoch statt 36 px, Auswahlfelder benutzen das native <select>, und Eingabefelder haben mindestens 16 px Schrift. Alles drei, damit die Anwendung am Handy zuverlässig zu bedienen ist — im Heim und auf dem Amt.


Bauprotokoll

Jeder Commit trägt die Trailer Etappe: und Phase:. Daraus lässt sich nachvollziehen, wie lange welcher Bauabschnitt gedauert hat:

npm run protokoll            # Tabelle auf der Konsole
npm run protokoll:md         # Markdown für docs/BAUPROTOKOLL.md

Der aktuelle Stand liegt in docs/BAUPROTOKOLL.md — dort stehen auch die Laufzeiten der parallel gebauten Etappen und die Fehler, die während des Baus aufgefallen sind.


Stand

nachzuzählen mit
Zeilen TypeScript/TSX 79.102 git ls-files "*.ts" "*.tsx" | xargs wc -l
Prisma-Modelle 34 grep -c '^model ' prisma/schema.prisma
Seiten 82 find src/app -name page.tsx | wc -l
Unit-Tests (Vitest) 602 npm test
End-to-End-Tests (Playwright) 87 npm run test:e2e — 47 Tests über zwei Geräteprofile, geräteabhängige übersprungen
TypeScript-Fehler 0 npm run typecheck

Die Befehle stehen dabei, weil die Zahlen sonst altern, ohne dass es jemandem auffällt — bei den Modellen und Seiten war genau das passiert.

Alle acht Etappen sind umgesetzt. Der Produktionsbuild ist gegen den fertigen Standalone-Server geprüft: strenge CSP aktiv, keine Konsolenfehler, keine Anfrage an einen fremden Host.

S
Description
No description provided
Readme
2.7 MiB
2026-09-01 14:40:31 +00:00
Languages
TypeScript 97.4%
JavaScript 1.2%
Shell 0.9%
PLpgSQL 0.2%
Dockerfile 0.2%