//! # Zesdex REST API — Axum HTTP server //! //! Provides RESTful endpoints for the Zesdex application, enabling //! web clients, mobile apps, and third-party integrations. //! //! ## Architecture //! //! ```text //! src/ //! ├── lib.rs — Module declarations, re-exports, router builder //! ├── state.rs — ApiState with concrete service implementations //! ├── error.rs — ApiError enum + IntoResponse //! ├── dto/ — Request/response DTOs (serde) //! ├── handlers/ — Axum route handlers //! └── middleware/ — Tower layers (JWT auth, etc.) //! ``` //! //! ## Flow //! //! 1. `build_router()` constructs an Axum `Router` with all routes nested. //! 2. Each handler receives `State>` or direct extractors. //! 3. Handlers delegate to application-layer service implementations. //! 4. Domain/infrastructure errors are mapped to `ApiError` → HTTP status codes. pub mod dto; pub mod error; pub mod handlers; pub mod middleware; pub mod state; pub use error::ApiError; pub use state::ApiState; use std::sync::Arc; use axum::Router; use tower_http::cors::CorsLayer; /// Build the API router with all routes registered. /// /// Flow: create CORS layer → build sub-routers for each resource → nest /// them under `/api/v1` → attach shared state → return. /// /// ## Arguments /// * `state` — shared application state (wrapped in `Arc` for clone-free sharing) /// /// ## Example /// ```ignore /// let state = ApiState::new("/path/to/data"); /// let app = build_router(state); /// let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); /// axum::serve(listener, app).await.unwrap(); /// ``` pub fn build_router(state: ApiState) -> Router { let shared_state: Arc = Arc::new(state); // CORS layer — permissive for local daemon / development use let cors = CorsLayer::permissive(); // Combine all sub-routers under a versioned prefix Router::new() .nest("/api/v1", api_v1_router()) .layer(cors) .with_state(shared_state) } /// Version 1 API sub-router. /// /// Groups all resource routes under `/api/v1/*`. fn api_v1_router() -> Router> { use handlers::{auth, chat, conversations, health, sessions}; // Sessions router combines session CRUD + nested conversations let sessions_router = Router::new() .route("/", axum::routing::get(sessions::list_sessions_handler)) .route("/", axum::routing::post(sessions::create_session_handler)) .route("/{id}", axum::routing::delete(sessions::delete_session_handler)) // Conversations are sub-resources of sessions .route( "/{id}/conversations", axum::routing::get(conversations::get_conversation_handler), ) .route( "/{id}/conversations", axum::routing::post(conversations::add_message_handler), ) .route( "/{id}/conversations/{cid}", axum::routing::delete(conversations::delete_message_handler), ); Router::new() .route("/health", axum::routing::get(health::health)) .nest("/auth", auth::router()) .nest("/sessions", sessions_router) .nest("/chat", chat::router()) } // Re-export commonly used types at the crate root for ergonomic access. pub use axum::http::StatusCode;