Files
Holzleitner---Backend--aktu…/README.md
Dennis Nemec f3e4c008ce Keycloak-Bootstrap-Skript (Realm-Import via Admin-REST-API)
tool/keycloak_bootstrap.sh importiert den Realm `holzleitner` idempotent über
die Keycloak-Admin-REST-API und konfiguriert ihn passend zum Backend (Client
holzleitner-app mit PKCE + Audience-Mapper holzleitner-api + personalnummer-
Claim, Rolle driver, Service-Account-Client holzleitner-provisioner, Test-User).

Löst den compose-`--import-realm`-Gotcha (greift nur beim ersten Start mit
leerem Volume): das Skript wendet die Realm-JSON jederzeit an.
- Startet Keycloak bei Bedarf (docker compose), wartet auf Erreichbarkeit,
  holt Admin-Token, legt den Realm via POST /admin/realms an.
- Default non-destruktiv (überspringt vorhandenen Realm); --reset löscht +
  importiert neu (Warnung: provisionierte Fahrer-Konten gehen verloren).
- Verifiziert die Clients und gibt die passenden [keycloak]-config.toml-Werte
  aus. Deps: bash + curl + python3 (kein jq).

Verifiziert: Import gegen Test-Realm → Token für testfahrer trägt
aud=holzleitner-api, personalnummer=1001 (int), Rolle driver.

README: Keycloak-Abschnitt aktualisiert (Import-Gotcha + Skript).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 18:20:18 +02:00

