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}