mod cli; mod config; mod db; mod helpers; mod notify; mod queries; mod server; mod templates; use argon2::{ password_hash::{rand_core::OsRng, PasswordHasher, SaltString}, Argon2, }; use clap::Parser; #[tokio::main] async fn main() { let opt = cli::MainOpt::parse(); match opt.cmd { cli::MainSubcommand::Init => { init_all(opt.opt, cli::StartOpt { tmp: false }); } cli::MainSubcommand::Start(subopt) => { let (config, dbs, templates) = init_all(opt.opt, subopt); server::start_server(config, dbs, templates).await } cli::MainSubcommand::Psw => { let mut config = config::read_config(&opt.opt.dir.0); let password = rpassword::prompt_password("Additional admin password: ").unwrap(); let salt = SaltString::generate(&mut OsRng); let argon2 = Argon2::default(); let password_hash = argon2 .hash_password(password.as_bytes(), &salt) .unwrap() .to_string(); config.admin_passwords.push(password_hash); config::write_config(&opt.opt.dir.0, &config); } } } fn init_all( opt: cli::MainCommonOpt, subopt: cli::StartOpt, ) -> (config::Config, db::Dbs, templates::Templates) { std::fs::create_dir_all(&opt.dir.0).expect("Cannot create dir"); let dbs = db::load_dbs((!subopt.tmp).then_some(&opt.dir.0)); ( config::read_config(&opt.dir.0), dbs, templates::Templates::new(&opt.dir.0), ) }