This commit is contained in:
Bastian Wagner
2026-07-16 09:49:22 +02:00
commit 543e8273a7
157 changed files with 22761 additions and 0 deletions

120
docs/architecture.md Normal file
View File

@@ -0,0 +1,120 @@
# Architektur
Das Repository ist ein modularer Monolith mit getrennten Workspaces fuer
Frontend, Backend und API-Client. Ausgeliefert wird eine einzelne Node.js-App,
die die gebaute Angular-Anwendung statisch mitliefert.
## Workspaces
```text
apps/frontend
apps/backend
packages/api-client
```
Es wird bewusst kein Nx eingesetzt. Workspace-Grenzen bleiben durch npm-Scripts,
TypeScript-Projekte und klare Importpfade nachvollziehbar.
## Backend
Das Backend liegt in `apps/backend` und basiert auf NestJS. Es ist nach Features
geschnitten:
- `auth`: OIDC Login, Callback, Logout und Authentifizierung
- `sessions`: serverseitige Sessions, CSRF-Token und Session-Listen
- `roles`: Code-definierte Permissions und rollenbasierte Rechteverwaltung
- `users`: lokale Benutzerprofile und Einstellungen
- `items`: Beispiel-Fachmodul
- `audit`: Audit-Log fuer administrative Nachvollziehbarkeit
- `health`: Liveness- und Readiness-Endpunkte
- `database`: TypeORM-Konfiguration, Entities und Migrationen
- `common`: Fehlerformat, Request Context, Validierung und HTTP-Helfer
Controller enthalten keine Datenbanklogik. Sie validieren den HTTP-Rand,
deklarieren Permissions und delegieren an Services. Services enthalten
Fachlogik. Datenbankzugriffe laufen ueber Repository-Klassen oder klar benannte
Persistence-Services.
Globale Backend-Bausteine:
- `ApiExceptionFilter` normalisiert Fehlerantworten.
- `ThrottlerGuard` setzt Rate Limits.
- `CsrfGuard` schuetzt schreibende Requests.
- `PermissionsGuard` prueft Rollen und Permissions.
- `RequestIdMiddleware` setzt Request-Korrelation.
- `helmet` setzt Security Header.
- `pino-http` loggt mit Redaction fuer Cookies, Tokens und Secrets.
## Frontend
Das Frontend liegt in `apps/frontend` und nutzt Angular Standalone Components.
Der Shell-Aufbau steckt in `src/app/layout/app-shell.ts`. Feature-Seiten liegen
unter `src/app/features`.
Design- und Implementierungsregeln:
- mobile-first HTML und SCSS
- keine UI-Komponentenbibliothek
- Signals fuer lokalen UI-State
- RxJS fuer HTTP- und API-Flows
- Angular Permissions nur fuer Darstellung und Navigation, nicht als
Sicherheitsgrenze
Der `permissionGuard` verhindert unpassende Navigation im Frontend. Die
verbindliche Autorisierung findet immer im Backend statt.
## API-Client
`packages/api-client` enthaelt den vom Frontend genutzten Angular-Service und die
DTO-Typen. Der Code ist als generiert markiert. Er wird mit folgendem Befehl neu
geschrieben:
```bash
npm run api:generate
```
Der Produktionsbuild ruft diesen Schritt vor Backend- und Frontend-Builds auf.
Bei API-Aenderungen muss der Generator synchron zur Backend-API angepasst werden.
## Runtime-Aufbau
Im Produktionsbuild entsteht:
```text
apps/backend/dist/main.js
apps/backend/dist/public/index.html
apps/backend/dist/public/assets...
```
NestJS liefert:
- `/api/*` als JSON API
- `/api/docs` als Swagger UI, wenn aktiviert
- `/health/live` fuer Liveness
- `/health/ready` fuer Readiness inklusive Datenbank- und Migrationspruefung
- alle anderen Pfade als Angular SPA Fallback
## Datenmodell und Migrationen
TypeORM laeuft mit:
- `synchronize: false`
- `migrationsRun: false`
- expliziten Migrationen unter `apps/backend/src/database/migrations`
Der normale App-Start fuehrt keine Migrationen aus. Stattdessen prueft
`MigrationHealthService`, ob Migrationen fehlen. Dadurch wird verhindert, dass
ein App-Rollout unbemerkt ein Schema veraendert.
## Autorisierung
Permissions werden in `apps/backend/src/roles/permissions.ts` definiert. Rollen
sind Datenbankdaten, Benutzer erhalten Permissions nur ueber Rollen.
Systemrollen:
- `admin`: alle Permissions
- `user`: Basisrechte fuer Items-Lesen und eigene Sessions
Der erste erfolgreich angemeldete Benutzer wird automatisch Admin. Danach
erhalten neue Benutzer initial die Rolle `user`.

