Dennis Nemec 438040acce Initial: Rust-Backend mit Clean Architecture (domain/application/infrastructure/api)
Vier-Crate-Workspace mit:
- Domain: Account, Car, Tour, Delivery, DeliveryItem, DeliveryNote, Customer,
  Article, Warehouse, ScanState, AuditAction — alle mit serde + feature-gated
  utoipa::ToSchema.
- Application: Ports (TourRepository, DeliveryRepository, ScanRepository,
  DeliveryNoteRepository, CarRepository, AuthService) und Use Cases.
- Infrastructure: Postgres-Adapter via sqlx (PgTourRepository etc.) +
  Keycloak-AuthService mit JWKS-Cache + OIDC-Discovery.
- API: Axum 0.8, utoipa-OpenAPI + Swagger-UI, JWT-Bearer-Middleware,
  AuthenticatedUser-Extractor.

Endpoints:
- GET /me/tours/today, /tours/{id}, /accounts/{pn}, /me/cars, /health
- POST /sync/tour, /scans (bulk + idempotent via clientScanId),
  /deliveries/{id}/{hold,resume,cancel,complete,notes}, /me/cars
- PUT /tours/{id}/delivery-order, /deliveries/{id}/assigned-car, /me/cars/{id}
- PATCH /me/cars/{id}

Datenmodell:
- 6 Migrationen (accounts, tours/deliveries/items + Stammdaten,
  scan_audit mit clientScanId-UNIQUE, state_reason refactor,
  delivery_notes, cars + FKs nachziehen).
- Business-stabile Beleg-Keys (belegart_id, belegnummer) für ERP-Sync.
- Append-only scan_audit + embedded scan_state als doppelte Wahrheit.

Dev-Setup:
- docker-compose mit Postgres 17 + Keycloak 26
- Keycloak-Realm 'holzleitner' mit Public-Client (PKCE), Testfahrer
  (PN 1001) + Audience-/Personalnummer-Mapper
2026-05-14 22:28:31 +02:00

Holzleitner-Backend

Rust-Backend für die Holzleitner-Lieferservice-App. Workspace mit Clean Architecture: domainapplicationinfrastructureapi.

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) Env-Datei vorbereiten
cp .env.example .env

# 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 Umgebungsvariablen gelesen (siehe .env.example), gruppiert nach Prefix:

Prefix Bereich
SERVER_* Bind-Host/Port
DATABASE_* Postgres-URL, Pool-Größe
KEYCLOAK_* OIDC-Issuer, Audience, JWKS-Cache (greift erst in der Keycloak-Phase)

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. Default: holzleitner_api=info,tower_http=info. Override via RUST_LOG.

Description
No description provided
Readme 533 KiB
Languages
Rust 92.7%
Shell 6.7%
PowerShell 0.6%