mod handlers; mod mail; use axum::{Router, http::Method}; use std::net::SocketAddr; use std::sync::Arc; use tower_http::cors::{Any, CorsLayer}; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; use sqlx::PgPool; use mail::Mailer; #[derive(Clone)] pub struct AppState { pub pool: PgPool, pub mail: Arc, } #[tokio::main] async fn main() { tracing_subscriber::registry() .with(tracing_subscriber::EnvFilter::new( std::env::var("RUST_LOG").unwrap_or_else(|_| "info".into()), )) .with(tracing_subscriber::fmt::layer()) .init(); let database_url = std::env::var("DATABASE_URL").expect("DATABASE_URL must be set"); let pool = db::establish_connection(&database_url) .await .expect("Failed to connect to the database"); tracing::info!("Connected to the database"); let mailer = Arc::new(Mailer::new()); let state = AppState { pool, mail: mailer, }; let cors = CorsLayer::new() .allow_methods([Method::GET, Method::POST, Method::PUT, Method::PATCH, Method::DELETE, Method::OPTIONS]) .allow_origin(Any) .allow_headers(Any); let app = Router::new() // ── Auth ───────────────────────────────────────────────────────── .nest("/api/auth", handlers::auth::router()) // ── Roles & User Self-Service ───────────────────────────────────── .nest("/api/admin/roles", handlers::roles::router()) // ── Notifications ───────────────────────────────────────────────── .nest("/api/me/notifications", handlers::notifications::router()) // ── Admin: Onboarding + Dashboard Config ────────────────────────── .nest("/api/admin/onboarding-config", handlers::config::onboarding_router()) .nest("/api/admin/dashboard-config", handlers::config::dashboard_router()) // ── Public Config ───────────────────────────────────────────────── .nest("/api/config/onboarding", handlers::config::onboarding_router()) .nest("/api/config/dashboard", handlers::config::dashboard_router()) .nest("/api/runtime-config", handlers::config::runtime_router()) .layer(cors) .with_state(state); let port: u16 = std::env::var("PORT") .unwrap_or_else(|_| "8080".to_string()) .parse() .expect("PORT must be a valid u16"); let addr = SocketAddr::from(([0, 0, 0, 0], port)); tracing::info!("Users service listening on {}", addr); let listener = tokio::net::TcpListener::bind(&addr).await.unwrap(); axum::serve(listener, app).await.unwrap(); }