111
docs/configuration.md Normal file
View File

@@ -0,0 +1,111 @@
# Konfiguration
Die Runtime-Konfiguration wird ueber Umgebungsvariablen geladen und mit Zod in
`apps/backend/src/config/env.ts` validiert. `.env.example` ist die Referenz fuer
lokale Entwicklung und Deployment-Vorlagen.
## Allgemein
| Variable | Bedeutung |
| ------------------- | -------------------------------------------------------------------------- |
| `NODE_ENV` | `development`, `test` oder `production` |
| `PORT` | HTTP-Port des Backends |
| `APP_BASE_URL` | Externe Basis-URL der App; Grundlage fuer OIDC Callback |
| `FRONTEND_BASE_URL` | Frontend-Origin fuer Redirects und CORS; faellt auf `APP_BASE_URL` zurueck |
| `TRUST_PROXY` | Express Trust Proxy, wenn hinter Reverse Proxy |
## Datenbank
| Variable | Bedeutung |
| ------------------- | --------------------------- |
| `DATABASE_HOST` | MySQL Host |
| `DATABASE_PORT` | MySQL Port, Standard `3306` |
| `DATABASE_NAME` | Datenbankname |
| `DATABASE_USER` | Datenbankbenutzer |
| `DATABASE_PASSWORD` | Datenbankpasswort |
| `DATABASE_SSL` | TLS fuer MySQL-Verbindung |
Die Datenbank muss MySQL 8 und `utf8mb4` unterstuetzen. Migrationen werden
separat ausgefuehrt.
## OIDC
| Variable | Bedeutung |
| ------------------------- | ---------------------------------------------------- |
| `OIDC_ISSUER` | Exakter Issuer des Providers |
| `OIDC_CLIENT_ID` | Client ID |
| `OIDC_CLIENT_SECRET` | Client Secret |
| `OIDC_SCOPES` | Scopes, typischerweise `openid profile email` |
| `OIDC_ALLOWED_ALGORITHMS` | Erlaubte ID-Token-Signaturalgorithmen, z. B. `RS256` |
| `OIDC_HTTP_TIMEOUT_MS` | Timeout fuer IdP-HTTP-Requests |
Der Callback ist immer:
```text
<APP_BASE_URL>/api/auth/callback
```
## Sessions und CSRF
| Variable | Bedeutung |
| ---------------------------------- | --------------------------------------------------------- |
| `SESSION_COOKIE_NAME` | Name des signierten Session-Cookies |
| `SESSION_IDLE_TIMEOUT_SECONDS` | Inaktivitaetsablauf |
| `SESSION_ABSOLUTE_TIMEOUT_SECONDS` | Absoluter Ablauf unabhaengig von Aktivitaet |
| `SESSION_SECRET` | Secret fuer Cookie-Signatur, mindestens 32 Zeichen |
| `SESSION_ENCRYPTION_KEY` | Secret fuer Token-Verschluesselung, mindestens 32 Zeichen |
| `CSRF_HEADER_NAME` | Header fuer CSRF-Token, Standard `X-CSRF-Token` |
In Produktion duerfen diese Secrets keine Beispielwerte enthalten. Die
Konfiguration bricht bei offensichtlichen Platzhaltern ab.
## CORS, Logging, Swagger und Rate Limits
| Variable | Bedeutung |
| ------------------------------------- | ---------------------------------------- |
| `CORS_ORIGINS` | Kommagetrennte erlaubte Origins |
| `LOG_LEVEL` | Pino Log Level |
| `SWAGGER_ENABLED` | Swagger UI und JSON aktivieren |
| `RATE_LIMIT_WINDOW_SECONDS` | Globales IP-Zeitfenster |
| `RATE_LIMIT_MAX_REQUESTS` | Globale Requests pro IP und Zeitfenster |
| `RATE_LIMIT_SENSITIVE_WINDOW_SECONDS` | Zeitfenster fuer sensible Endpunkte |
| `RATE_LIMIT_SENSITIVE_MAX_REQUESTS` | Requests pro IP auf sensiblen Endpunkten |
`SWAGGER_ENABLED` sollte in Produktion nur bewusst aktiviert werden. Das
eingebaute Rate Limiting ist In-Memory, pro Prozess und nicht fuer horizontale
Skalierung koordiniert. Es ist fuer genau eine Containerinstanz ausgelegt.
## Beispiel fuer Produktion
```env
NODE_ENV=production
PORT=3000
APP_BASE_URL=https://business.example.com
FRONTEND_BASE_URL=https://business.example.com
TRUST_PROXY=true
DATABASE_HOST=mysql.internal
DATABASE_PORT=3306
DATABASE_NAME=business_app
DATABASE_USER=business_app
DATABASE_PASSWORD=<secret>
DATABASE_SSL=true
OIDC_ISSUER=https://idp.example.com/realms/internal
OIDC_CLIENT_ID=business-app
OIDC_CLIENT_SECRET=<secret>
OIDC_SCOPES=openid profile email
OIDC_ALLOWED_ALGORITHMS=RS256
OIDC_HTTP_TIMEOUT_MS=5000
SESSION_COOKIE_NAME=app_session
SESSION_IDLE_TIMEOUT_SECONDS=28800
SESSION_ABSOLUTE_TIMEOUT_SECONDS=604800
SESSION_SECRET=<secret>
SESSION_ENCRYPTION_KEY=<secret>
CORS_ORIGINS=https://business.example.com
CSRF_HEADER_NAME=X-CSRF-Token
LOG_LEVEL=info
SWAGGER_ENABLED=false
RATE_LIMIT_WINDOW_SECONDS=60
RATE_LIMIT_MAX_REQUESTS=300
RATE_LIMIT_SENSITIVE_WINDOW_SECONDS=60
RATE_LIMIT_SENSITIVE_MAX_REQUESTS=10
```

