xbp 10.15.4

XBP is a zero-config build pack that can also interact with proxies, kafka, sockets, synthetic monitors.
Documentation
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;

    // We need to write contents; tee above didn't receive input. Use bash -c to echo content.
    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(())
}