use axum::{ error_handling::HandleErrorLayer, extract::Extension, http::{HeaderValue, Method, StatusCode}, routing::get, Router, }; use dotenvy::dotenv; use sqlx::postgres::PgPoolOptions; use std::{env, net::SocketAddr, path::PathBuf, sync::Arc, time::Duration}; use tower::ServiceBuilder; use tower_http::{cors::CorsLayer, services::ServeDir, trace::TraceLayer}; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; mod config; mod error; mod models; mod routes; mod services; mod utils; use config::Config; use error::AppError; #[tokio::main] async fn main() -> Result<(), Box> { dotenv().ok(); tracing_subscriber::registry() .with(tracing_subscriber::EnvFilter::new( env::var("RUST_LOG").unwrap_or_else(|_| "info,tower_http=debug".into()), )) .with(tracing_subscriber::fmt::layer()) .init(); let config = Config::from_env(); let config = Arc::new(config); let storage_path = PathBuf::from(&config.storage_path); if !storage_path.exists() { std::fs::create_dir_all(&storage_path)?; } let pool = PgPoolOptions::new() .max_connections(10) .connect(&config.database_url) .await? sqlx::migrate!("./migrations") .run(&pool) .await? tracing::info!("Database migrations applied successfully"); let encryption_service = services::encryption::EncryptionService::new(&config.master_key); let cors = CorsLayer::new() .allow_origin("*".parse::().unwrap()) .allow_methods([Method::GET, Method::POST, Method::PUT, Method::DELETE]) .allow_headers([axum::http::header::CONTENT_TYPE, axum::http::header::AUTHORIZATION]); let api_router = Router::new() .nest("/api", routes::api_routes()) .layer( ServiceBuilder::new() .layer(HandleErrorLayer::new(|error| async move { ( StatusCode::INTERNAL_SERVER_ERROR, format!("Unhandled error: {}", error), ) })) .layer(TraceLayer::new_for_http()) .layer(Extension(pool.clone())) .layer(Extension(Arc::clone(&config))) .layer(Extension(encryption_service)), ); let static_files_service = ServeDir::new("static"); let static_router = Router::new().nest_service("/", static_files_service.clone()); let app = Router::new() .merge(api_router) .fallback_service(static_router) .layer(cors); let addr = SocketAddr::from(([0, 0, 0, 0], 8080)); tracing::info!("Listening on {}", addr); axum::Server::bind(&addr) .serve(app.into_make_service()) .await? Ok(()) }