use crate::logging::{log_error, log_info, log_success};
use std::path::PathBuf;
use tokio::process::Command;
use tracing::debug;
pub async fn install_api_service(port: u16, debug: bool) -> Result<(), String> {
let exe = std::env::current_exe()
.map_err(|e| format!("Failed to resolve current executable: {}", e))?;
let exe_dir: PathBuf = exe
.parent()
.map(|p| p.to_path_buf())
.unwrap_or_else(|| PathBuf::from("."));
let unit = format!(
"[Unit]\nDescription=XBP API Server\nAfter=network.target\n\n[Service]\nEnvironment=XBP_API_BIND=127.0.0.1\nEnvironment=PORT_XBP_API={port}\nWorkingDirectory={}\nExecStart={}\nRestart=on-failure\n\n[Install]\nWantedBy=multi-user.target\n",
exe_dir.display(),
exe.display()
);
let unit_path = "/etc/systemd/system/xbp-api.service";
let _ = log_info("api", "Writing systemd unit (requires sudo)", None).await;
let write_cmd = format!(
"echo \"{}\" | sudo tee {}",
unit.replace('\"', "\\\""),
unit_path
);
if debug {
debug!("Writing unit with: {}", write_cmd);
}
let write_output = Command::new("bash")
.arg("-c")
.arg(&write_cmd)
.output()
.await
.map_err(|e| format!("Failed to write unit file: {}", e))?;
if !write_output.status.success() {
return Err(format!(
"Writing unit failed: {}",
String::from_utf8_lossy(&write_output.stderr)
));
}
let _ = Command::new("sudo")
.args(["systemctl", "daemon-reload"])
.status()
.await
.map_err(|e| format!("Failed to daemon-reload: {}", e))?;
let enable_status = Command::new("sudo")
.args(["systemctl", "enable", "--now", "xbp-api.service"])
.status()
.await
.map_err(|e| format!("Failed to enable service: {}", e))?;
if !enable_status.success() {
let _ = log_error(
"api",
"Failed to enable xbp-api.service",
Some(&format!("{:?}", enable_status)),
)
.await;
return Err("systemctl enable failed".into());
}
let _ = log_success(
"api",
&format!(
"Installed and started xbp-api.service on PORT_XBP_API={}",
port
),
None,
)
.await;
Ok(())
}