use anyhow::Result; use axum::{ extract::State, http::StatusCode, response::Json, routing::{get, post}, Router, }; use serde_json::{json, Value}; use std::net::SocketAddr; use tower_http::cors::CorsLayer; use tracing::{info, warn}; mod config; mod database; mod models; mod handlers; mod services; mod auth; mod error; use config::Settings; use database::Database; use error::AppError; #[derive(Clone)] pub struct AppState { pub db: Database, pub settings: Settings, } #[tokio::main] async fn main() -> Result<()> { // Initialize tracing tracing_subscriber::fmt::init(); // Load configuration let settings = Settings::new() .map_err(|e| anyhow::anyhow!("Failed to load configuration: {}", e))?; info!("Starting ai.card API server v{}", env!("CARGO_PKG_VERSION")); info!("Configuration loaded from: {}", settings.config_dir.display()); // Initialize database let database = Database::connect(&settings.database_url).await?; // Run migrations database.migrate().await?; info!("Database migrations completed"); let app_state = AppState { db: database, settings: settings.clone(), }; // Build application routes let app = create_app(app_state).await; // Start server let addr = SocketAddr::from(([0, 0, 0, 0], settings.port)); info!("ai.card API server listening on {}", addr); let listener = tokio::net::TcpListener::bind(addr).await?; axum::serve(listener, app).await?; Ok(()) } async fn create_app(state: AppState) -> Router { Router::new() // Health check .route("/health", get(health_check)) // API v1 routes .nest("/api/v1", create_api_routes()) // CORS middleware .layer(CorsLayer::permissive()) // Application state .with_state(state) } fn create_api_routes() -> Router { Router::new() // Authentication routes .nest("/auth", handlers::auth::create_routes()) // Card routes .nest("/cards", handlers::cards::create_routes()) // Sync routes .nest("/sync", handlers::sync::create_routes()) } async fn health_check() -> Result, AppError> { Ok(Json(json!({ "status": "healthy", "service": "ai.card", "version": env!("CARGO_PKG_VERSION"), "timestamp": chrono::Utc::now().to_rfc3339() }))) }