Fachdokumente nach docs/, Entscheidungen festgehalten
CLAUDE.md und README.md verweisen auf docs/, die Dateien lagen aber im Wurzelverzeichnis. Verschoben. docs/13-entscheidungen.md hält fest, wo die Dokumente sich widersprechen und wie es aufgelöst wurde. Der wichtigste Punkt: 04-datenmodell.md verlangt eine Regel, die jedes UPDATE auf CaseEntryVersion verwirft, 05-akten-und-vorlagen.md verlangt, dass eine Umklassifizierung rückwirkend auf alle Versionen wirkt. Beides zusammen geht nicht. Aufgelöst über einen Trigger, der die Tabelle anhängend hält, aber das Anheben von minClearance zulässt. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
# Architektur
|
||||
|
||||
## Dienste
|
||||
|
||||
```
|
||||
Browser
|
||||
└─ cloudflared (Tunnel, TLS und Kompression liegen bei Cloudflare)
|
||||
└─ web Next.js 15, App Router, Server Actions, SSE-Endpunkt
|
||||
├─ pgbouncer ─ db PostgreSQL 16
|
||||
├─ files Volume, getrennt in quarantine/ und store/
|
||||
├─ clamav clamd, angesprochen über Unix-Socket
|
||||
└─ pdf Playwright-Container für PDF-Erzeugung
|
||||
|
||||
worker discord.js, hängt am Gateway, schreibt Rollen in db
|
||||
db-backup Dauerläufer mit Cron, nächtlicher Dump
|
||||
offsite-backup Dauerläufer mit Cron, restic auf zweites Ziel
|
||||
```
|
||||
|
||||
Alles liegt in **einem** Dockge-Stack. Kein Reverse Proxy im Stack: der Tunnel terminiert TLS,
|
||||
Cloudflare komprimiert, und Next.js liefert seine statischen Dateien selbst mit korrekten
|
||||
Cache-Headern aus. Am Router wird kein Port geöffnet.
|
||||
|
||||
Migrationen laufen im Startkommando von `web`, nicht in einem Init-Container. Begründung und
|
||||
die vollständige `compose.yaml` in `docs/11-betrieb.md`.
|
||||
|
||||
## Warum ein eigener Worker
|
||||
|
||||
Die Web-App fragt nie live bei Discord an. Der Worker hält `GuildMember` in Postgres aktuell:
|
||||
|
||||
- Gateway-Ereignisse `guildMemberUpdate`, `guildMemberRemove`, `guildMemberAdd`, `roleUpdate`
|
||||
- Voller Abgleich alle 5 Minuten als Sicherheitsnetz
|
||||
- Bei jeder Änderung: `pg_notify('member_changed', userId)`, damit die Web-App laufende
|
||||
Sessions invalidieren kann
|
||||
|
||||
Dadurch ist eine Rechteänderung in Discord in unter einer Sekunde in der App wirksam,
|
||||
ohne dass ein einziger Request in der App auf die Discord-API warten muss.
|
||||
|
||||
## Verzeichnisstruktur
|
||||
|
||||
```
|
||||
.
|
||||
├─ app/
|
||||
│ ├─ (auth)/ Login, Callback, Fehlerseiten
|
||||
│ ├─ (app)/
|
||||
│ │ ├─ dashboard/
|
||||
│ │ ├─ faelle/
|
||||
│ │ │ ├─ page.tsx Liste
|
||||
│ │ │ ├─ loading.tsx Skelett
|
||||
│ │ │ ├─ neu/
|
||||
│ │ │ └─ [caseNumber]/
|
||||
│ │ │ ├─ page.tsx Akte
|
||||
│ │ │ ├─ loading.tsx
|
||||
│ │ │ └─ @chat/ Paralleler Slot, lädt unabhängig
|
||||
│ │ ├─ personal/
|
||||
│ │ ├─ suche/
|
||||
│ │ └─ admin/ Rollen-Mapping, Vorlagen, Protokoll
|
||||
│ └─ api/
|
||||
│ ├─ auth/[...nextauth]/
|
||||
│ ├─ files/[id]/ Ausliefernder Endpunkt mit Rechteprüfung
|
||||
│ ├─ upload/
|
||||
│ └─ stream/ SSE
|
||||
├─ lib/
|
||||
│ ├─ auth/ Session, Rechteauflösung
|
||||
│ ├─ access/ Zugriffsprüfung als Prisma-Where-Fragmente
|
||||
│ ├─ db.ts
|
||||
│ ├─ audit.ts
|
||||
│ ├─ i18n/de.ts
|
||||
│ └─ realtime/
|
||||
├─ components/
|
||||
├─ worker/ Eigenes Paket, discord.js
|
||||
├─ prisma/
|
||||
├─ docs/
|
||||
└─ docker/
|
||||
```
|
||||
|
||||
## Datenflüsse, die man kennen muss
|
||||
|
||||
**Seitenaufruf einer Akte.** Server Component lädt Kopfdaten und Abschnitte in einer Abfrage.
|
||||
Verlauf und Chat hängen in eigenen `<Suspense>`-Grenzen und streamen nach. Die Seite ist
|
||||
sichtbar, bevor der Verlauf da ist.
|
||||
|
||||
**Neuer Vermerk.** Server Action schreibt `CaseEntry`, danach `revalidateTag('case:<id>')`
|
||||
und `pg_notify` für alle offenen SSE-Verbindungen dieser Akte. Der Client hat den Eintrag
|
||||
schon optimistisch angezeigt und ersetzt ihn beim Eintreffen anhand der Client-ID.
|
||||
|
||||
**Upload.** Datei geht per XHR mit Fortschritt an `/api/upload`, landet in `quarantine/`,
|
||||
Prüfkette läuft asynchron, Statuswechsel kommt über SSE zurück.
|
||||
|
||||
**Rechteänderung in Discord.** Worker schreibt, `pg_notify`, Web-App verwirft den Cache der
|
||||
betroffenen Session, nächster Request rechnet die Stufe neu.
|
||||
Reference in New Issue
Block a user