139
docs/deployment.md Normal file
View File

@@ -0,0 +1,139 @@
# Deployment und Betrieb
Die Anwendung wird als ein Docker-Image ausgeliefert. Das Image enthaelt das
gebaute NestJS-Backend und das gebaute Angular-Frontend.
## Release-Build
Vor jedem Release:
```bash
npm ci
npm run lint
npm run format:check
npm run typecheck
npm test
npm run build
docker build -t registry.example.com/business-app:<tag> .
```
`npm run build` fuehrt aus:
1. API-Client generieren
2. API-Client bauen
3. Backend bauen
4. Frontend bauen
5. Frontend nach `apps/backend/dist/public` kopieren
Das Dockerfile baut erneut im Container und erzeugt anschliessend ein schlankes
Runtime-Image mit Non-Root-User.
## Runtime-Konfiguration
Konfiguration erfolgt ueber Umgebungsvariablen. Nutze `.env.example` nur als
Vorlage. Production-Secrets gehoeren in Secret Management, CI/CD Variables oder
eine geschuetzte Server-Konfiguration.
Wichtige Produktionswerte:
- `NODE_ENV=production`
- `APP_BASE_URL=https://app.example.com`
- `FRONTEND_BASE_URL=https://app.example.com`
- `TRUST_PROXY=true`, wenn hinter Reverse Proxy
- `DATABASE_SSL=true`, wenn die Datenbank TLS erzwingt
- `SWAGGER_ENABLED=false`, ausser bewusst anders entschieden
- starke Werte fuer `SESSION_SECRET`, `SESSION_ENCRYPTION_KEY`, `OIDC_CLIENT_SECRET`
## Migrationen
Migrationen werden nicht beim App-Start ausgefuehrt. Fuehre sie vor dem neuen
App-Container aus, mit demselben Image und derselben Konfiguration:
```bash
docker run --rm --env-file .env registry.example.com/business-app:<tag> node apps/backend/dist/database/run-migrations.js
```
Danach den App-Container starten oder aktualisieren. Wenn Migrationen fehlen,
schlaegt der App-Start fehl beziehungsweise `/health/ready` bleibt nicht bereit.
## Container starten
Ein einfaches Compose-Beispiel liegt in `compose.yml`:
```bash
docker compose up -d
```
Der Container:
- laeuft als Non-Root-User
- verwendet ein read-only Root Filesystem
- nutzt `/tmp` als tmpfs
- dropt Linux Capabilities
- prueft `/health/ready` als Healthcheck
Compose baut oder nutzt das Image `internal-business-app:latest`. Fuer echte
Deployments sollte ein versioniertes Registry-Image verwendet werden.
## Reverse Proxy
Nginx oder ein Load Balancer sollte TLS terminieren und alle Routen an den
App-Container weiterleiten. Die Angular-Dateien werden nicht separat durch Nginx
ausgeliefert. Ein Beispiel liegt in `docs/nginx-example.conf`.
Wichtig:
- WebSocket-Konfiguration ist fuer dieses Boilerplate nicht erforderlich.
- `X-Forwarded-Proto` und `X-Forwarded-For` sollten gesetzt werden.
- `APP_BASE_URL` muss zur externen HTTPS-URL passen.
- Wenn `TRUST_PROXY=true` gesetzt ist, muss der Proxy vertrauenswuerdig sein.
## Healthchecks
- `/health/live`: Prozess lebt.
- `/health/ready`: Datenbank erreichbar, Migration-Check initialisiert und keine
offenen Migrationen.
Readiness ist der richtige Check fuer Rolling Deployments und Container
Orchestrierung.
## TeamCity-Beispiel
1. Checkout
2. `npm ci`
3. `npm run lint`
4. `npm run format:check`
5. `npm run typecheck`
6. `npm test`
7. `npm run build`
8. `docker build -t <registry>/<image>:<build-number> .`
9. `docker push <registry>/<image>:<build-number>`
10. Migration-Job mit neuem Image ausfuehren
11. App-Service auf neues Image aktualisieren
12. `/health/ready` pruefen
## Rollback
Ein Rollback ist nur dann einfach, wenn die Datenbankmigrationen rueckwaerts
kompatibel geplant wurden. Fuer riskante Schema-Aenderungen sollte das Expand-
Contract-Muster verwendet werden:
1. Neue Spalten oder Tabellen hinzufuegen, alte weiter bedienen.
2. Anwendung umstellen.
3. Daten migrieren.
4. Alte Spalten oder Pfade in einem spaeteren Release entfernen.
## Betrieb
Beobachte mindestens:
- HTTP-Fehlerraten und Latenzen
- Healthcheck-Status
- Datenbankverbindungen
- Login-Fehler vom OIDC Provider
- `MIGRATION_MISSING`
- Rate-Limit-Treffer, insbesondere `RATE_LIMIT_EXCEEDED` auf Login- und Admin-Endpunkten
- Audit-Log fuer administrative Aktionen
Logs sollten zentral gesammelt werden. Request IDs helfen dabei, Frontend-Fehler,
Backend-Logs und Audit-Eintraege zusammenzufuehren.

