2023-06-21 11:47:04 +00:00
|
|
|
#![feature(trait_upcasting)]
|
|
|
|
|
2023-05-11 12:19:36 +00:00
|
|
|
use clap::Parser;
|
|
|
|
use futures::stream::StreamExt;
|
|
|
|
use signal_hook_tokio::Signals;
|
|
|
|
|
2023-07-31 18:46:54 +00:00
|
|
|
use std::path;
|
2023-06-18 12:28:46 +00:00
|
|
|
|
2023-05-11 12:19:36 +00:00
|
|
|
#[derive(Parser, Debug)]
|
|
|
|
#[command(version)]
|
2023-06-25 11:35:59 +00:00
|
|
|
#[command(about = "A domani to another dimension")]
|
2023-05-11 12:19:36 +00:00
|
|
|
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,
|
|
|
|
|
2023-05-18 20:02:57 +00:00
|
|
|
#[arg(
|
|
|
|
long,
|
2023-07-09 14:09:00 +00:00
|
|
|
help = "Path to config file",
|
|
|
|
required = true,
|
|
|
|
env = "DOMANI_CONFIG_PATH"
|
2023-05-18 20:02:57 +00:00
|
|
|
)]
|
2023-07-09 14:09:00 +00:00
|
|
|
config_path: path::PathBuf,
|
2023-07-16 15:43:16 +00:00
|
|
|
|
|
|
|
#[arg(long, help = "Dump the full process configuration to stdout and exit")]
|
|
|
|
dump_config: bool,
|
2023-05-11 12:19:36 +00:00
|
|
|
}
|
|
|
|
|
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())
|
|
|
|
})
|
|
|
|
};
|
|
|
|
|
2023-08-03 09:18:31 +00:00
|
|
|
// inteface_cname is a CNAME record which points to the interface domain of the service.
|
|
|
|
// Since the interface domain _must_ point to the service (otherwise it wouldn't work) it's
|
2023-07-11 17:16:09 +00:00
|
|
|
// reasonable to assume that a CNAME on any domain would suffice to point that domain to
|
|
|
|
// the service.
|
2023-08-03 09:18:31 +00:00
|
|
|
if let Some(ref interface_domain) = config.service.interface_domain {
|
|
|
|
let interface_cname = domani::service::ConfigDNSRecord::CNAME {
|
|
|
|
name: interface_domain.clone(),
|
|
|
|
};
|
|
|
|
|
|
|
|
let dns_records_have_interface_cname = config
|
|
|
|
.service
|
|
|
|
.dns_records
|
|
|
|
.iter()
|
|
|
|
.any(|r| r == &interface_cname);
|
|
|
|
|
|
|
|
if !dns_records_have_interface_cname {
|
|
|
|
log::info!("Adding 'CNAME {interface_domain}' to service.dns_records");
|
|
|
|
config.service.dns_records.push(interface_cname);
|
|
|
|
}
|
2023-07-11 17:16:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
config
|
|
|
|
};
|
|
|
|
|
2023-07-16 15:43:16 +00:00
|
|
|
if cli.dump_config {
|
|
|
|
let stdout = std::io::stdout().lock();
|
|
|
|
serde_yaml::to_writer(stdout, &config).expect("writing config to stdout");
|
|
|
|
return;
|
|
|
|
};
|
|
|
|
|
2023-07-31 18:46:54 +00:00
|
|
|
let gemini_enabled = config.service.gemini.gemini_addr.is_some();
|
|
|
|
|
2023-07-09 14:09:00 +00:00
|
|
|
let origin_store = domani::origin::git::FSStore::new(&config.origin)
|
2023-06-14 18:22:10 +00:00
|
|
|
.expect("git origin store initialization failed");
|
2023-05-11 12:19:36 +00:00
|
|
|
|
2023-07-12 18:25:35 +00:00
|
|
|
let domain_checker = domani::domain::checker::DNSChecker::new(
|
|
|
|
domani::token::MemStore::new(),
|
|
|
|
&config.domain.dns,
|
|
|
|
)
|
|
|
|
.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-05-20 12:51:36 +00:00
|
|
|
|
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");
|
2023-05-18 20:02:57 +00:00
|
|
|
|
2023-06-21 12:02:42 +00:00
|
|
|
Some(
|
2023-07-12 17:01:31 +00:00
|
|
|
domani::domain::acme::manager::ManagerImpl::new(
|
|
|
|
domain_acme_store,
|
2023-07-12 18:25:35 +00:00
|
|
|
domani::token::MemStore::new(),
|
2023-07-12 17:01:31 +00:00
|
|
|
&acme_config,
|
|
|
|
)
|
|
|
|
.await
|
|
|
|
.expect("domain acme manager initialization failed"),
|
2023-06-21 12:02:42 +00:00
|
|
|
)
|
2023-05-20 12:28:02 +00:00
|
|
|
} else {
|
2023-05-20 12:51:36 +00:00
|
|
|
None
|
2023-05-20 12:28:02 +00:00
|
|
|
};
|
2023-05-18 20:02:57 +00:00
|
|
|
|
2023-07-31 18:46:54 +00:00
|
|
|
let domain_gemini_store = if gemini_enabled {
|
|
|
|
Some(
|
|
|
|
domani::domain::gemini::FSStore::new(&config.domain.store_dir_path.join("gemini"))
|
|
|
|
.unwrap_or_else(|e| panic!("domain gemini store initialization failed: {e}")),
|
|
|
|
)
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
};
|
2023-07-27 14:09:44 +00:00
|
|
|
|
2023-07-21 12:43:39 +00:00
|
|
|
let mut task_stack = domani::task_stack::TaskStack::new();
|
2023-06-21 11:15:42 +00:00
|
|
|
|
2023-07-03 11:39:44 +00:00
|
|
|
let domain_manager = domani::domain::manager::ManagerImpl::new(
|
2023-06-21 11:15:42 +00:00
|
|
|
&mut task_stack,
|
2023-05-18 20:02:57 +00:00
|
|
|
origin_store,
|
2023-07-15 17:41:11 +00:00
|
|
|
domain_store,
|
2023-05-18 20:02:57 +00:00
|
|
|
domain_checker,
|
2023-06-21 12:02:42 +00:00
|
|
|
domain_acme_manager,
|
2023-07-31 18:46:54 +00:00
|
|
|
domain_gemini_store,
|
2023-05-18 20:02:57 +00:00
|
|
|
);
|
2023-05-17 12:37:23 +00:00
|
|
|
|
2023-07-31 18:46:54 +00:00
|
|
|
let _ = domani::service::http::Service::new(
|
2023-06-21 11:15:42 +00:00
|
|
|
&mut task_stack,
|
|
|
|
domain_manager.clone(),
|
2023-07-31 18:46:54 +00:00
|
|
|
domani::domain::manager::HttpsCertResolver::from(domain_manager.clone()),
|
2023-07-21 16:10:48 +00:00
|
|
|
config.service.clone(),
|
|
|
|
);
|
|
|
|
|
2023-07-31 18:46:54 +00:00
|
|
|
if gemini_enabled {
|
|
|
|
let _ = domani::service::gemini::Service::new(
|
|
|
|
&mut task_stack,
|
|
|
|
domain_manager.clone(),
|
|
|
|
domani::domain::manager::GeminiCertResolver::from(domain_manager.clone()),
|
|
|
|
config.service,
|
|
|
|
);
|
|
|
|
}
|
2023-06-18 13:57:51 +00:00
|
|
|
|
2023-06-21 11:15:42 +00:00
|
|
|
let mut signals =
|
|
|
|
Signals::new(signal_hook::consts::TERM_SIGNALS).expect("initializing signals failed");
|
2023-06-18 13:57:51 +00:00
|
|
|
|
2023-06-21 11:15:42 +00:00
|
|
|
if (signals.next().await).is_some() {
|
|
|
|
log::info!("Gracefully shutting down...");
|
2023-05-18 20:02:57 +00:00
|
|
|
}
|
|
|
|
|
2023-06-21 11:15:42 +00:00
|
|
|
tokio::spawn(async move {
|
|
|
|
if (signals.next().await).is_some() {
|
|
|
|
log::warn!("Forcefully shutting down");
|
|
|
|
std::process::exit(1);
|
|
|
|
};
|
|
|
|
});
|
|
|
|
|
|
|
|
task_stack
|
2023-06-18 12:28:46 +00:00
|
|
|
.stop()
|
|
|
|
.await
|
2023-06-21 11:15:42 +00:00
|
|
|
.expect("failed to stop all background tasks");
|
2023-06-18 12:28:46 +00:00
|
|
|
|
2023-06-13 19:33:43 +00:00
|
|
|
log::info!("Graceful shutdown complete");
|
2023-05-11 12:19:36 +00:00
|
|
|
}
|