Files
Holzleitner---Backend--aktu…/crates/api/src/openapi.rs
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

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(),
),
);
}
}
}