presentation: HTTP server crate w/ handlers, routes, background tasks

Axum binary that wires all clean-arch crates together:
- AppState holds pre-built Deps structs (auth, channels, schedule, library, etc.)
- JWT extractors (CurrentUser, AdminUser, OptionalCurrentUser)
- Handlers delegate to application use cases, map to api-types DTOs
- Routes: auth, channels, schedule, library, admin, providers, config, iptv
- Background: auto-scheduler, broadcast poller, webhook consumer, library sync
- Factory builds everything from Config + DbPool
- SimpleProviderRegistry impl of IProviderRegistry trait
- NoopMediaProvider fallback
This commit is contained in:
2026-07-12 03:23:20 +02:00
parent afed5c01b4
commit 56d742a74c
25 changed files with 3186 additions and 5 deletions

View File

@@ -0,0 +1,76 @@
//! k-tv server entry point.
use std::net::SocketAddr;
use tower_http::cors::{Any, CorsLayer};
use tower_http::trace::TraceLayer;
use tracing::info;
mod background;
mod errors;
mod extractors;
mod factory;
mod handlers;
mod mappers;
mod routes;
mod state;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
// Load .env file if present
let _ = dotenvy::dotenv();
// Initialize tracing
tracing_subscriber::fmt()
.with_env_filter(
tracing_subscriber::EnvFilter::try_from_default_env()
.unwrap_or_else(|_| "info".into()),
)
.init();
// Load config
let config = infra_wiring::Config::from_env()
.map_err(|e| anyhow::anyhow!("Config error: {}", e))?;
let host = config.host.clone();
let port = config.port;
let cors_origins = config.cors_origins.clone();
info!("Starting k-tv server on {}:{}", host, port);
// Build the application state (connects DB, creates adapters, spawns background tasks)
let app_state = factory::build_app_state(config).await?;
// Build CORS layer
let cors = if cors_origins.iter().any(|o| o == "*") {
CorsLayer::new()
.allow_origin(Any)
.allow_methods(Any)
.allow_headers(Any)
} else {
let origins: Vec<_> = cors_origins
.iter()
.filter_map(|o| o.parse().ok())
.collect();
CorsLayer::new()
.allow_origin(origins)
.allow_methods(Any)
.allow_headers(Any)
};
// Build the router
let app = axum::Router::new()
.nest("/api/v1", routes::api_v1_router())
.layer(cors)
.layer(TraceLayer::new_for_http())
.with_state(app_state);
// Start serving
let addr: SocketAddr = format!("{}:{}", host, port).parse()?;
let listener = tokio::net::TcpListener::bind(addr).await?;
info!("Listening on {}", addr);
axum::serve(listener, app).await?;
Ok(())
}