use std::sync::Arc; use axum::Router; use sqlx::postgres::PgPoolOptions; use tokio::net::TcpListener; use tower_http::cors::CorsLayer; use tower_http::trace::TraceLayer; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; mod api; mod config; mod db; mod middleware; mod models; mod services; use config::AppConfig; /// Shared application state available to all handlers pub struct AppState { pub db: sqlx::PgPool, pub nats: async_nats::Client, pub config: AppConfig, } #[tokio::main] async fn main() -> anyhow::Result<()> { // Load environment variables dotenvy::dotenv().ok(); // Initialize tracing tracing_subscriber::registry() .with(tracing_subscriber::EnvFilter::try_from_default_env().unwrap_or_else(|_| { "corrosion_api=debug,tower_http=debug,axum=trace".into() })) .with(tracing_subscriber::fmt::layer()) .init(); let config = AppConfig::from_env()?; // Database connection pool let db = PgPoolOptions::new() .max_connections(config.database_max_connections) .connect(&config.database_url) .await?; tracing::info!("Connected to PostgreSQL"); // Run migrations sqlx::migrate!("./migrations").run(&db).await?; tracing::info!("Database migrations applied"); // NATS connection let nats = async_nats::connect(&config.nats_url).await?; tracing::info!("Connected to NATS at {}", config.nats_url); let state = Arc::new(AppState { db, nats, config }); // Build router let app = Router::new() .nest("/api/auth", api::auth::router()) .nest("/api/servers", api::servers::router()) .nest("/api/wipes", api::wipes::router()) .nest("/api/maps", api::maps::router()) .nest("/api/plugins", api::plugins::router()) .nest("/api/panels", api::panels::router()) .nest("/api/schedules", api::schedules::router()) .nest("/api/logs", api::logs::router()) .nest("/api/public", api::public::router()) .nest("/api/team", api::team::router()) .nest("/api/notifications", api::notifications::router()) .nest("/api/license", api::license::router()) .nest("/api/store", api::store::router()) .layer(CorsLayer::permissive()) // TODO: Restrict in production .layer(TraceLayer::new_for_http()) .with_state(state); let bind_addr = "0.0.0.0:3000"; let listener = TcpListener::bind(bind_addr).await?; tracing::info!("Corrosion API listening on {}", bind_addr); axum::serve(listener, app).await?; Ok(()) }