119
docs/development.md Normal file
View File

@@ -0,0 +1,119 @@
# Entwicklung
Diese Datei beschreibt die normalen Entwicklungsablaeufe im Monorepo.
## Installation
```bash
npm ci
```
Abhaengigkeiten werden nur im Root installiert. Workspace-spezifische Befehle
werden ueber Root-Scripts oder `npm --workspace ...` ausgefuehrt.
## Lokaler Start
```bash
npm run dev
```
Alternativ getrennt:
```bash
npm run dev:frontend
npm run dev:backend
```
Das Frontend nutzt den Proxy in `apps/frontend/proxy.conf.json`, damit `/api`
lokal an das Backend weitergereicht wird.
## Feature-Workflow
1. Backend-DTOs und Entities modellieren.
2. Repository fuer Datenzugriff erstellen oder erweitern.
3. Service mit Fachlogik implementieren.
4. Controller nur als HTTP-Rand und Permission-Deklaration verwenden.
5. Permission in `roles/permissions.ts` ergaenzen, falls noetig.
6. Migration erzeugen und kontrollieren.
7. API-Client-Generator aktualisieren.
8. Frontend-Route, Navigation und Page bauen.
9. Tests fuer Verhalten schreiben.
10. Qualitaetsbefehle ausfuehren.
## Migrationen
Status anzeigen:
```bash
npm run migration:status
```
Migration erzeugen:
```bash
npm run migration:generate
```
Migration ausfuehren:
```bash
npm run migration:run
```
Generierte Migrationen muessen reviewed werden. Sie duerfen keine
versehentlichen Datenverluste, falsche Defaults oder umgebungsspezifische Namen
enthalten.
## API-Client
Nach API-Aenderungen:
```bash
npm run api:generate
```
Der Client unter `packages/api-client/src` ist generiert. Aendere stattdessen den
Generator oder ersetze ihn spaeter bewusst durch einen OpenAPI-basierten
Generator.
## Tests
```bash
npm test
npm run test:backend
npm run test:frontend
```
Tests sollen Verhalten pruefen, nicht nur Existenz. Backend-Tests fuer Services
sollen fachliche Regeln, Fehlerfaelle, Permissions und Datenbankinteraktionen
abdecken. Frontend-Tests sollen sichtbares Verhalten, Guards, Interaktionen und
API-Fehlerpfade abdecken.
Integrationstests gegen MySQL muessen eine separate Testdatenbank verwenden,
deren Name eindeutig `test` enthaelt. Tests duerfen niemals gegen
Produktionsdatenbanken laufen.
## Qualitaet vor Abschluss
```bash
npm run lint
npm run format:check
npm run typecheck
npm test
npm run build
docker build .
```
Diese Befehle sind die Mindestpruefung fuer Merge und Release.
## Fehlerformat
Backend-Fehler werden ueber `ApiExceptionFilter` in ein konsistentes Format
gebracht. Neue fachliche Fehler sollten `ApiError` und `ErrorCode` verwenden,
damit Frontend und Logs stabil bleiben.
## Logging
`pino-http` redigiert sensible Header und Token-Felder. Neue Logs duerfen keine
Secrets, Session-IDs, Cookies, Access Tokens, Refresh Tokens oder ID Tokens
enthalten.

