domani/src/main.rs

175 lines
5.1 KiB
Rust
Raw Normal View History

#![feature(trait_upcasting)]
use clap::Parser;
use futures::stream::StreamExt;
use signal_hook_tokio::Signals;
use std::path;
#[derive(Parser, Debug)]
#[command(version)]
2023-06-25 11:35:59 +00:00
#[command(about = "A domani to another dimension")]
struct Cli {
2023-06-13 19:33:43 +00:00
#[arg(
long,
help = "OFF, ERROR, WARN, INFO, DEBUG, or TRACE",
default_value_t = log::LevelFilter::Info,
2023-06-25 11:35:59 +00:00
env = "DOMANI_LOG_LEVEL"
2023-06-13 19:33:43 +00:00
)]
log_level: log::LevelFilter,
2023-06-25 11:35:59 +00:00
#[arg(long, default_value_t = false, env = "DOMANI_LOG_TIMESTAMP")]
2023-06-13 19:33:43 +00:00
log_timestamp: bool,
#[arg(
long,
2023-07-09 14:09:00 +00:00
help = "Path to config file",
required = true,
env = "DOMANI_CONFIG_PATH"
)]
2023-07-09 14:09:00 +00:00
config_path: path::PathBuf,
#[arg(long, help = "Dump the full process configuration to stdout and exit")]
dump_config: bool,
}
2023-05-20 12:28:02 +00:00
#[tokio::main]
async fn main() {
2023-07-09 14:09:00 +00:00
let cli = Cli::parse();
2023-06-13 19:33:43 +00:00
env_logger::Builder::new()
2023-07-09 14:09:00 +00:00
.filter_level(cli.log_level)
2023-06-13 19:33:43 +00:00
.format_timestamp(
2023-07-09 14:09:00 +00:00
cli.log_timestamp
2023-06-13 19:33:43 +00:00
.then_some(env_logger::TimestampPrecision::Micros),
)
.init();
2023-07-11 17:16:09 +00:00
let config = {
let mut config: domani::config::Config = {
let path = &cli.config_path;
let f = std::fs::File::open(path).unwrap_or_else(|e| {
panic!("failed to open config file at {}: {e}", path.display())
});
serde_yaml::from_reader(f).unwrap_or_else(|e| {
panic!("failed to parse config file at {}: {e}", path.display())
})
};
// primary_cname is a CNAME record which points to the primary domain of the service. Since
// the primary domain _must_ point to the service (otherwise HTTPS wouldn't work) it's
// reasonable to assume that a CNAME on any domain would suffice to point that domain to
// the service.
let primary_cname = domani::service::ConfigDNSRecord::CNAME {
name: config.service.primary_domain.clone(),
};
let dns_records_have_primary_cname = config
.service
.dns_records
.iter()
.any(|r| r == &primary_cname);
if !dns_records_have_primary_cname {
log::info!(
"Adding 'CNAME {}' to service.dns_records",
&config.service.primary_domain
);
config.service.dns_records.push(primary_cname);
}
config
};
if cli.dump_config {
let stdout = std::io::stdout().lock();
serde_yaml::to_writer(stdout, &config).expect("writing config to stdout");
return;
};
2023-07-09 14:09:00 +00:00
let origin_store = domani::origin::git::FSStore::new(&config.origin)
.expect("git origin store initialization failed");
let domain_checker = domani::domain::checker::DNSChecker::new(
domani::token::MemStore::new(),
&config.domain.dns,
config.service.primary_domain.clone(),
)
.await
.expect("domain checker initialization failed");
2023-05-17 12:37:23 +00:00
2023-07-15 17:41:11 +00:00
let domain_store =
2023-07-09 14:09:00 +00:00
domani::domain::store::FSStore::new(&config.domain.store_dir_path.join("domains"))
2023-06-29 14:54:55 +00:00
.expect("domain config store initialization failed");
2023-05-17 12:37:23 +00:00
2023-07-15 17:41:11 +00:00
let domain_store =
domani::domain::store::StoreWithBuiltin::new(domain_store, config.domain.builtins);
2023-07-09 14:09:00 +00:00
let domain_acme_manager = if config.service.http.https_addr.is_some() {
let acme_config = config
.domain
2023-07-09 13:08:33 +00:00
.acme
.expect("acme configuration must be set if https is enabled");
2023-06-29 14:54:55 +00:00
let domain_acme_store =
2023-07-09 14:09:00 +00:00
domani::domain::acme::store::FSStore::new(&config.domain.store_dir_path.join("acme"))
2023-06-29 14:54:55 +00:00
.expect("domain acme store initialization failed");
Some(
domani::domain::acme::manager::ManagerImpl::new(
domain_acme_store,
domani::token::MemStore::new(),
&acme_config,
)
.await
.expect("domain acme manager initialization failed"),
)
2023-05-20 12:28:02 +00:00
} else {
None
2023-05-20 12:28:02 +00:00
};
2023-07-21 12:43:39 +00:00
let mut task_stack = domani::task_stack::TaskStack::new();
let domain_manager = domani::domain::manager::ManagerImpl::new(
&mut task_stack,
origin_store,
2023-07-15 17:41:11 +00:00
domain_store,
domain_checker,
domain_acme_manager,
);
2023-05-17 12:37:23 +00:00
2023-06-25 11:35:59 +00:00
let _ = domani::service::http::new(
&mut task_stack,
domain_manager.clone(),
2023-07-09 11:43:38 +00:00
domain_manager.clone(),
config.service.clone(),
);
let _ = domani::service::gemini::Service::new(
&mut task_stack,
domain_manager.clone(),
2023-07-09 14:09:00 +00:00
config.service,
);
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");
2023-06-13 19:33:43 +00:00
log::info!("Graceful shutdown complete");
}