mod commands;
mod output;
use std::sync::Arc;
use async_trait::async_trait;
use clap::{Parser, Subcommand};
use crate::{
api,
cli::commands::{notify::NotifyCommand, token::TokenCommand},
config, database, dns, log_error, log_info, logger, service, socket,
};
struct DnsNotifySender;
#[async_trait]
impl service::notify::NotifySender for DnsNotifySender {
async fn send_notify(&self, zone_name: Option<&str>) -> Result<(), String> {
dns::xfr::notify::send_notify(zone_name, false)
.await
.map_err(|e| e.to_string())
}
}
#[derive(Parser, Debug)]
#[command(name = "bindizr", version, about)]
pub(crate) struct Args {
#[command(subcommand)]
pub command: Command,
}
#[derive(Subcommand, Debug)]
pub(crate) enum Command {
Start {
#[arg(short, long, value_name = "FILE")]
config: Option<String>,
},
Status,
Token {
#[command(subcommand)]
subcommand: TokenCommand,
},
Get {
#[command(subcommand)]
subcommand: commands::get::GetCommand,
},
Create {
#[command(subcommand)]
subcommand: commands::create::CreateCommand,
},
Delete {
#[command(subcommand)]
subcommand: commands::delete::DeleteCommand,
},
Notify {
#[command(subcommand)]
subcommand: NotifyCommand,
},
}
pub(crate) async fn bootstrap(config_file: Option<&str>) -> Result<(), String> {
if let Some(file) = config_file {
config::initialize(Some(file));
} else {
config::initialize(None);
}
logger::initialize();
service::notify::set_notify_sender(Arc::new(DnsNotifySender)).map_err(String::from)?;
database::initialize().await;
dns::initialize().await;
if config::get_bindizr_config().dns.notify_on_startup {
match dns::xfr::notify::send_notify(None, false).await {
Ok(()) => log_info!("Startup DNS NOTIFY completed."),
Err(e) => log_error!("Startup DNS NOTIFY failed: {}", e),
}
}
log_info!("Bindizr is running in foreground mode.");
log_info!("For production use, please run bindizr as a systemd service:");
log_info!("# systemctl start bindizr");
socket::server::initialize().await?;
api::initialize().await?;
tokio::signal::ctrl_c()
.await
.map_err(|e| format!("Failed to listen for shutdown signal: {}", e))?;
log_info!("Shutdown signal received, exiting gracefully...");
Ok(())
}
pub async fn execute() {
let args = Args::parse();
if let Err(e) = match args.command {
Command::Start { config } => commands::start::handle_command(config).await,
Command::Status => commands::status::handle_command().await,
Command::Token { subcommand } => commands::token::handle_command(subcommand).await,
Command::Get { subcommand } => commands::get::handle_command(subcommand).await,
Command::Create { subcommand } => commands::create::handle_command(subcommand).await,
Command::Delete { subcommand } => commands::delete::handle_command(subcommand).await,
Command::Notify { subcommand } => commands::notify::handle_notify(&subcommand).await,
} {
eprintln!("Error: {}", e);
std::process::exit(1);
}
}