mod config; mod errors; mod models; mod routes; mod services; use actix_cors::Cors; use actix_web::{web, App, HttpServer, middleware}; use sqlx::postgres::PgPoolOptions; use tracing_subscriber::EnvFilter; use crate::config::AppConfig; use crate::services::cache::CacheService; use crate::services::martin::MartinService; use crate::services::osrm::OsrmService; use crate::services::photon::PhotonService; #[actix_web::main] async fn main() -> std::io::Result<()> { // Load .env file if present (ignore errors when missing). let _ = dotenvy::dotenv(); // Set up tracing subscriber with env-filter (defaults to INFO). tracing_subscriber::fmt() .with_env_filter( EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info")), ) .init(); let config = AppConfig::from_env(); tracing::info!("Starting maps-backend on {}:{}", config.host, config.port); // Database connection pool. let pool = PgPoolOptions::new() .max_connections(20) .connect(&config.database_url) .await .expect("Failed to connect to PostgreSQL"); tracing::info!("Connected to PostgreSQL"); // Redis cache service. let cache = CacheService::new(&config.redis_url).expect("Failed to connect to Redis"); tracing::info!("Redis client initialized"); // Upstream service clients. let martin = MartinService::new(&config); let photon = PhotonService::new(&config); let osrm = OsrmService::new(&config); // Server start time for the health endpoint uptime counter. let start_time = std::time::Instant::now(); let bind_addr = format!("{}:{}", config.host, config.port); // Wrap shared state in web::Data (Arc) so cloning inside the closure is cheap. let pool = web::Data::new(pool); let cache = web::Data::new(cache); let martin = web::Data::new(martin); let photon = web::Data::new(photon); let osrm = web::Data::new(osrm); let config = web::Data::new(config); let start_time = web::Data::new(start_time); HttpServer::new(move || { let cors = Cors::permissive(); App::new() .wrap(cors) .wrap(middleware::Logger::default()) .app_data(pool.clone()) .app_data(cache.clone()) .app_data(martin.clone()) .app_data(photon.clone()) .app_data(osrm.clone()) .app_data(config.clone()) .app_data(start_time.clone()) .configure(routes::configure) }) .bind(&bind_addr)? .run() .await }