//! NxMesh Master Library //! //! This crate implements the control plane for NxMesh. pub mod api; pub mod config; pub mod db; pub mod domain; pub mod events; pub mod grpc; pub mod infrastructure; pub mod services; use std::sync::Arc; use config::Settings; use db::{Database, Migrator}; use sea_orm_migration::MigratorTrait; use tokio::net::TcpListener; use tracing::{error, info}; /// Start the master server pub async fn start(settings: Settings) -> Result<(), Box> { let settings = Arc::new(settings); info!("Connecting to database..."); let db = Database::connect(&settings.database.url).await.map_err(|e| { error!("Failed to connect to database: {}", e); e })?; info!("Running database migrations..."); Migrator::up(db.conn(), None).await.map_err(|e| { error!("Failed to run migrations: {}", e); e })?; info!("Database migrations complete"); // Create application state let app_state = api::routes::AppState::new(db.clone(), settings.clone()); // Generate setup token if no admin exists match app_state.setup_service.has_admin_users().await { Ok(false) => { info!("================================================================="); info!(" INITIAL SETUP REQUIRED "); info!("================================================================="); info!("No admin user found. A setup token has been generated."); info!(""); match app_state.setup_service.generate_setup_token().await { Ok(token_info) => { info!("SETUP TOKEN: {}", token_info.plain_token); info!("EXPIRES AT: {}", token_info.expires_at); info!(""); info!("Use this token to create the first admin account at:"); info!(" http://{}:{}/setup", settings.server.bind_address, settings.server.port); info!(""); info!("WARNING: This token is single-use and will expire in 24 hours."); info!(" It is displayed only once in these logs."); } Err(e) => { error!("Failed to generate setup token: {}", e); } } info!("================================================================="); } Ok(true) => { info!("Admin user exists - initial setup already completed"); } Err(e) => { error!("Failed to check admin status: {}", e); } } // Create router let app = api::routes::create_router(app_state); // Start HTTP server let http_addr = format!("{}:{}", settings.server.bind_address, settings.server.port); info!("Starting HTTP server on {}", http_addr); let http_listener = TcpListener::bind(&http_addr).await?; // Start gRPC server in a separate task let grpc_settings = settings.clone(); let grpc_db = db.clone(); let grpc_handle = tokio::spawn(async move { let grpc_addr = format!("{}:{}", grpc_settings.grpc.bind_address, grpc_settings.grpc.port); info!("Starting gRPC server on {}", grpc_addr); if let Err(e) = grpc::server::start(&grpc_addr, grpc_db).await { error!("gRPC server error: {}", e); } }); // Run HTTP server info!("Master server ready!"); tokio::select! { result = axum::serve(http_listener, app) => { if let Err(e) = result { error!("HTTP server error: {}", e); } } _ = tokio::signal::ctrl_c() => { info!("Shutdown signal received"); } } // Cancel gRPC server grpc_handle.abort(); info!("Master server shutdown complete"); Ok(()) }