use clap::{Parser, Subcommand}; use sqlx::postgres::PgPoolOptions; use std::time::Duration; use std::sync::Arc; use crate::repo::user_repo::UserRepository; use crate::repo::space_repo::SpaceRepository; use crate::repo::channel_repo::ChannelRepository; use crate::repo::{UserRepo, SpaceRepo, ChannelRepo}; use argon2::{ password_hash::{PasswordHasher, SaltString}, Argon2, }; use rand::rngs::OsRng; #[derive(Parser)] #[command(author, version, about, long_about = None)] pub struct Cli { #[command(subcommand)] pub command: Option, } #[derive(Subcommand)] pub enum Commands { /// First-time setup for the server Setup { /// Admin username #[arg(short, long)] username: String, /// Admin password #[arg(short, long)] password: String, /// Default space name #[arg(short, long, default_value = "Default Space")] space: String, /// Default channel name #[arg(short, long, default_value = "general")] channel: String, }, } pub async fn handle_cli() -> bool { let cli = Cli::parse(); match cli.command { Some(Commands::Setup { username, password, space, channel }) => { if let Err(e) = run_setup(username, password, space, channel).await { eprintln!("Setup failed: {}", e); std::process::exit(1); } println!("Setup completed successfully!"); true } None => false, } } async fn run_setup(username: String, password: String, space_name: String, channel_name: String) -> Result<(), Box> { dotenv::dotenv().ok(); let db_url = std::env::var("DATABASE_URL").expect("DATABASE_URL must be set"); let pool = PgPoolOptions::new() .max_connections(1) .acquire_timeout(Duration::from_secs(5)) .connect(&db_url) .await?; let user_repo = UserRepository::new(pool.clone()); let space_repo = SpaceRepository::new(pool.clone()); let channel_repo = ChannelRepository::new(pool.clone()); // 1. Create admin user println!("Creating admin user: {}...", username); let argon2 = Argon2::default(); let salt = SaltString::generate(&mut OsRng); let passhash = argon2 .hash_password(password.as_bytes(), &salt) .map_err(|e| e.to_string())? .to_string(); let user_id = user_repo.new_user("admin@localhost", &username, &passhash).await?; user_repo.set_role(user_id, "admin").await?; // 2. Create default space println!("Creating default space: {}...", space_name); let space_id = space_repo.create(&space_name, Some("Default space created during setup"), user_id).await?; // 3. Create default channel println!("Creating default channel: {}...", channel_name); channel_repo.create(&channel_name, Some("Default channel"), space_id).await?; Ok(()) }