build(quick-261009-p0m): Laufzeitabbilder ohne Entwicklungswerkzeuge

- api: eigene Stufe prod-deps (nur Betriebsabhaengigkeiten, Prisma-Client dort erzeugt), Laufzeitstufe direkt von node:24-alpine
- api und web: npm, npx, corepack und yarn des Node-Abbilds entfernt
- api-Abbild 1,59 auf 1,12 GB; Fundzahl 3 kritisch/45 hoch auf 0 kritisch/6 hoch
- Beleg: Start gegen frische Datenbank (63 Migrationen), Rauchtests am neuen Stack
- Sicherheitsprotokoll, Betriebs- und Entwicklungsanleitung, CHANGELOG

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-10-09 20:29:00 +02:00
parent 072f9551dc
commit 2a8a7d4270
6 changed files with 65 additions and 12 deletions
+16
View File
@@ -970,6 +970,22 @@ nicht weiter. Der Ordner `security-reports/` ist von Git ausgeschlossen.
## Konventionen und Fallstricke
**Laufzeitabbilder ohne Entwicklungswerkzeuge (quick-261009-p0m):** Das Server-Abbild
(`apps/api/Dockerfile`) hat eine eigene Stufe `prod-deps`: dort läuft
`pnpm install --frozen-lockfile --prod --filter=@tessera/api...`, und der Prisma-Client wird in
genau dieser Stufe erzeugt (der `postinstall` von `apps/api` braucht das Schema vor der
Installation, deshalb wird `apps/api/prisma` vorher kopiert). Ein fester Pfad aus der
Builder-Stufe wäre fehleranfällig, weil der Ordnername von `@prisma/client` im pnpm-Speicher von
den aufgelösten Peer-Paketen abhängt. Die Laufzeitstufe beider Abbilder (`api` und `web`) kommt
direkt aus `node:24-alpine` und entfernt als Erstes npm, npx, corepack und yarn. Im Container gibt
es dadurch kein `pnpm` und kein `npm` mehr; Befehle laufen mit `node` (der Prisma-Aufruf des
Startskripts liegt unter `apps/api/node_modules/.bin/prisma`). **Falle:** Importiert Laufzeit-Code
ein Paket, das nur unter `devDependencies` steht, scheitert das erst im Container – die Unit-Tests
laufen mit allen Abhängigkeiten und fangen es nicht. Dann gehört das Paket in `dependencies`.
**Beweis nach jeder Änderung an den Abbildern:** ein Start gegen eine frische, leere Datenbank (alle
Migrationen laufen, die Zeile „Tessera API running“ erscheint) plus die Rauchtests am neu gebauten
Stack – so wie es `checks/fresh-db-start.sh` im Auftrag quick-261009-p0m vormacht.
**NestJS-Routenreihenfolge:** NestJS matcht Routen in Deklarationsreihenfolge. Eine statische Route
wie `@Get('source-config')` **muss vor** einem `@Get(':id')`-Platzhalter derselben Klasse stehen —
sonst interpretiert der Platzhalter den literalen Pfadteil als `id` und "beschattet" die statische