use std::env; use std::time::Duration; use actix_cors::Cors; use actix_web::{App, HttpServer, middleware::Logger, web}; use dotenv::from_filename; use reqwest::Certificate; use uuid::Uuid; use crate::account::hash; use crate::users::{User, ADMIN_ROLE}; mod account; mod airports; mod db; mod error; mod metars; mod scheduler; mod system; mod users; #[derive(Debug, Clone)] struct AppState { client: reqwest::Client, } #[actix_web::main] async fn main() -> Result<(), Box> { initialize_environment()?; db::initialize().await?; // scheduler::update_airports(); // Initialize admin user let admin_email = env::var("ADMIN_EMAIL"); let admin_password = env::var("ADMIN_PASSWORD"); if admin_email.is_ok() && admin_password.is_ok() { let email = admin_email.unwrap(); if User::select_by_email(&email).await.is_none() { log::debug!("Creating default administrator"); let password = admin_password.unwrap(); let password_hash = hash(&password)?; if email == "admin@example.com" || password == "changeme" { log::warn!( "Default admin credentials are in use, update the ADMIN_EMAIL and ADMIN_PASSWORD." ); } let admin_user = User { id: Uuid::new_v4(), email, email_verified: true, password_hash, role: ADMIN_ROLE.to_string(), first_name: "Admin".to_string(), last_name: "".to_string(), avatar: None, updated_at: Default::default(), created_at: Default::default(), }; match admin_user.insert().await { Ok(_) => log::debug!("Default administrator was successfully created"), Err(err) => { log::warn!("{}", err); } }; } } let mut client_builder = reqwest::Client::builder() .timeout(Duration::from_secs(10)) .tls_built_in_root_certs(true); if let Ok(val) = env::var("NGINX_SSL_ENABLED") { if val == "true" { let certificate_path = env::var("SSL_CA_PATH")?; let certificate_data = std::fs::read(certificate_path)?; let certificate = Certificate::from_pem(&certificate_data)?; client_builder = client_builder.add_root_certificate(certificate); } } let client = client_builder .build() .expect("Failed to create reqwest client"); let state = AppState { client }; let host = "0.0.0.0"; let port = env::var("API_PORT").unwrap_or("5000".to_string()); let server = match HttpServer::new(move || { let cors = Cors::default() .allow_any_origin() .allow_any_method() .allow_any_header() .supports_credentials() .max_age(3600); App::new() .wrap(cors) .wrap(Logger::default()) .app_data(web::Data::new(state.clone())) .service( web::scope("api") .configure(airports::init_routes) .configure(metars::init_routes) .configure(account::init_routes) .configure(users::init_routes) .configure(system::init_routes), ) }) .bind(format!("{}:{}", host, port)) { Ok(b) => { log::info!("Server bound to {}:{}", host, port); b } Err(err) => { log::error!("Could not bind server: {}", err); return Err(err.into()); } }; if let Err(err) = server.run().await { return Err(err.into()); } Ok(()) } fn initialize_environment() -> std::io::Result<()> { // Iterate over files in the current directory for entry in std::fs::read_dir(".")? { let entry = entry?; let path = entry.path(); // Check if the file name starts with ".env" and is a file if let Some(file_name) = path.file_name().and_then(|n| n.to_str()) { if file_name.starts_with(".env") && path.is_file() { // Try to load the file if let Err(err) = from_filename(&file_name) { eprintln!("Failed to load {}: {}", file_name, err); } else { println!("Loaded: {}", file_name); } } } } env_logger::init_from_env(env_logger::Env::default().filter_or("RUST_LOG", "warn,api=info")); Ok(()) }