leistungsbilanz-ts/README.md
Julian Appel 2e3e85a8e5 Harden the container runtime and shut down cleanly
Running the stack on built output stopped the idle CPU load, but the
remaining findings from the deployment report were still open.

The Dockerfile is now multi-stage. The prod target runs on node:24-slim as
the "node" user instead of root and carries only its runtime dependencies;
"npm prune --omit=dev" drops tsx, drizzle-kit and typescript. The migration
scripts need drizzle-orm and better-sqlite3, both regular dependencies, so
they keep working. The dev target keeps devDependencies for compose.dev.yaml.

Both compose files get a mem_limit. The server stack also sets memswap_limit
to the same value, so a leak lets the OOM killer replace the container
instead of pushing the host into swap. The Express API is no longer
published on the host: it has no authentication and is reached through the
Next.js rewrite. Development ports are bound to the loopback interface.
Services drop all capabilities and run with no-new-privileges.

SIGTERM and SIGINT now close the HTTP server and the SQLite handle and exit,
instead of only logging the signal and waiting for the container's SIGKILL
ten seconds later. server.closeIdleConnections() releases the keep-alive
sockets the Next.js server holds, which would otherwise keep server.close()
pending for the whole grace period. The compose start commands exec into
node, because otherwise sh stays the foreground process and the signal never
reaches it.

Delete scripts/docker-start.sh rather than fixing its unsupervised
background API: both compose stacks set their own command, so the
single-container entrypoint has no caller left. Also remove drizzle.config.js
(a stray tsc artifact drizzle-kit never reads) and the pre-Next index.html
and styles.css.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-16 11:04:34 +02:00

150 lines
5 KiB
Markdown

# Leistungsbilanz
Leistungsbilanz ist eine Webanwendung für die elektrische Ausführungsplanung. Im
Mittelpunkt steht ein tabellenähnlicher Stromkreislisten-Editor, der Stromkreise,
Gerätezeilen und wiederverwendbare Projektgeräte fachlich getrennt behandelt.
Das Projekt befindet sich in aktiver Entwicklung. Der lokale Entwicklungsbetrieb
mit SQLite und Docker Compose ist unterstützt. Ein Produktionsdeployment,
Mehrbenutzerbetrieb und der Revit-/IFCGUID-Datenaustausch sind noch nicht
implementiert.
## Unterstützter Arbeitsablauf
- Projekte, Verteilungen, Etagen und Räume verwalten
- pro Verteilung geschützte Stromkreisgruppen für Beleuchtung, 1-phasige und
3-phasige Stromkreise verwalten
- Hauptschalter, Überspannungsableiter, Gruppenschutz und zusätzliche
Verteilergeräte in der Stromkreisliste abbilden
- Schutzgeräte je Stromkreis und optional je Gruppe konfigurieren
- leere, einzeilige und mehrzeilige Stromkreise abbilden
- Stromkreise und Gerätezeilen per Drag-and-drop umstrukturieren
- Projektgeräte einfügen, verknüpfen und kontrolliert synchronisieren
- komplette Stromkreisblöcke filtern und sortieren
- BMKs stabil halten und nur auf ausdrücklichen Befehl neu nummerieren
- Änderungen projektweit und auch nach einem Reload rückgängig machen und wiederholen
- benannte Sicherungspunkte anlegen, wiederherstellen und Revisionen einsehen
- automatische Sicherungspunkte mit begrenzter Aufbewahrung nutzen
## Technik
- Next.js 16, React 19 und TypeScript für das Frontend
- Express 5 und Zod für die API
- SQLite, `better-sqlite3` und Drizzle ORM für die Persistenz
- eigener Spreadsheet-Grid statt eines Bootstrap-Tabellenframeworks
- Node.js-Test-Runner für Domain-, Grid- und SQLite-Integrationstests
## Schnellstart mit Docker
Voraussetzungen:
- Git
- Docker Desktop mit Docker Compose
```powershell
git clone <repository-url>
Set-Location leistungsbilanz-ts
docker compose up --build --detach
```
Danach:
- Frontend: <http://localhost:3001>
- API-Healthcheck: <http://localhost:3001/health> (über den Next.js-Rewrite)
Die Express-API ist nicht mehr auf dem Host veröffentlicht. Sie hat keine
Authentifizierung und ist nur innerhalb des Compose-Netzes erreichbar; der
Zugriff läuft über den Rewrite im Web-Dienst.
Der API-Container führt ausstehende Migrationen und die Schemaprüfung beim Start
automatisch aus. Die SQLite-Datei liegt auf dem Host unter
`data/leistungsbilanz.db` und bleibt beim Stoppen erhalten.
Das Laufzeit-Image läuft als Benutzer `node` (UID 1000), nicht als root. Ein
`data/`-Verzeichnis, das ein früherer root-Container angelegt hat, braucht
deshalb einmalig `sudo chown -R 1000:1000 data` auf dem Host.
```powershell
docker compose ps
docker compose logs --follow
docker compose down
```
`compose.yaml` startet den Produktionsstand: gebautes `dist/` und `next start`,
ohne Quellcode-Mounts und ohne Datei-Watcher, mit Speichergrenzen und ohne
root-Rechte im Container. Details stehen in
[Deployment und Betrieb](docs/deployment.md).
Für die Entwicklung mit Hot Reload gibt es einen eigenen Stack mit
Quellcode-Mounts und Watchern:
```powershell
docker compose -f compose.dev.yaml up --build --detach
docker compose -f compose.dev.yaml logs --follow
docker compose -f compose.dev.yaml down
```
Die Watcher darin laufen im Polling-Modus, weil Bind-Mounts unter Windows und
macOS keine inotify-Events durchreichen. Das kostet dauerhaft CPU, auch wenn
niemand die Anwendung benutzt — deshalb gehört dieser Stack nicht auf einen
Server.
## Direkte lokale Entwicklung
Voraussetzungen:
- Node.js 24
- npm
```powershell
npm ci
npm run db:migrate
npm run db:verify:circuit-schema
npm run dev:api
```
In einem zweiten Terminal:
```powershell
npm run dev:web
```
Frontend und API laufen anschließend auf denselben Ports wie im Docker-Setup.
## Qualitätssicherung
```powershell
npm test
npm run build:api
npm run build:web
npm run typecheck:scripts
npx tsc --noEmit -p tsconfig.next.json
```
Wichtige Datenbankbefehle:
```powershell
npm run db:backup
npm run db:migrate
npm run db:verify:circuit-schema
npm run db:generate
```
`db:backup` verwendet die SQLite-Online-Backup-API und prüft das Ergebnis auf
Integrität und Fremdschlüsselverletzungen. Migration `0000` ist die
Release-Baseline für eine leere Datenbank; nachfolgende nummerierte Migrationen
entwickeln diese Baseline additiv weiter. Datenbanken aus Entwicklungsständen
vor der Baseline werden nicht unterstützt.
## Dokumentation
- [Dokumentationsübersicht](docs/README.md)
- [Aktuelle Architektur](docs/current-architecture.md)
- [Editor-Interaktionen](docs/circuit-list-editor-interactions.md)
- [API des Stromkreislisten-Editors](docs/circuit-list-editor-api.md)
- [Entwicklungs- und Contributor-Workflow](docs/development-workflow.md)
- [Bekannte Einschränkungen](docs/circuit-list-editor-known-limitations.md)
- [Roadmap](docs/spec/07-implementation-phases-todo.md)
Für LLM-gestützte Änderungen enthält [AGENTS.md](AGENTS.md) die verbindlichen
Domänenregeln, unterstützten Einstiegspunkte und Architekturgrenzen.