//! API Server Entry Point //! //! Configures and starts the HTTP server with JWT-based authentication. use std::sync::Arc; use tracing::info; use domain::{ChannelService, IProviderRegistry, ScheduleEngineService, UserService}; use infra::factory::{build_activity_log_repository, build_app_settings_repository, build_channel_repository, build_library_repository, build_provider_config_repository, build_schedule_repository, build_user_repository}; #[cfg(feature = "local-files")] use infra::factory::build_transcode_settings_repository; mod config; mod database; mod library_scheduler; mod provider_registry; mod dto; mod error; mod events; mod extractors; mod log_layer; mod poller; mod routes; mod scheduler; mod server; mod startup; mod state; mod telemetry; mod webhook; use crate::config::Config; use crate::state::AppState; #[tokio::main] async fn main() -> anyhow::Result<()> { let handles = telemetry::init_tracing(); let config = Config::from_env(); info!("Starting server on {}:{}", config.host, config.port); // Setup database let db_pool = database::init_database(&config).await?; let user_repo = build_user_repository(&db_pool).await?; let channel_repo = build_channel_repository(&db_pool).await?; let schedule_repo = build_schedule_repository(&db_pool).await?; let activity_log_repo = build_activity_log_repository(&db_pool).await?; let user_service = UserService::new(user_repo); let channel_service = ChannelService::new(channel_repo.clone()); // Build provider registry — all configured providers are registered simultaneously. let provider_config_repo = build_provider_config_repository(&db_pool).await?; let bundle = provider_registry::build_provider_registry( &config, &db_pool, &provider_config_repo, ).await?; let registry_arc = bundle.registry; let provider_registry: Arc>> = Arc::new(tokio::sync::RwLock::new(Arc::clone(®istry_arc))); let (event_tx, event_rx) = tokio::sync::broadcast::channel::(64); let bg_channel_repo = channel_repo.clone(); let webhook_channel_repo = channel_repo.clone(); tokio::spawn(webhook::run_webhook_consumer( event_rx, webhook_channel_repo, reqwest::Client::new(), )); let schedule_engine = ScheduleEngineService::new( Arc::clone(®istry_arc) as Arc, channel_repo, schedule_repo, ); #[cfg(feature = "local-files")] let transcode_settings_repo = build_transcode_settings_repository(&db_pool).await.ok(); let library_repo = build_library_repository(&db_pool).await?; let app_settings_repo = build_app_settings_repository(&db_pool).await?; let library_sync_adapter: Arc = Arc::new(infra::FullSyncAdapter::new(Arc::clone(&library_repo))); #[allow(unused_mut)] let mut state = AppState::new( user_service, channel_service, schedule_engine, provider_registry, provider_config_repo, config.clone(), event_tx.clone(), handles.log_tx, handles.log_history, activity_log_repo, db_pool, library_repo, library_sync_adapter, app_settings_repo, #[cfg(feature = "local-files")] transcode_settings_repo, ) .await?; #[cfg(feature = "local-files")] if !bundle.local_index.is_empty() { *state.local_index.write().await = bundle.local_index; } #[cfg(feature = "local-files")] if let Some(tm) = bundle.transcode_manager { *state.transcode_manager.write().await = Some(tm); } startup::spawn_background_tasks( Arc::clone(&state.schedule_engine), bg_channel_repo, event_tx, ); tokio::spawn(library_scheduler::run_library_sync( Arc::clone(&state.library_sync_adapter), Arc::clone(&state.provider_registry), Arc::clone(&state.app_settings_repo), )); server::build_and_serve(state, &config).await }