use comunic_server::{cleanup_thread, server}; use comunic_server::constants::admin::{ADMIN_ROLES_LIST, AdminRole}; use comunic_server::data::admin::NewAdmin; use comunic_server::data::config::{conf, Config}; use comunic_server::data::error::Res; use comunic_server::data::user::UserID; use comunic_server::helpers::{account_helper, admin_account_helper, admin_roles_helper, database}; use comunic_server::utils::date_utils::current_year; type MainActionFunction = Res; struct Action { name: &'static str, description: &'static str, arguments: Vec<&'static str>, function: Box) -> MainActionFunction>, } fn get_actions() -> Vec { vec![ // Start server Action { name: "serve", description: "Start the Comunic Server (default action)", arguments: vec![], function: Box::new(serve), }, // Show help Action { name: "help", description: "Show this help", arguments: vec![], function: Box::new(help), }, // Reset password Action { name: "reset_password", description: "Create a password reset URL for a user", arguments: vec!["user_id"], function: Box::new(reset_password), }, // Create a new administrator Action { name: "create_admin", description: "Create a new administrator account", arguments: vec!["name", "email"], function: Box::new(create_admin), }, // Create a reset token for an admin Action { name: "create_admin_reset_token", description: "Create a new reset token to register a new access key to an admin account", arguments: vec!["email"], function: Box::new(create_admin_reset_token), }, // Get the list of available admin roles Action { name: "list_admin_roles", description: "Get the list of available admin roles", arguments: vec![], function: Box::new(list_admin_roles), }, // Attribute a role to an admin Action { name: "grant_admin_role", description: "Grant a role to an admin", arguments: vec!["mail", "role_id"], function: Box::new(grant_admin_role), } ] } #[actix_rt::main] async fn main() -> std::io::Result<()> { let args: Vec = std::env::args().collect(); let conf_file = match args.get(1) { Some(el) => el.to_string(), None => { eprintln!("Please specify configuration file as first argument!"); std::process::exit(-3); } }; // Load configuration Config::load(&conf_file).expect("Could not load configuration!"); // Connect to the database database::connect(&conf().database).expect("Could not connect to database!"); // Get selected action let action = args .get(2) .map(|a| a.as_str()) .unwrap_or("serve") .to_string(); let actions = get_actions(); let selected_action = actions .iter() .filter(|p| p.name.eq(&action)) .next(); let selected_action = match selected_action { None => { eprintln!("Action {} invalid! For more information try 'help'!", action); std::process::exit(-1); } Some(a) => a }; if !selected_action.arguments.is_empty() && selected_action.arguments.len() + 3 != args.len() { eprintln!("Invalid number of arguments!"); std::process::exit(-2); } let args = match args.len() { 0 | 1 | 2 => vec![], _ => (&args[3..]).to_vec() }; let res = (selected_action.function)(args.to_vec()); res.expect("Failed to execute action!"); Ok(()) } /// Start Comunic Server (main action) fn serve(_a: Vec) -> Res { let t = std::thread::spawn(|| { let sys = actix::System::new("sys"); let promise = async { // Start cleanup thread cleanup_thread::start().expect("Failed to start cleanup thread!"); // Start the server server::start_server(conf()).await }; tokio::runtime::Runtime::new().unwrap().block_on(promise) .expect("Failed to start server!"); let _ = sys.run(); }); t.join().unwrap(); Ok(()) } fn help(_a: Vec) -> Res { println!("Comunic API v3 Server - (c) Pierre HUBERT 2012 - {}", current_year()); println!("Usage: {} [conf-file] [action] [args...]", std::env::args().next().unwrap()); println!("Available actions:"); for action in get_actions() { println!("\t{} {}\t- {}", action.name, action.arguments.iter().map(|s| format!("[{}]", s)).collect::>().join(" "), action.description ); } Ok(()) } fn reset_password(args: Vec) -> Res { let user_id = UserID::new(args[0].parse::()?); let token = account_helper::generate_password_reset_token(&user_id)?; println!("{}", conf().password_reset_url.replace("{TOKEN}", &token)); Ok(()) } fn create_admin(args: Vec) -> Res { let new_admin = NewAdmin { name: args[0].to_string(), email: args[1].to_string(), }; if !mailchecker::is_valid(&new_admin.email) { eprintln!("Specified email address is not valid!"); std::process::exit(-1); } let id = admin_account_helper::create(&new_admin) .expect("Failed to create account!"); println!("* New admin ID: {}", id.id()); Ok(()) } fn create_admin_reset_token(args: Vec) -> Res { let admin = admin_account_helper::find_admin_by_email(&args[0]) .expect("Failed to load admin information!"); println!("Generate a new reset token for {} ({})", admin.name, admin.email); let token = admin_account_helper::create_new_reset_token(admin.id) .expect("Failed to create admin reset token!"); println!("Reset token: {}", token.token); Ok(()) } fn list_admin_roles(_a: Vec) -> Res { println!("Here are the currently defined roles in the code:\n"); for role in Vec::from(ADMIN_ROLES_LIST) { println!("* {} - {}\n{}\n", role.id, role.name, role.description); } Ok(()) } fn grant_admin_role(args: Vec) -> Res { let role = AdminRole::from_id(&args[1]) .expect("Requested role does not exist!"); let admin = admin_account_helper::find_admin_by_email(&args[0]) .expect("Failed to load admin information!"); if admin.roles.contains(&role) { eprintln!("The administrator has already this role!"); std::process::exit(-3); } admin_roles_helper::add_role(admin.id, role)?; println!("Success."); Ok(()) }