diff --git a/k-tv-backend/api/src/main.rs b/k-tv-backend/api/src/main.rs index 75f7cf3..9af9b6d 100644 --- a/k-tv-backend/api/src/main.rs +++ b/k-tv-backend/api/src/main.rs @@ -2,20 +2,14 @@ //! //! Configures and starts the HTTP server with JWT-based authentication. -use std::net::SocketAddr; use std::sync::Arc; -use axum::Router; -use axum::http::{HeaderName, HeaderValue}; -use tower_http::cors::{AllowHeaders, AllowMethods, AllowOrigin, CorsLayer}; use tracing::info; use domain::{ChannelService, IProviderRegistry, ScheduleEngineService, UserService}; use infra::factory::{build_activity_log_repository, build_channel_repository, build_provider_config_repository, build_schedule_repository, build_user_repository}; #[cfg(feature = "local-files")] use infra::factory::build_transcode_settings_repository; -use k_core::http::server::{ServerConfig, apply_standard_middleware}; -use tokio::net::TcpListener; mod config; mod database; @@ -28,6 +22,7 @@ mod log_layer; mod poller; mod routes; mod scheduler; +mod server; mod startup; mod state; mod telemetry; @@ -112,55 +107,11 @@ async fn main() -> anyhow::Result<()> { *state.transcode_manager.write().await = Some(tm); } - let server_config = ServerConfig { - cors_origins: config.cors_allowed_origins.clone(), - }; - startup::spawn_background_tasks( Arc::clone(&state.schedule_engine), bg_channel_repo, event_tx, ); - let app = Router::new() - .nest("/api/v1", routes::api_v1_router()) - .with_state(state); - - let app = apply_standard_middleware(app, &server_config); - - // Wrap with an outer CorsLayer that includes the custom password headers. - // Being outermost it handles OPTIONS preflights before k_core's inner layer. - let origins: Vec = config - .cors_allowed_origins - .iter() - .filter_map(|o| o.parse().ok()) - .collect(); - let cors = CorsLayer::new() - .allow_origin(AllowOrigin::list(origins)) - .allow_methods(AllowMethods::any()) - .allow_headers(AllowHeaders::list([ - axum::http::header::AUTHORIZATION, - axum::http::header::CONTENT_TYPE, - HeaderName::from_static("x-channel-password"), - HeaderName::from_static("x-block-password"), - ])); - let app = app.layer(cors); - - let addr: SocketAddr = format!("{}:{}", config.host, config.port).parse()?; - let listener = TcpListener::bind(addr).await?; - - tracing::info!("🚀 API server running at http://{}", addr); - tracing::info!("🔒 Authentication mode: JWT (Bearer token)"); - - #[cfg(feature = "auth-jwt")] - tracing::info!(" ✓ JWT auth enabled"); - - #[cfg(feature = "auth-oidc")] - tracing::info!(" ✓ OIDC integration enabled (stateless cookie state)"); - - tracing::info!("📝 API endpoints available at /api/v1/..."); - - axum::serve(listener, app).await?; - - Ok(()) + server::build_and_serve(state, &config).await } diff --git a/k-tv-backend/api/src/server.rs b/k-tv-backend/api/src/server.rs new file mode 100644 index 0000000..f780593 --- /dev/null +++ b/k-tv-backend/api/src/server.rs @@ -0,0 +1,59 @@ +use std::net::SocketAddr; + +use axum::Router; +use axum::http::{HeaderName, HeaderValue}; +use k_core::http::server::{ServerConfig, apply_standard_middleware}; +use tokio::net::TcpListener; +use tower_http::cors::{AllowHeaders, AllowMethods, AllowOrigin, CorsLayer}; + +use crate::config::Config; +use crate::routes; +use crate::state::AppState; + +pub async fn build_and_serve(state: AppState, config: &Config) -> anyhow::Result<()> { + let server_config = ServerConfig { + cors_origins: config.cors_allowed_origins.clone(), + }; + + let app = Router::new() + .nest("/api/v1", routes::api_v1_router()) + .with_state(state); + + let app = apply_standard_middleware(app, &server_config); + + // Wrap with an outer CorsLayer that includes the custom password headers. + // Being outermost it handles OPTIONS preflights before k_core's inner layer. + let origins: Vec = config + .cors_allowed_origins + .iter() + .filter_map(|o| o.parse().ok()) + .collect(); + let cors = CorsLayer::new() + .allow_origin(AllowOrigin::list(origins)) + .allow_methods(AllowMethods::any()) + .allow_headers(AllowHeaders::list([ + axum::http::header::AUTHORIZATION, + axum::http::header::CONTENT_TYPE, + HeaderName::from_static("x-channel-password"), + HeaderName::from_static("x-block-password"), + ])); + let app = app.layer(cors); + + let addr: SocketAddr = format!("{}:{}", config.host, config.port).parse()?; + let listener = TcpListener::bind(addr).await?; + + tracing::info!("🚀 API server running at http://{}", addr); + tracing::info!("🔒 Authentication mode: JWT (Bearer token)"); + + #[cfg(feature = "auth-jwt")] + tracing::info!(" ✓ JWT auth enabled"); + + #[cfg(feature = "auth-oidc")] + tracing::info!(" ✓ OIDC integration enabled (stateless cookie state)"); + + tracing::info!("📝 API endpoints available at /api/v1/..."); + + axum::serve(listener, app).await?; + + Ok(()) +}