use clap::Parser; use futures::stream::StreamExt; use signal_hook::consts::signal; use signal_hook_tokio::Signals; use tokio::sync::oneshot; use std::net::SocketAddr; use std::path; use std::str::FromStr; #[derive(Parser, Debug)] #[command(version)] #[command(about = "A domiply to another dimension")] struct Cli { #[arg(long, default_value_t = SocketAddr::from_str("127.0.0.1:3030").unwrap(), env = "DOMIPLY_HTTP_LISTEN_ADDR")] http_listen_addr: SocketAddr, #[arg(long, required = true, env = "DOMIPLY_PASSPHRASE")] passphrase: String, #[arg(long, required = true, env = "DOMIPLY_ORIGIN_STORE_GIT_DIR_PATH")] origin_store_git_dir_path: path::PathBuf, #[arg(long, required = true, env = "DOMIPLY_DOMAIN_CHECKER_TARGET_CNAME")] domain_checker_target_cname: domiply::domain::Name, #[arg(long, default_value_t = String::from("1.1.1.1:53"), env = "DOMIPLY_DOMAIN_CHECKER_RESOLVER_ADDR")] domain_checker_resolver_addr: String, #[arg(long, required = true, env = "DOMIPLY_DOMAIN_CONFIG_STORE_DIR_PATH")] domain_config_store_dir_path: path::PathBuf, } #[tokio::main] async fn main() { let config = Cli::parse(); // set up signal handling, stop_ch_rx will be used to signal that the stop signal has been // received let stop_ch_rx = { let mut signals = Signals::new(&[signal::SIGTERM, signal::SIGINT, signal::SIGQUIT]) .expect("initialized signals"); let (stop_ch_tx, stop_ch_rx) = oneshot::channel(); tokio::spawn(async move { if let Some(_) = signals.next().await { println!("Gracefully shutting down..."); let _ = stop_ch_tx.send(()); } if let Some(_) = signals.next().await { println!("Forcefully shutting down"); std::process::exit(1); } }); stop_ch_rx }; let origin_store = domiply::origin::store::git::new(config.origin_store_git_dir_path) .expect("git origin store initialized"); let domain_checker = domiply::domain::checker::new( config.domain_checker_target_cname.clone(), &config.domain_checker_resolver_addr, ) .expect("domain checker initialized"); let domain_config_store = domiply::domain::config::new(&config.domain_config_store_dir_path) .expect("domain config store initialized"); let manager = domiply::domain::manager::new(origin_store, domain_config_store, domain_checker); let service = domiply::service::new( manager, config.domain_checker_target_cname, config.passphrase, ) .expect("service initialized"); let (addr, server) = warp::serve(service).bind_with_graceful_shutdown(config.http_listen_addr, async { stop_ch_rx.await.ok(); }); println!("Listening on {addr}"); tokio::task::spawn(server) .await .expect("server shutdown gracefully"); println!("Graceful shutdown complete"); }