Bringt das Backend vom initialen Skeleton auf den aktuellen Arbeitsstand (Clean Architecture: domain → application → infrastructure → api). Wesentliche Bereiche: - ERP-Anbindung (MSSQL-Pull der Touren, Import-Scheduler, Rückschreiben) - Lieferlebenszyklus: Scan/Hold/Cancel/Complete, Gutschriften, Notizen, Bild-Anhänge, Unterschriften, PDF-Lieferreport → DOCUframe - Stammdaten: Kunden, Artikel, Lager, Zahlungsarten, Services - Keycloak-JWT-Gate + Fahrer-Provisionierung via Admin-API - Admin-API-Key-Gate (X-Admin-Api-Key) für Maschinen-Endpunkte Jüngste Änderungen dieser Session: - Belegspezifische Kontaktdaten: alle ERP-Adressen (Beleg-/Liefer-/ Rechnungsadresse, Ansprechpartner, Kundenstamm) mit Telefon/Mobil/ E-Mail werden gesynct (Migration 0029, MSSQL-Query, TourDetails) - Konfiguration von .env (envy/dotenvy) auf config.toml (toml/serde) umgestellt; Vorlage config.example.toml, Pfad via HOLZLEITNER_CONFIG Nicht im Repo (per .gitignore): config.toml (Secrets), data/ (Laufzeit-/ Kundendaten), demo.mp4, .claude/, variocontrol-ai/. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Holzleitner-Backend
Rust-Backend für die Holzleitner-Lieferservice-App. Workspace mit Clean
Architecture: domain → application → infrastructure → api.
Schnellstart (lokale Entwicklung)
# 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:
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 wird bei jedem docker compose up aus
keycloak/import/realm-holzleitner.json frisch importiert. Wer in der
Admin-UI Änderungen macht, sollte sie in die JSON zurückspielen,
sonst sind sie beim nächsten docker compose down weg.
Token für Dev-Tests holen
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:
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:
# 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).