- Added `SetupService` to handle the generation and validation of setup tokens. - Integrated setup token generation during application startup if no admin users exist. - Created API endpoints for checking setup status and completing the initial setup. - Updated `AuthService` to include functionality for creating the initial admin user. - Enhanced error handling for setup and authentication processes. - Added frontend components for login and protected routes. - Implemented Zustand store for managing authentication state. - Updated Vite configuration to check setup status and serve the setup page if required. - Documented the initial setup process in `setup.md`.
118 lines
3.9 KiB
Rust
118 lines
3.9 KiB
Rust
//! 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<dyn std::error::Error>> {
|
|
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(())
|
|
}
|