88
docs/getting-started.md Normal file
View File

@@ -0,0 +1,88 @@
# Getting Started
Diese Anleitung bringt eine lokale Entwicklungsumgebung fuer das Boilerplate zum
Laufen. Sie setzt voraus, dass MySQL 8 und ein OIDC Provider bereits verfuegbar
sind.
## Voraussetzungen
- Node.js `24.18.0` aus `.nvmrc`
- npm `11.x`
- Docker fuer Image-Builds und spaetere Auslieferung
- MySQL 8 mit `utf8mb4`
- OIDC Client mit Authorization Code Flow, PKCE und Discovery Endpoint
Das Repository ist ein npm-Workspace-Monorepo. Abhaengigkeiten werden immer aus
dem Root installiert.
```bash
npm ci
cp .env.example .env
```
## Lokale Konfiguration
Trage in `.env` mindestens folgende Werte ein:
- `DATABASE_HOST`, `DATABASE_PORT`, `DATABASE_NAME`, `DATABASE_USER`, `DATABASE_PASSWORD`
- `OIDC_ISSUER`, `OIDC_CLIENT_ID`, `OIDC_CLIENT_SECRET`
- `SESSION_SECRET` mit mindestens 32 zufaelligen Zeichen
- `SESSION_ENCRYPTION_KEY` mit mindestens 32 zufaelligen Zeichen
- `APP_BASE_URL=http://localhost:3000`
- `FRONTEND_BASE_URL=http://localhost:4200`
- `CORS_ORIGINS=http://localhost:4200,http://localhost:3000`
Der OIDC Provider muss als Redirect URI diese URL erlauben:
```text
http://localhost:3000/api/auth/callback
```
## Datenbank vorbereiten
Die Anwendung fuehrt Migrationen beim normalen Start nicht automatisch aus.
Fuehre sie bewusst aus:
```bash
npm run migration:status
npm run migration:run
```
Wenn Migrationen fehlen, verweigert das Backend den Start beziehungsweise
`/health/ready` bleibt nicht bereit.
## Entwicklung starten
```bash
npm run dev
```
Das startet:
- Frontend: `http://localhost:4200`
- Backend: `http://localhost:3000`
- API: `http://localhost:3000/api`
- Health: `http://localhost:3000/health/live` und `/health/ready`
- Swagger, falls `SWAGGER_ENABLED=true`: `http://localhost:3000/api/docs`
Das Frontend proxyt `/api` ueber `apps/frontend/proxy.conf.json` an das Backend.
Dadurch kann lokal mit Cookie-basierter Authentifizierung gearbeitet werden.
## Erster Login
1. Oeffne `http://localhost:4200`.
2. Melde dich ueber den OIDC Provider an.
3. Der erste lokal angelegte Benutzer erhaelt automatisch die Rollen `user` und
`admin`.
4. Weitere Benutzer erhalten initial die Rolle `user`.
Systemrollen und Permissions werden beim Login synchronisiert. Permissions sind
im Code definiert und werden nicht frei in der UI angelegt.
## Haefige Probleme
- `Ungueltige Konfiguration`: `.env` verletzt das Schema in `apps/backend/src/config/env.ts`.
- `MIGRATION_MISSING`: `npm run migration:run` ausfuehren.
- `UNAUTHORIZED`: Session abgelaufen, Benutzer deaktiviert oder OIDC-Konfiguration falsch.
- `CSRF_INVALID`: Schreibender Request ohne `X-CSRF-Token`; im Angular-Client erledigt das der Interceptor.
- OIDC Callback schlaegt fehl: Redirect URI, Issuer, Client Secret und erlaubte Algorithmen pruefen.

