From ae7ef10b67737f08a86743f687bca30a12e9e17a Mon Sep 17 00:00:00 2001 From: salmonik Date: Sun, 12 Jul 2026 19:38:03 +0200 Subject: first stable --- src/main.rs | 99 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 97 insertions(+), 2 deletions(-) (limited to 'src/main.rs') diff --git a/src/main.rs b/src/main.rs index e7a11a9..a96b41b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,98 @@ -fn main() { - println!("Hello, world!"); +mod auth; +mod config; +mod db; +mod docker; +mod models; +mod rcon; +mod routes; + +use tracing_subscriber::EnvFilter; + +use crate::db::Database; +use crate::docker::DockerManager; +use crate::routes::AppState; + +#[tokio::main] +async fn main() { + tracing_subscriber::fmt() + .with_env_filter(EnvFilter::try_from_default_env().unwrap_or_else(|_| "info".into())) + .init(); + + let config = config::Config::load().unwrap_or_else(|e| { + eprintln!("Configuration error: {e}"); + eprintln!("Please create a config.toml file based on the example."); + std::process::exit(1); + }); + + if !config.server_directory.exists() { + eprintln!( + "Server directory does not exist: {}", + config.server_directory.display() + ); + eprintln!("Please create it or update config.toml."); + std::process::exit(1); + } + + let db_path = config.server_directory.join("mcui.db"); + let db = Database::open(&db_path).unwrap_or_else(|e| { + eprintln!("Failed to open database: {e}"); + std::process::exit(1); + }); + + bootstrap_superuser(&db, &config); + + let docker = DockerManager::new(config.clone()).unwrap_or_else(|e| { + eprintln!("Failed to connect to Docker: {e}"); + eprintln!("Is the Docker daemon running and accessible?"); + std::process::exit(1); + }); + + let state = AppState { + config: config.clone(), + db: db.clone(), + docker, + }; + + let router = routes::build_router(state); + + let addr = format!("127.0.0.1:{}", config.webui_port); + tracing::info!("Starting Minecraft UI on http://{addr}"); + + let listener = tokio::net::TcpListener::bind(&addr) + .await + .unwrap_or_else(|e| { + eprintln!("Failed to bind to {addr}: {e}"); + std::process::exit(1); + }); + + axum::serve(listener, router).await.unwrap_or_else(|e| { + eprintln!("Server error: {e}"); + }); +} + +fn bootstrap_superuser(db: &Database, config: &config::Config) { + let conn = db.conn(); + let count: i32 = conn + .query_row("SELECT COUNT(*) FROM accounts", [], |row| row.get(0)) + .unwrap_or(0); + drop(conn); + + if count == 0 { + let hash = auth::hash_password(&config.initial_superuser_password) + .expect("Failed to hash initial password"); + + let conn = db.conn(); + conn.execute( + "INSERT INTO accounts (username, password, is_superuser, password_change_required) + VALUES (?1, ?2, 1, 1)", + rusqlite::params![config.initial_superuser, hash], + ) + .expect("Failed to create initial superuser"); + drop(conn); + + tracing::info!( + "Created initial superuser '{}' (password change required on first login)", + config.initial_superuser + ); + } } -- cgit v1.2.3