xbp 10.17.1

XBP is a zero-config build pack that can also interact with proxies, kafka, sockets, synthetic monitors.
Documentation
mod handlers;
mod models;

use actix_web::{web, App, HttpServer};
use models::{RouteConditions, RouteEntry, RouteTarget};
use reqwest::Client;
use std::collections::HashMap;
use std::env;
use std::sync::atomic::AtomicUsize;
use std::sync::Arc;
use tokio::sync::RwLock;
use tracing::info;

#[derive(Clone)]
pub struct RouteRing {
    pub targets: Vec<RouteTarget>,
    pub conditions: Option<RouteConditions>,
    pub cursor: Arc<AtomicUsize>,
}

impl RouteRing {
    pub fn new(entry: RouteEntry) -> Self {
        Self {
            targets: entry.targets,
            conditions: entry.conditions,
            cursor: Arc::new(AtomicUsize::new(0)),
        }
    }
}

#[derive(Clone)]
pub struct AppState {
    pub routes: Arc<RwLock<HashMap<String, RouteRing>>>,
    pub client: Client,
}

impl AppState {
    pub fn new() -> Self {
        AppState {
            routes: Arc::new(RwLock::new(HashMap::new())),
            client: Client::new(),
        }
    }
}

impl Default for AppState {
    fn default() -> Self {
        Self::new()
    }
}

pub async fn start_api_server() -> std::io::Result<()> {
    let bind_host = env::var("XBP_API_BIND").unwrap_or_else(|_| "127.0.0.1".to_string());
    let port = env::var("PORT_XBP_API")
        .unwrap_or_else(|_| "8080".to_string())
        .parse::<u16>()
        .unwrap_or(8080);

    info!("Starting XBP API server on {}:{}", bind_host, port);

    let state = web::Data::new(AppState::new());

    HttpServer::new(move || {
        App::new()
            .app_data(state.clone())
            .route("/ports", web::get().to(handlers::list_ports))
            .route("/ports/{port}", web::get().to(handlers::get_port))
            .route("/ports/{port}/kill", web::post().to(handlers::kill_port))
            .route(
                "/network/floating-ips",
                web::get().to(handlers::list_network_floating_ips),
            )
            .route(
                "/network/floating-ips",
                web::post().to(handlers::add_network_floating_ip),
            )
            .route(
                "/network/configs",
                web::get().to(handlers::list_network_configs),
            )
            .route("/systemctl", web::get().to(handlers::list_systemctl))
            .route(
                "/systemctl/{service}",
                web::get().to(handlers::get_systemctl_service),
            )
            .route(
                "/systemctl/{service}/{action}",
                web::post().to(handlers::systemctl_action),
            )
            .route("/pm2", web::get().to(handlers::list_pm2))
            .route("/pm2/{name}", web::delete().to(handlers::delete_pm2))
            .route("/pm2/{name}/start", web::post().to(handlers::start_pm2))
            .route("/pm2/{name}/stop", web::post().to(handlers::stop_pm2))
            .route("/pm2/{name}/restart", web::post().to(handlers::restart_pm2))
            .route("/services", web::get().to(handlers::list_services))
            .route(
                "/services/{name}/{command}",
                web::post().to(handlers::run_service_command),
            )
            .route("/config", web::get().to(handlers::get_config))
            .route("/logs", web::get().to(handlers::get_logs))
            .route(
                "/install/{package}",
                web::post().to(handlers::install_package),
            )
            .route("/setup", web::post().to(handlers::setup))
            .route("/redeploy", web::post().to(handlers::redeploy))
            .route(
                "/redeploy/{service_name}",
                web::post().to(handlers::redeploy_service),
            )
            .route(
                "/binary/download",
                web::post().to(handlers::download_and_run_binary),
            )
            .route(
                "/openapi/download",
                web::get().to(handlers::download_openapi),
            )
            .route("/health", web::get().to(handlers::health))
            .route("/commands/exec", web::post().to(handlers::exec_command))
            .route("/routes", web::get().to(handlers::list_routes))
            .route("/routes", web::post().to(handlers::create_route))
            .route("/routes/{domain}", web::delete().to(handlers::delete_route))
            .route("/proxy/{domain}/{tail:.*}", web::to(handlers::proxy_route))
            .route("/metrics", web::get().to(handlers::metrics))
    })
    .bind((bind_host, port))?
    .run()
    .await
}