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
119 lines
4.9 KiB
Rust
119 lines
4.9 KiB
Rust
//! OpenAPI-Aggregation: zieht alle annotierten Handler und Schemata in
|
|
//! ein einziges Dokument zusammen, das der `/openapi.json`-Endpoint
|
|
//! serviert und Swagger-UI darstellt.
|
|
//!
|
|
//! Neue Endpoints werden hier in `paths(...)` registriert, neue Schemata
|
|
//! in `components(schemas(...))`. Die Annotation am Handler (via
|
|
//! `#[utoipa::path(...)]`) liefert die eigentliche Beschreibung.
|
|
|
|
use utoipa::Modify;
|
|
use utoipa::OpenApi;
|
|
use utoipa::openapi::security::{HttpAuthScheme, HttpBuilder, SecurityScheme};
|
|
|
|
#[derive(OpenApi)]
|
|
#[openapi(
|
|
info(
|
|
title = "Holzleitner Backend API",
|
|
version = "0.1.0",
|
|
description = "Backend für die Holzleitner-Lieferservice-App — Tour, Beladung, Ausführung."
|
|
),
|
|
paths(
|
|
crate::routes::health::health,
|
|
crate::routes::accounts::get_account,
|
|
crate::routes::tours::list_my_tours_today,
|
|
crate::routes::tours::get_tour,
|
|
crate::routes::tours::set_delivery_order,
|
|
crate::routes::tours::sync_tour,
|
|
crate::routes::scans::apply_scans,
|
|
crate::routes::deliveries::hold,
|
|
crate::routes::deliveries::resume,
|
|
crate::routes::deliveries::cancel,
|
|
crate::routes::deliveries::complete,
|
|
crate::routes::deliveries::create_note,
|
|
crate::routes::deliveries::assign_car,
|
|
crate::routes::cars::list_my_cars,
|
|
crate::routes::cars::create_my_car,
|
|
crate::routes::cars::update_my_car,
|
|
),
|
|
components(
|
|
schemas(
|
|
holzleitner_domain::Account,
|
|
holzleitner_domain::Address,
|
|
holzleitner_domain::Article,
|
|
holzleitner_domain::AuditAction,
|
|
holzleitner_domain::Car,
|
|
holzleitner_domain::Customer,
|
|
holzleitner_domain::CustomerContact,
|
|
holzleitner_domain::Delivery,
|
|
holzleitner_domain::DeliveryItem,
|
|
holzleitner_domain::DeliveryNote,
|
|
holzleitner_domain::DeliveryState,
|
|
holzleitner_domain::ScanState,
|
|
holzleitner_domain::ScanStatus,
|
|
holzleitner_domain::Tour,
|
|
holzleitner_domain::Warehouse,
|
|
holzleitner_application::dto::TourDetails,
|
|
holzleitner_application::dto::DeliveryWithItems,
|
|
holzleitner_application::dto::TourSummary,
|
|
holzleitner_application::dto::SyncTourRequest,
|
|
holzleitner_application::dto::SyncDelivery,
|
|
holzleitner_application::dto::SyncDeliveryItem,
|
|
holzleitner_application::dto::SetDeliveryOrderRequest,
|
|
holzleitner_application::dto::SetDeliveryOrderResponse,
|
|
holzleitner_application::dto::DeliveryOrderEntry,
|
|
holzleitner_application::dto::ApplyScansRequest,
|
|
holzleitner_application::dto::ApplyScansResponse,
|
|
holzleitner_application::dto::ScanEvent,
|
|
holzleitner_application::dto::ScanResult,
|
|
holzleitner_application::dto::ScanResultStatus,
|
|
holzleitner_application::dto::HoldDeliveryRequest,
|
|
holzleitner_application::dto::CancelDeliveryRequest,
|
|
holzleitner_application::dto::DeliveryResponse,
|
|
holzleitner_application::dto::CreateDeliveryNoteRequest,
|
|
holzleitner_application::dto::DeliveryNoteResponse,
|
|
holzleitner_application::dto::CreateCarRequest,
|
|
holzleitner_application::dto::UpdateCarRequest,
|
|
holzleitner_application::dto::CarResponse,
|
|
holzleitner_application::dto::CarsList,
|
|
holzleitner_application::dto::AssignCarRequest,
|
|
crate::routes::tours::TourSummaryList,
|
|
crate::routes::tours::SyncTourResponse,
|
|
)
|
|
),
|
|
modifiers(&SecurityAddon),
|
|
tags(
|
|
(name = "health", description = "Health- und Status-Endpoints"),
|
|
(name = "accounts", description = "Account-Stammdaten"),
|
|
(name = "tours", description = "Touren der Fahrer"),
|
|
(name = "sync", description = "ERP-Sync-Endpunkte"),
|
|
(name = "scans", description = "Scan-Events (Beladung & Auslieferung)"),
|
|
(name = "deliveries", description = "Delivery-Lifecycle (hold / resume / cancel / complete)"),
|
|
(name = "cars", description = "Fahrzeug-Stammdaten pro Fahrer"),
|
|
),
|
|
security(
|
|
("bearer_auth" = [])
|
|
)
|
|
)]
|
|
pub struct ApiDoc;
|
|
|
|
/// Hängt das `bearer_auth`-Security-Scheme nachträglich in die Spec ein —
|
|
/// das geht nur über einen `Modify`-Hook, weil derive-Macros das nicht
|
|
/// direkt erlauben.
|
|
struct SecurityAddon;
|
|
|
|
impl Modify for SecurityAddon {
|
|
fn modify(&self, openapi: &mut utoipa::openapi::OpenApi) {
|
|
if let Some(components) = openapi.components.as_mut() {
|
|
components.add_security_scheme(
|
|
"bearer_auth",
|
|
SecurityScheme::Http(
|
|
HttpBuilder::new()
|
|
.scheme(HttpAuthScheme::Bearer)
|
|
.bearer_format("JWT")
|
|
.build(),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
}
|