kittynode_web/
lib.rs

1use axum::{
2    Router,
3    extract::{Path, Query},
4    http::StatusCode,
5    response::Json,
6    routing::{get, post},
7};
8use eyre::Result;
9use kittynode_core::api;
10use kittynode_core::api::DockerStartStatus;
11use kittynode_core::api::types::{
12    Config, LogsQuery, OperationalState, Package, PackageConfig, SystemInfo,
13};
14use serde::Deserialize;
15use std::collections::HashMap;
16use tokio::net::TcpListener;
17
18pub async fn hello_world() -> &'static str {
19    "Hello World!"
20}
21
22pub async fn add_capability(Path(name): Path<String>) -> Result<StatusCode, (StatusCode, String)> {
23    api::add_capability(&name).map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
24    Ok(StatusCode::OK)
25}
26
27pub async fn remove_capability(
28    Path(name): Path<String>,
29) -> Result<StatusCode, (StatusCode, String)> {
30    api::remove_capability(&name)
31        .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
32    Ok(StatusCode::OK)
33}
34
35pub async fn get_capabilities() -> Result<Json<Vec<String>>, (StatusCode, String)> {
36    api::get_capabilities()
37        .map(Json)
38        .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))
39}
40
41pub async fn get_packages() -> Result<Json<HashMap<String, Package>>, (StatusCode, String)> {
42    api::get_packages()
43        .map(Json)
44        .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))
45}
46
47pub async fn get_config() -> Result<Json<Config>, (StatusCode, String)> {
48    api::get_config()
49        .map(Json)
50        .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))
51}
52
53pub async fn install_package(Path(name): Path<String>) -> Result<StatusCode, (StatusCode, String)> {
54    api::install_package(&name)
55        .await
56        .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
57    Ok(StatusCode::OK)
58}
59
60#[derive(Default, Deserialize)]
61pub struct DeletePackageQuery {
62    include_images: Option<bool>,
63}
64
65pub async fn delete_package(
66    Path(name): Path<String>,
67    Query(params): Query<DeletePackageQuery>,
68) -> Result<StatusCode, (StatusCode, String)> {
69    let include_images = params.include_images.unwrap_or(false);
70    api::delete_package(&name, include_images)
71        .await
72        .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
73    Ok(StatusCode::OK)
74}
75
76pub async fn get_installed_packages() -> Result<Json<Vec<Package>>, (StatusCode, String)> {
77    api::get_installed_packages()
78        .await
79        .map(Json)
80        .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))
81}
82
83pub async fn is_docker_running() -> Result<StatusCode, (StatusCode, String)> {
84    match api::is_docker_running().await {
85        true => Ok(StatusCode::OK),
86        false => Err((
87            StatusCode::SERVICE_UNAVAILABLE,
88            "Docker is not running".to_string(),
89        )),
90    }
91}
92
93pub async fn init_kittynode() -> Result<StatusCode, (StatusCode, String)> {
94    api::init_kittynode().map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
95    Ok(StatusCode::OK)
96}
97
98pub async fn delete_kittynode() -> Result<StatusCode, (StatusCode, String)> {
99    api::delete_kittynode().map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
100    Ok(StatusCode::OK)
101}
102
103pub async fn get_system_info() -> Result<Json<SystemInfo>, (StatusCode, String)> {
104    api::get_system_info()
105        .map(Json)
106        .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))
107}
108
109pub async fn get_container_logs(
110    Path(container_name): Path<String>,
111    Query(params): Query<LogsQuery>,
112) -> Result<Json<Vec<String>>, (StatusCode, String)> {
113    api::get_container_logs(&container_name, params.tail)
114        .await
115        .map(Json)
116        .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))
117}
118
119pub async fn get_package_config(
120    Path(name): Path<String>,
121) -> Result<Json<PackageConfig>, (StatusCode, String)> {
122    api::get_package_config(&name)
123        .await
124        .map(Json)
125        .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))
126}
127
128pub async fn update_package_config(
129    Path(name): Path<String>,
130    Json(config): Json<PackageConfig>,
131) -> Result<StatusCode, (StatusCode, String)> {
132    api::update_package_config(&name, config)
133        .await
134        .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
135    Ok(StatusCode::OK)
136}
137
138pub async fn start_docker_if_needed() -> Result<Json<DockerStartStatus>, (StatusCode, String)> {
139    api::start_docker_if_needed()
140        .await
141        .map(Json)
142        .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))
143}
144
145pub async fn get_operational_state() -> Result<Json<OperationalState>, (StatusCode, String)> {
146    api::get_operational_state()
147        .await
148        .map(Json)
149        .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))
150}
151
152pub fn app() -> Router {
153    Router::new()
154        .route("/", get(hello_world))
155        .route("/add_capability/:name", post(add_capability))
156        .route("/remove_capability/:name", post(remove_capability))
157        .route("/get_capabilities", get(get_capabilities))
158        .route("/get_packages", get(get_packages))
159        .route("/get_config", get(get_config))
160        .route("/install_package/:name", post(install_package))
161        .route("/delete_package/:name", post(delete_package))
162        .route("/get_installed_packages", get(get_installed_packages))
163        .route("/is_docker_running", get(is_docker_running))
164        .route("/init_kittynode", post(init_kittynode))
165        .route("/delete_kittynode", post(delete_kittynode))
166        .route("/get_system_info", get(get_system_info))
167        .route("/logs/:container_name", get(get_container_logs))
168        .route("/get_package_config/:name", get(get_package_config))
169        .route("/update_package_config/:name", post(update_package_config))
170        .route("/start_docker_if_needed", post(start_docker_if_needed))
171        .route("/get_operational_state", get(get_operational_state))
172}
173
174pub async fn run() -> Result<()> {
175    tracing_subscriber::fmt::init();
176    let app = app();
177    let listener = TcpListener::bind("0.0.0.0:3000").await?;
178    axum::serve(listener, app).await?;
179    Ok(())
180}