168 lines
6.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Holzleitner-Backend
Rust-Backend für die Holzleitner-Lieferservice-App. Workspace mit Clean
Architecture: `domain``application``infrastructure``api`.
## Schnellstart (lokale Entwicklung)
```bash
# 1) Postgres + Keycloak hochfahren
docker compose up -d
# Keycloak braucht ~30s bis "Listening on http://0.0.0.0:8080" im Log steht.
# 2) Konfiguration vorbereiten
cp config.example.toml config.toml
# Werte in config.toml anpassen (DB-URL, Keycloak-Issuer, ERP-Zugang, …).
# 3) Backend starten
cargo run -p holzleitner-api
```
Migrations laufen beim Start automatisch über `sqlx::migrate!`.
Smoke-Test danach:
```bash
curl http://127.0.0.1:3000/health
# → ok
curl http://127.0.0.1:3000/accounts/1001
# → {"personalnummer":1001,"name":"Müller Logistik GmbH","active":true}
```
## Keycloak (Dev)
| Wo | URL / Credentials |
|---|---|
| Admin-Console | http://localhost:8080/admin/ (admin / admin) |
| Realm | `holzleitner` |
| Client | `holzleitner-app` (public, PKCE) |
| Test-User | `testfahrer` / `test` · Personalnummer 1001 · Rolle `driver` |
| Audience im Access-Token | `holzleitner-api` |
Der Realm liegt in `keycloak/import/realm-holzleitner.json` und ist bereits
passend zum Backend konfiguriert (Client `holzleitner-app` mit PKCE +
Audience-Mapper `holzleitner-api` + `personalnummer`-Claim, Rolle `driver`,
Service-Account-Client `holzleitner-provisioner`, Test-User). Wer in der
Admin-UI Änderungen macht, sollte sie **in die JSON zurückspielen**.
**Wichtig:** Das compose-`--import-realm` greift nur beim **ersten** Start
(solange das `keycloak-data`-Volume leer ist) — spätere JSON-Änderungen landen
NICHT automatisch in Keycloak. Zum Anwenden/Bootstrappen:
```bash
# Importiert den Realm über die Admin-REST-API (idempotent, startet Keycloak
# bei Bedarf via docker compose). Tut nichts, falls der Realm schon existiert.
./tool/keycloak_bootstrap.sh
# Sauberer Neu-Import (LÖSCHT den Realm inkl. provisionierter Fahrer-Konten):
./tool/keycloak_bootstrap.sh --reset
```
Das Skript gibt am Ende die `[keycloak]`-Werte aus, die in die `config.toml`
gehören (issuer_url, audience, provisioner-secret …). Overrides via
`KC_URL` / `KC_ADMIN` / `KC_ADMIN_PASSWORD` / `REALM_FILE`.
### Token für Dev-Tests holen
```bash
curl -s -X POST \
http://localhost:8080/realms/holzleitner/protocol/openid-connect/token \
-d 'grant_type=password' \
-d 'client_id=holzleitner-app' \
-d 'username=testfahrer' \
-d 'password=test' | jq -r .access_token
```
Den Token in den `Authorization`-Header packen, sobald die JWT-Middleware
in der API-Schicht aktiv ist:
```bash
TOKEN=$(curl -s -X POST .../token -d ... | jq -r .access_token)
curl -H "Authorization: Bearer $TOKEN" http://127.0.0.1:3000/accounts/1001
```
## Crate-Layout
| Crate | Inhalt | Abhängigkeiten |
|---|---|---|
| `holzleitner-domain` | Reines Domänenmodell (serde + UUID + chrono) | — |
| `holzleitner-application` | Use Cases und Ports (Trait-Definitionen für Repository, AuthService) | `domain` |
| `holzleitner-infrastructure` | Konkrete Adapter (sqlx-Postgres, später Keycloak) | `domain`, `application` |
| `holzleitner-api` | Axum HTTP-Layer + Composition Root | alle |
## Konfiguration
Werte werden aus `config.toml` gelesen (Vorlage: `config.example.toml`),
gruppiert in TOML-Sections. Der Dateipfad ist über die Env-Variable
`HOLZLEITNER_CONFIG` überschreibbar (z. B. im Deployment); Default ist
`config.toml` im Arbeitsverzeichnis. Die echte `config.toml` enthält
Secrets und ist `.gitignore`-t.
| Section | Bereich | Pflicht? |
|---|---|---|
| `[server]` | Bind-Host/Port | ja |
| `[database]` | Postgres-URL, Pool-Größe | ja |
| `[keycloak]` | OIDC-Issuer, Audience, JWKS-Cache, Provisioning | ja |
| `[gsd]` | DOCUframe-REST (Datei-Upload) | ja |
| `[erp]` | ERPframe-MSSQL (Touren-Pull) | optional |
| `[import]` | Import-Scheduler (Cron, Offset) | optional |
| `[report]` / `[signature]` / `[attachment]` | Lokale Speicher-Pfade | optional |
| `[dev]` | `today_override`, `sync_enabled` (DEV-ONLY) | optional |
| `[admin]` | `api_key` für das `/admin`-Gate | optional |
| `[logging]` | Log-Filter (Default; `RUST_LOG`-Env hat Vorrang) | optional |
Unbekannte Schlüssel werden beim Laden abgewiesen (`deny_unknown_fields`),
sodass Tippfehler sofort als Startfehler auffallen.
## Migrations
Migrations liegen im Workspace-Root `migrations/`. Eingebettet via
`sqlx::migrate!()` — kein zusätzlicher Laufzeit-Dateizugriff nötig.
Neue Migration anlegen:
```bash
# Format: <epoch>_<beschreibung>.sql, z.B.:
touch migrations/0002_tour.sql
```
## Logging
`tracing` + `tracing-subscriber` mit `EnvFilter`. Der Default-Filter steht
in `config.toml` unter `[logging] filter`; die Env-Variable `RUST_LOG` hat
Vorrang (Ad-hoc-Debugging ohne Datei-Edit, z. B. `RUST_LOG=debug cargo run`).
Im **Konsolenmodus** geht alles auf `stderr`. Im **Windows-Dienst-Modus**
(keine Konsole) wird stattdessen in rollende Tages-Logdateien unter
`[logging] dir` (Default `logs/`, relativ zur EXE) geschrieben.
## Windows-Dienst
Das Backend kann als Windows-Dienst laufen (gleiches Muster wie der
Mail-Client). Beim Start versucht die EXE zuerst, sich beim Service Control
Manager zu registrieren; gelingt das nicht (interaktiver Start), fällt sie in
den Konsolenmodus zurück. `--console` (bzw. `-c`) erzwingt den Konsolenmodus.
Der Dienst setzt sein Arbeitsverzeichnis auf das EXE-Verzeichnis — `config.toml`,
`data/` und `logs/` müssen also **neben der EXE** liegen.
```powershell
# 1) Release-Build (auf dem Zielsystem oder per Cross-Build)
cargo build --release -p holzleitner-api
# 2) target\release\holzleitner-server.exe + config.toml + die *.ps1-Skripte
# in ein Installationsverzeichnis kopieren, z. B. C:\HolzleitnerBackend\
# 3) Als Administrator registrieren (Dienst "Holzleitner Backend")
.\install-service.ps1
# Optional mit Dienstkonto (DB-/Netzwerkzugriff):
.\install-service.ps1 -Credential (Get-Credential)
# Entfernen
.\uninstall-service.ps1
```
Der Dienst startet verzögert-automatisch (nach den System-/DB-Diensten) und
wird bei Absturz 3× im Abstand von 60 s neu gestartet. Interner Dienst-Name:
`HolzleitnerBackend`. Boot-Fehler **vor** der Logger-Initialisierung (z. B.
fehlende `config.toml`) landen in `holzleitner-backend-fatal.log` neben der EXE.