use std::{fmt, sync::Arc, time::Duration};
pub use bestool_canopy as canopy;
pub use bestool_canopy::Redacted;
pub mod backup;
pub mod commands;
mod context;
mod daemon;
pub mod doctor;
pub mod http_server;
mod metrics;
pub mod tasks;
#[cfg(windows)]
pub mod windows_service;
pub use backup::{BackupRegistry, BackupRunner, BackupTask, RunningBackup};
pub use context::InternalContext;
pub use daemon::{run, run_with_shutdown};
pub use tasks::{BackgroundTask, TaskContext, TaskEndpoint, TaskEndpointResponse};
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
pub fn http_builder() -> reqwest::ClientBuilder {
canopy::client_builder(VERSION)
}
pub fn http_client() -> reqwest::Client {
http_builder()
.build()
.expect("failed to build alertd HTTP client")
}
#[derive(Clone)]
pub struct DaemonConfig {
pub pg_pool: Option<bestool_postgres::pool::PgPool>,
pub database_url: Option<String>,
pub device_key_pem: Option<Redacted<String>>,
pub tamanu_version: String,
pub no_server: bool,
pub server_addrs: Vec<std::net::SocketAddr>,
pub watchdog_timeout: Option<Duration>,
pub background_tasks: Vec<Arc<dyn BackgroundTask>>,
pub backups: Option<Arc<BackupRegistry>>,
pub binary_version: String,
}
impl fmt::Debug for DaemonConfig {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("DaemonConfig")
.field("database_url", &self.database_url)
.field("device_key_pem", &self.device_key_pem)
.field("tamanu_version", &self.tamanu_version)
.field("binary_version", &self.binary_version)
.field("no_server", &self.no_server)
.field("server_addrs", &self.server_addrs)
.field("watchdog_timeout", &self.watchdog_timeout)
.field(
"background_tasks",
&self
.background_tasks
.iter()
.map(|t| t.name())
.collect::<Vec<_>>(),
)
.finish()
}
}
impl DaemonConfig {
pub fn new(
pg_pool: Option<bestool_postgres::pool::PgPool>,
database_url: Option<String>,
tamanu_version: String,
) -> Self {
Self {
pg_pool,
database_url,
device_key_pem: None,
tamanu_version,
no_server: false,
server_addrs: Vec::new(),
watchdog_timeout: Some(Duration::from_secs(10 * 60)),
background_tasks: Vec::new(),
backups: None,
binary_version: VERSION.to_string(),
}
}
pub fn with_binary_version(mut self, version: String) -> Self {
self.binary_version = version;
self
}
pub fn with_task(mut self, task: Arc<dyn BackgroundTask>) -> Self {
self.background_tasks.push(task);
self
}
pub fn with_backups(mut self, registry: Arc<BackupRegistry>) -> Self {
self.backups = Some(registry);
self
}
pub fn with_device_key_pem(mut self, pem: String) -> Self {
self.device_key_pem = Some(Redacted(pem));
self
}
pub fn with_no_server(mut self, no_server: bool) -> Self {
self.no_server = no_server;
self
}
pub fn with_server_addrs(mut self, server_addrs: Vec<std::net::SocketAddr>) -> Self {
self.server_addrs = server_addrs;
self
}
pub fn with_watchdog_timeout(mut self, watchdog_timeout: Option<Duration>) -> Self {
self.watchdog_timeout = watchdog_timeout;
self
}
}
pub(crate) struct LogError<'a>(pub &'a miette::Report);
impl fmt::Display for LogError<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use miette::ReportHandler;
let handler = miette::NarratableReportHandler::new();
if let Err(e) = handler.debug(self.0.as_ref(), f) {
write!(f, "{}: {}", self.0, e)
} else {
Ok(())
}
}
}