diff options
Diffstat (limited to 'makima/src/server/mod.rs')
| -rw-r--r-- | makima/src/server/mod.rs | 88 |
1 files changed, 88 insertions, 0 deletions
diff --git a/makima/src/server/mod.rs b/makima/src/server/mod.rs new file mode 100644 index 0000000..c33eeef --- /dev/null +++ b/makima/src/server/mod.rs @@ -0,0 +1,88 @@ +//! Web server module for the makima audio API. + +pub mod handlers; +pub mod messages; +pub mod openapi; +pub mod state; + +use axum::{ + routing::{get, post}, + Router, +}; +use tower_http::cors::{Any, CorsLayer}; +use tower_http::trace::TraceLayer; +use utoipa::OpenApi; +use utoipa_swagger_ui::SwaggerUi; + +use crate::server::handlers::{listen, tts}; +use crate::server::openapi::ApiDoc; +use crate::server::state::SharedState; + +/// Create the axum Router with all routes configured. +pub fn make_router(state: SharedState) -> Router { + // API v1 routes + let api_v1 = Router::new() + .route("/listen", get(listen::websocket_handler)) + .route("/tts/synthesize", post(tts::synthesize_handler)) + .with_state(state); + + let swagger = SwaggerUi::new("/swagger-ui") + .url("/api-docs/openapi.json", ApiDoc::openapi()); + + Router::new() + .nest("/api/v1", api_v1) + .merge(swagger) + .layer( + CorsLayer::new() + .allow_origin(Any) + .allow_methods(Any) + .allow_headers(Any), + ) + .layer(TraceLayer::new_for_http()) +} + +/// Run the HTTP server with graceful shutdown support. +/// +/// # Arguments +/// * `state` - Shared application state containing ML models +/// * `addr` - Address to bind to (e.g., "0.0.0.0:8080") +pub async fn run_server(state: SharedState, addr: &str) -> anyhow::Result<()> { + let app = make_router(state); + let listener = tokio::net::TcpListener::bind(addr).await?; + + tracing::info!("Server listening on {}", addr); + tracing::info!("Swagger UI available at http://{}/swagger-ui", addr); + + axum::serve(listener, app) + .with_graceful_shutdown(shutdown_signal()) + .await?; + + Ok(()) +} + +/// Wait for shutdown signals (Ctrl+C or SIGTERM). +async fn shutdown_signal() { + let ctrl_c = async { + tokio::signal::ctrl_c() + .await + .expect("Failed to install Ctrl+C handler"); + }; + + #[cfg(unix)] + let terminate = async { + tokio::signal::unix::signal(tokio::signal::unix::SignalKind::terminate()) + .expect("Failed to install signal handler") + .recv() + .await; + }; + + #[cfg(not(unix))] + let terminate = std::future::pending::<()>(); + + tokio::select! { + _ = ctrl_c => {}, + _ = terminate => {}, + } + + tracing::info!("Shutdown signal received, starting graceful shutdown"); +} |