32
docs/nginx-example.conf Normal file
View File

@@ -0,0 +1,32 @@
server {
listen 443 ssl http2;
server_name app.example.com;
ssl_certificate /etc/letsencrypt/live/app.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/app.example.com/privkey.pem;
client_max_body_size 1m;
proxy_connect_timeout 5s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
add_header X-Content-Type-Options nosniff always;
add_header Referrer-Policy strict-origin-when-cross-origin always;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Request-ID $request_id;
}
}
server {
listen 80;
server_name app.example.com;
return 301 https://$host$request_uri;
}

117
docs/security.md Normal file
View File

@@ -0,0 +1,117 @@
# Security-Modell
Das Boilerplate trennt Browser, Backend und Identity Provider strikt. Der Browser
bekommt keine OIDC-Tokens. Das Backend ist fuer Authentifizierung,
Autorisierung, CSRF und Session-Verwaltung verbindlich.
## OIDC
Der Login nutzt Authorization Code Flow mit PKCE:
1. Browser ruft `/api/auth/login` auf.
2. Backend erzeugt `state`, `nonce`, `code_verifier` und leitet zum IdP weiter.
3. IdP ruft `/api/auth/callback` mit `code` und `state` auf.
4. Backend tauscht den Code gegen Tokens.
5. Backend validiert ID Token, Issuer, Audience, Nonce und Algorithmus.
6. Backend laedt optional UserInfo.
7. Backend legt oder aktualisiert den lokalen Benutzer.
8. Backend erzeugt eine serverseitige Session.
Erlaubte Signaturalgorithmen werden ueber `OIDC_ALLOWED_ALGORITHMS` gesetzt.
`none` ist explizit verboten.
## Sessions
Sessions liegen in MySQL. Das Session-Cookie enthaelt keine Tokens oder
Benutzerdaten, sondern nur eine signierte Session-ID.
Gespeichert werden unter anderem:
- verschluesselte OIDC-Tokens
- Hash des CSRF-Tokens
- Ablaufzeiten fuer Idle Timeout und absolutes Timeout
- User-Agent und IP fuer Anzeige und Audit-Kontext
- Revocation-Zeitpunkt
Die Token-Verschluesselung nutzt `SESSION_ENCRYPTION_KEY`. Cookie-Signaturen
nutzen `SESSION_SECRET`. Beide Werte muessen in Produktion echte Secrets sein.
## CSRF
Schreibende Methoden `POST`, `PUT`, `PATCH` und `DELETE` brauchen ein gueltiges
CSRF-Token. Das Frontend liest das CSRF-Cookie und sendet es als
`X-CSRF-Token`. Das Backend vergleicht nur den Hash gegen die Session.
Public Routes sind vom CSRF-Guard ausgenommen, sofern sie mit `@Public()`
markiert sind.
## Rollen und Permissions
Permissions werden in `apps/backend/src/roles/permissions.ts` definiert. Rollen
sind Datenbankdaten und referenzieren diese Permissions. Benutzer haben keine
direkten Permissions.
Backend-Controller schuetzen Endpunkte mit:
```ts
@RequirePermissions(Permission.ItemsRead)
```
Angular nutzt Permissions nur fuer Navigation und Darstellung. Eine versteckte
Schaltflaeche ist keine Sicherheitsgrenze.
## Benutzerstatus
Deaktivierte Benutzer werden trotz erfolgreichem IdP-Login abgewiesen. Aktive
Sessions deaktivierter Benutzer werden beim Session-Resolve unbrauchbar gemacht.
## Cookies und Proxy
In Produktion sollte TLS vor der Anwendung terminiert werden, zum Beispiel mit
Nginx. Setze `TRUST_PROXY=true`, wenn die Anwendung hinter einem vertrauenswuerdigen
Reverse Proxy laeuft und korrekte Forwarded Header benoetigt.
`APP_BASE_URL` muss die externe URL der Anwendung enthalten, weil daraus die OIDC
Callback URL gebaut wird.
## Logging und Redaction
Das Backend redigiert sensible Werte in Standard-HTTP-Logs:
- `Authorization`
- `Cookie`
- `X-CSRF-Token`
- `Set-Cookie`
- Access-, Refresh- und ID-Tokens
Neue Logs duerfen keine Secrets, Tokens, Session-IDs oder vollstaendige Cookies
enthalten. Fuer Auditing sollten fachliche IDs und Request IDs verwendet werden.
## Swagger
`SWAGGER_ENABLED` ist in Produktion standardmaessig `false`. Wenn Swagger in
Produktion aktiviert wird, sollte der Zugriff ueber Netzwerkregeln oder Reverse
Proxy zusaetzlich eingeschraenkt werden.
## Rate Limiting
Das konfigurierte Rate Limiting ist fuer eine einzelne Containerinstanz ohne
Redis ausgelegt. Die Zaehler liegen im Prozessspeicher. Bei horizontaler
Skalierung muss ein gemeinsamer Store eingefuehrt oder der Schutz an den Reverse
Proxy verlagert werden.
Es gibt zwei fruehe Ebenen:
- Globales IP-Limit fuer alle Requests, bevor Guards und Controller laufen.
- Strengeres IP-Limit fuer sensible Endpunkte, ebenfalls bevor Session-Resolve
und fachliche Datenbankzugriffe laufen.
Sensible Endpunkte werden mit `@SensitiveRateLimit()` markiert. Dazu gehoeren
Login, OIDC Callback, Logout, Session-Revoke sowie Benutzer- und
Rollenverwaltung. Bei Ueberschreitung antwortet die API mit HTTP 429,
`RATE_LIMIT_EXCEEDED`, `Retry-After` und `X-RateLimit-*` Headern.
Ein zusaetzliches Benutzer-Limit waere technisch moeglich, wuerde in diesem
Projekt aber erst nach dem serverseitigen Session-Resolve greifen. Fuer die
geforderte fruehe Lastreduktion ist deshalb das globale IP-Limit die verbindliche
Schutzschicht.

