170 lines
5.3 KiB
Rust
170 lines
5.3 KiB
Rust
#![feature(trait_upcasting)]
|
|
|
|
use clap::Parser;
|
|
use futures::stream::StreamExt;
|
|
use signal_hook_tokio::Signals;
|
|
|
|
use std::net::SocketAddr;
|
|
use std::path;
|
|
use std::str::FromStr;
|
|
|
|
#[derive(Parser, Debug)]
|
|
#[command(version)]
|
|
#[command(about = "A domani to another dimension")]
|
|
struct Cli {
|
|
#[arg(
|
|
long,
|
|
help = "OFF, ERROR, WARN, INFO, DEBUG, or TRACE",
|
|
default_value_t = log::LevelFilter::Info,
|
|
env = "DOMANI_LOG_LEVEL"
|
|
)]
|
|
log_level: log::LevelFilter,
|
|
|
|
#[arg(long, default_value_t = false, env = "DOMANI_LOG_TIMESTAMP")]
|
|
log_timestamp: bool,
|
|
|
|
#[arg(long, required = true, env = "DOMANI_HTTP_DOMAIN")]
|
|
http_domain: domani::domain::Name,
|
|
|
|
#[arg(long, default_value_t = SocketAddr::from_str("[::]:3030").unwrap(), env = "DOMANI_HTTP_LISTEN_ADDR")]
|
|
http_listen_addr: SocketAddr,
|
|
|
|
#[arg(
|
|
long,
|
|
help = "E.g. '[::]:443', if given then SSL certs will automatically be retrieved for all domains using LetsEncrypt",
|
|
env = "DOMANI_HTTPS_LISTEN_ADDR",
|
|
requires = "domain_acme_contact_email"
|
|
)]
|
|
https_listen_addr: Option<SocketAddr>,
|
|
|
|
#[arg(long, required = true, env = "DOMANI_PASSPHRASE")]
|
|
passphrase: String,
|
|
|
|
#[arg(long, required = true, env = "DOMANI_ORIGIN_STORE_GIT_DIR_PATH")]
|
|
origin_store_git_dir_path: path::PathBuf,
|
|
|
|
#[arg(long, required = true, env = "DOMANI_DOMAIN_CHECKER_TARGET_A")]
|
|
domain_checker_target_a: std::net::Ipv4Addr,
|
|
|
|
#[arg(long, default_value_t = String::from("1.1.1.1:53"), env = "DOMANI_DOMAIN_CHECKER_RESOLVER_ADDR")]
|
|
domain_checker_resolver_addr: String,
|
|
|
|
#[arg(long, required = true, env = "DOMANI_DOMAIN_CONFIG_STORE_DIR_PATH")]
|
|
domain_config_store_dir_path: path::PathBuf,
|
|
|
|
#[arg(long, env = "DOMANI_DOMAIN_ACME_CONTACT_EMAIL")]
|
|
domain_acme_contact_email: Option<String>,
|
|
}
|
|
|
|
#[tokio::main]
|
|
async fn main() {
|
|
let config = Cli::parse();
|
|
|
|
env_logger::Builder::new()
|
|
.filter_level(config.log_level)
|
|
.format_timestamp(
|
|
config
|
|
.log_timestamp
|
|
.then_some(env_logger::TimestampPrecision::Micros),
|
|
)
|
|
.init();
|
|
|
|
let origin_config = domani::origin::Config {
|
|
store_dir_path: config.origin_store_git_dir_path,
|
|
};
|
|
|
|
let domain_config = domani::domain::Config {
|
|
store_dir_path: config.domain_config_store_dir_path,
|
|
dns: domani::domain::ConfigDNS {
|
|
resolver_addr: std::net::SocketAddr::from_str(
|
|
config.domain_checker_resolver_addr.as_str(),
|
|
)
|
|
.expect("parsing resolver addr"),
|
|
|
|
target_records: vec![domani::domain::ConfigDNSTargetRecord::A {
|
|
addr: config.domain_checker_target_a,
|
|
}],
|
|
},
|
|
acme: config
|
|
.https_listen_addr
|
|
.and(Some(domani::domain::ConfigACME {
|
|
contact_email: config.domain_acme_contact_email.unwrap(),
|
|
})),
|
|
};
|
|
|
|
let service_http_config = domani::service::http::Config {
|
|
http_addr: config.http_listen_addr,
|
|
https_addr: config.https_listen_addr,
|
|
primary_domain: config.http_domain,
|
|
target_a: config.domain_checker_target_a,
|
|
passphrase: config.passphrase,
|
|
};
|
|
|
|
let origin_store = domani::origin::git::FSStore::new(&origin_config)
|
|
.expect("git origin store initialization failed");
|
|
|
|
let domain_checker = domani::domain::checker::DNSChecker::new(&domain_config.dns)
|
|
.await
|
|
.expect("domain checker initialization failed");
|
|
|
|
let domain_config_store =
|
|
domani::domain::store::FSStore::new(&domain_config.store_dir_path.join("domains"))
|
|
.expect("domain config store initialization failed");
|
|
|
|
let domain_acme_manager = if service_http_config.https_addr.is_some() {
|
|
let acme_config = domain_config
|
|
.acme
|
|
.expect("acme configuration must be set if https is enabled");
|
|
|
|
let domain_acme_store =
|
|
domani::domain::acme::store::FSStore::new(&domain_config.store_dir_path.join("acme"))
|
|
.expect("domain acme store initialization failed");
|
|
|
|
Some(
|
|
domani::domain::acme::manager::ManagerImpl::new(domain_acme_store, &acme_config)
|
|
.await
|
|
.expect("domain acme manager initialization failed"),
|
|
)
|
|
} else {
|
|
None
|
|
};
|
|
|
|
let mut task_stack = domani::util::TaskStack::new();
|
|
|
|
let domain_manager = domani::domain::manager::ManagerImpl::new(
|
|
&mut task_stack,
|
|
origin_store,
|
|
domain_config_store,
|
|
domain_checker,
|
|
domain_acme_manager,
|
|
);
|
|
|
|
let _ = domani::service::http::new(
|
|
&mut task_stack,
|
|
domain_manager.clone(),
|
|
domain_manager.clone(),
|
|
service_http_config,
|
|
);
|
|
|
|
let mut signals =
|
|
Signals::new(signal_hook::consts::TERM_SIGNALS).expect("initializing signals failed");
|
|
|
|
if (signals.next().await).is_some() {
|
|
log::info!("Gracefully shutting down...");
|
|
}
|
|
|
|
tokio::spawn(async move {
|
|
if (signals.next().await).is_some() {
|
|
log::warn!("Forcefully shutting down");
|
|
std::process::exit(1);
|
|
};
|
|
});
|
|
|
|
task_stack
|
|
.stop()
|
|
.await
|
|
.expect("failed to stop all background tasks");
|
|
|
|
log::info!("Graceful shutdown complete");
|
|
}
|