Skip to main content

xbp_cli/api/
mod.rs

1mod handlers;
2mod models;
3
4use actix_web::{web, App, HttpServer};
5use models::{RouteConditions, RouteEntry, RouteTarget};
6use reqwest::Client;
7use std::collections::HashMap;
8use std::env;
9use std::sync::atomic::AtomicUsize;
10use std::sync::Arc;
11use tokio::sync::RwLock;
12use tracing::info;
13
14#[derive(Clone)]
15pub struct RouteRing {
16    pub targets: Vec<RouteTarget>,
17    pub conditions: Option<RouteConditions>,
18    pub cursor: Arc<AtomicUsize>,
19}
20
21impl RouteRing {
22    pub fn new(entry: RouteEntry) -> Self {
23        Self {
24            targets: entry.targets,
25            conditions: entry.conditions,
26            cursor: Arc::new(AtomicUsize::new(0)),
27        }
28    }
29}
30
31#[derive(Clone)]
32pub struct AppState {
33    pub routes: Arc<RwLock<HashMap<String, RouteRing>>>,
34    pub client: Client,
35}
36
37impl AppState {
38    pub fn new() -> Self {
39        AppState {
40            routes: Arc::new(RwLock::new(HashMap::new())),
41            client: Client::new(),
42        }
43    }
44}
45
46impl Default for AppState {
47    fn default() -> Self {
48        Self::new()
49    }
50}
51
52pub async fn start_api_server() -> std::io::Result<()> {
53    let bind_host = env::var("XBP_API_BIND").unwrap_or_else(|_| "127.0.0.1".to_string());
54    let port = env::var("PORT_XBP_API")
55        .unwrap_or_else(|_| "8080".to_string())
56        .parse::<u16>()
57        .unwrap_or(8080);
58
59    info!("Starting XBP API server on {}:{}", bind_host, port);
60
61    let state = web::Data::new(AppState::new());
62
63    HttpServer::new(move || {
64        App::new()
65            .app_data(state.clone())
66            .route("/ports", web::get().to(handlers::list_ports))
67            .route("/ports/{port}", web::get().to(handlers::get_port))
68            .route("/ports/{port}/kill", web::post().to(handlers::kill_port))
69            .route(
70                "/network/floating-ips",
71                web::get().to(handlers::list_network_floating_ips),
72            )
73            .route(
74                "/network/floating-ips",
75                web::post().to(handlers::add_network_floating_ip),
76            )
77            .route(
78                "/network/configs",
79                web::get().to(handlers::list_network_configs),
80            )
81            .route("/systemctl", web::get().to(handlers::list_systemctl))
82            .route(
83                "/systemctl/{service}",
84                web::get().to(handlers::get_systemctl_service),
85            )
86            .route(
87                "/systemctl/{service}/{action}",
88                web::post().to(handlers::systemctl_action),
89            )
90            .route("/pm2", web::get().to(handlers::list_pm2))
91            .route("/pm2/{name}", web::delete().to(handlers::delete_pm2))
92            .route("/pm2/{name}/start", web::post().to(handlers::start_pm2))
93            .route("/pm2/{name}/stop", web::post().to(handlers::stop_pm2))
94            .route("/pm2/{name}/restart", web::post().to(handlers::restart_pm2))
95            .route("/services", web::get().to(handlers::list_services))
96            .route(
97                "/services/{name}/{command}",
98                web::post().to(handlers::run_service_command),
99            )
100            .route("/config", web::get().to(handlers::get_config))
101            .route("/logs", web::get().to(handlers::get_logs))
102            .route(
103                "/install/{package}",
104                web::post().to(handlers::install_package),
105            )
106            .route("/setup", web::post().to(handlers::setup))
107            .route("/redeploy", web::post().to(handlers::redeploy))
108            .route(
109                "/redeploy/{service_name}",
110                web::post().to(handlers::redeploy_service),
111            )
112            .route(
113                "/binary/download",
114                web::post().to(handlers::download_and_run_binary),
115            )
116            .route(
117                "/openapi/download",
118                web::get().to(handlers::download_openapi),
119            )
120            .route("/health", web::get().to(handlers::health))
121            .route("/commands/exec", web::post().to(handlers::exec_command))
122            .route("/routes", web::get().to(handlers::list_routes))
123            .route("/routes", web::post().to(handlers::create_route))
124            .route("/routes/{domain}", web::delete().to(handlers::delete_route))
125            .route("/proxy/{domain}/{tail:.*}", web::to(handlers::proxy_route))
126            .route("/metrics", web::get().to(handlers::metrics))
127    })
128    .bind((bind_host, port))?
129    .run()
130    .await
131}