97
docs/using-as-template.md Normal file
View File

@@ -0,0 +1,97 @@
# Als Projektvorlage Verwenden
Dieses Boilerplate ist bewusst nah an einer realen internen Business-Anwendung.
Beim Start eines neuen Projekts solltest du zuerst Namen, Branding,
Runtime-Konfiguration und Demo-Fachlichkeit ersetzen, aber die Sicherheits- und
Betriebsentscheidungen beibehalten.
## Empfohlener Start
1. Repository kopieren oder als Template verwenden.
2. Paketnamen und Beschreibungen in `package.json`, `apps/*/package.json` und
`packages/api-client/package.json` anpassen.
3. UI-Name `Business App` in `apps/frontend/src/app/layout/app-shell.ts` ersetzen.
4. Swagger-Titel und Beschreibung in `apps/backend/src/main.ts` ersetzen.
5. Docker-Image-Namen in README, CI und Compose auf den Projektnamen aendern.
6. `.env.example` auf die Zielumgebung zuschneiden, aber keine Secrets eintragen.
7. Lizenz, Repository-URL, Ownership und Open-Source-Metadaten bewusst setzen.
## Was du behalten solltest
- Backend-for-Frontend Authentifizierung: OIDC-Tokens bleiben im Backend.
- Server-seitige Sessions in MySQL.
- CSRF-Schutz fuer alle schreibenden Requests.
- TypeORM-Migrationen statt `synchronize`.
- Controller delegieren an Services; Datenbankzugriff liegt in Repositories oder
klar benannten Persistence-Services.
- Permissions sind Code, Rollen sind Daten.
- Keine UI-Library als versteckte Projektbindung.
- Striktes TypeScript ohne `any`.
## Demo-Fachlichkeit entfernen oder ersetzen
Das Modul `items` ist ein Beispiel fuer ein einfaches fachliches CRUD-Modul mit
optimistischer Versionierung und Soft Delete. Fuer ein neues Projekt gibt es zwei
saubere Optionen:
- Behalten und in das erste echte Fachmodul umbenennen.
- Entfernen und anhand der Struktur ein neues Modul erstellen.
Wenn du `items` entfernst, pruefe mindestens:
- Backend-Import in `apps/backend/src/app.module.ts`
- Entity-Export in `apps/backend/src/database/entities.ts`
- Initiale Migration und neue Migration fuer Schema-Aenderungen
- Permissions in `apps/backend/src/roles/permissions.ts`
- Routen in `apps/frontend/src/app/app.routes.ts`
- Navigation in `apps/frontend/src/app/layout/app-shell.ts`
- API-Client-Generator in `scripts/generate-api-client.mjs`
- Frontend-Pages und Tests unter `apps/frontend/src/app/features/items`
## Ein neues Feature anlegen
Backend:
1. Feature-Ordner unter `apps/backend/src/<feature>` anlegen.
2. Entity, DTOs, Repository, Service, Controller und Module erstellen.
3. Controller nur mit Services verdrahten.
4. Datenbankzugriff im Repository kapseln.
5. Permissions in `roles/permissions.ts` ergaenzen.
6. Controller-Methoden mit `@RequirePermissions(...)` schuetzen.
7. Entity in `database/entities.ts` aufnehmen.
8. Migration erzeugen und pruefen.
9. Verhaltenstests fuer Service oder Controller schreiben.
Frontend:
1. Page unter `apps/frontend/src/app/features/<feature>` erstellen.
2. Route in `app.routes.ts` anlegen.
3. Navigation in `app-shell.ts` ergaenzen.
4. Permission im Route-`data` und in der Navigation konsistent setzen.
5. API-Client ueber `packages/api-client` verwenden.
6. Verhaltenstests fuer relevante UI-Logik schreiben.
API-Client:
Aktuell schreibt `scripts/generate-api-client.mjs` den Client reproduzierbar aus
einer gepflegten Vorlage. Wenn die API waechst, muss der Generator erweitert oder
durch eine echte OpenAPI-Codegenerierung ersetzt werden. Der Client selbst ist
generierter Code und sollte nicht manuell editiert werden.
## Open-Source-Veroeffentlichung
Vor einer Veroeffentlichung als Open-Source-Projekt:
- `license` in `package.json` bewusst setzen.
- `LICENSE` und optional `NOTICE` hinzufuegen.
- Namen, Screenshots, Domaenen, Beispiel-Registry und interne Hinweise entfernen.
- Beispiel-Secrets in `.env.example` nur als Platzhalter belassen.
- Pruefen, ob alle Dependencies und generierten Artefakte lizenzkompatibel sind.
- Sicherheitsmodell in README und `docs/security.md` aktuell halten.
## Projektgrenzen
Dieses Boilerplate ist fuer eine einzelne deploybare Business-Anwendung gebaut.
Es ist kein Microservice-Framework, kein Nx-Workspace und keine Multi-Tenant
Plattform. Wenn ein Projekt diese Grenzen braucht, sollte die Architektur zuerst
bewusst erweitert werden, statt sie indirekt ueber Feature-Code einzuschleppen.