use hero_websocket_server::{ServerBuilder, ServerConfig}; use clap::Parser; #[derive(Parser, Debug)] #[clap(author, version, about, long_about = None)] struct Args { #[clap(short = 'H', long, value_parser, default_value = "127.0.0.1")] host: String, #[clap(short, long, value_parser, default_value_t = 8443)] port: u16, #[clap(long, value_parser, default_value = "redis://127.0.0.1/")] redis_url: String, #[clap(long, help = "Enable authentication")] auth: bool, #[clap(long, help = "Enable TLS/WSS")] tls: bool, #[clap(long, value_parser, help = "Path to TLS certificate file")] cert: Option, #[clap(long, value_parser, help = "Path to TLS private key file")] key: Option, #[clap(long, value_parser, help = "Separate port for TLS connections")] tls_port: Option, #[clap(short, long, action = clap::ArgAction::Count, help = "Increase verbosity (-v for debug, -vv for trace)")] verbose: u8, #[clap(long, help = "Remove timestamps from log output")] no_timestamp: bool, #[clap(long, help = "Enable webhook handling")] webhooks: bool, #[clap(short, long, value_parser, help = "Path to configuration file")] config: Option, #[clap(long, help = "Generate a sample configuration file")] generate_config: bool, } #[actix_web::main] async fn main() -> std::io::Result<()> { let args = Args::parse(); // Handle config file generation if args.generate_config { let sample_config = ServerConfig::create_sample(); let config_path = "config.json"; match sample_config.to_file(config_path) { Ok(_) => { println!("✅ Sample configuration file generated: {}", config_path); println!("📝 Edit the file to customize your server configuration."); return Ok(()); } Err(e) => { eprintln!("❌ Failed to generate config file: {}", e); std::process::exit(1); } } } // Load configuration from file if provided, otherwise use CLI args let config = if let Some(config_path) = &args.config { match ServerConfig::from_file(config_path) { Ok(config) => { println!("📄 Loaded configuration from: {}", config_path); config } Err(e) => { eprintln!("❌ Failed to load config file {}: {}", config_path, e); std::process::exit(1); } } } else { // Create config from CLI arguments ServerConfig { host: args.host.clone(), port: args.port, redis_url: args.redis_url.clone(), auth: args.auth, tls: args.tls, cert: args.cert.clone(), key: args.key.clone(), tls_port: args.tls_port, webhooks: args.webhooks, circles: std::collections::HashMap::new(), // Empty circles when using CLI } }; // Configure logging based on verbosity level let log_config = match args.verbose { 0 => { // Default: suppress actix server logs, show only hero_websocket_server info and above "warn,hero_websocket_server=info" } 1 => { // -v: show debug for hero_websocket_server, info for actix "info,hero_websocket_server=debug,actix_server=info" } 2 => { // -vv: show debug for everything "debug" } _ => { // -vvv and above: show trace for everything "trace" } }; std::env::set_var("RUST_LOG", log_config); // Configure env_logger with or without timestamps if args.no_timestamp { env_logger::Builder::from_default_env() .format_timestamp(None) .init(); } else { env_logger::init(); } // Validate configuration if let Err(e) = config.validate() { eprintln!("❌ Configuration validation failed: {}", e); std::process::exit(1); } // Build server from configuration let builder = ServerBuilder::new().from_config(config.clone()); let server = match builder.build() { Ok(server) => server, Err(e) => { eprintln!("Error building server: {}", e); std::process::exit(1); } }; println!("🚀 Starting Circles WebSocket Server"); println!("📋 Configuration:"); println!(" Host: {}", config.host); println!(" Port: {}", config.port); println!(" Redis URL: {}", config.redis_url); if let Some(tls_port) = config.tls_port { println!(" TLS Port: {}", tls_port); } println!(" Authentication: {}", if config.auth { "ENABLED" } else { "DISABLED" }); println!(" TLS/WSS: {}", if config.tls { "ENABLED" } else { "DISABLED" }); println!(" Webhooks: {}", if config.webhooks { "ENABLED" } else { "DISABLED" }); println!(" Circles configured: {}", config.circles.len()); if config.tls { if let (Some(cert), Some(key)) = (&config.cert, &config.key) { println!(" Certificate: {}", cert); println!(" Private Key: {}", key); } } if config.webhooks { println!(" Webhook secrets loaded from environment variables:"); println!(" - STRIPE_WEBHOOK_SECRET"); println!(" - IDENFY_WEBHOOK_SECRET"); } if config.auth && !config.circles.is_empty() { println!(" Configured circles:"); for (circle_name, members) in &config.circles { println!(" - {}: {} members", circle_name, members.len()); } } println!(); let (server_task, _server_handle) = server.spawn_circle_server()?; server_task.await? }