Skip to main content

minifly_litefs/
server.rs

1use axum::{
2    extract::{Path, State as AxumState, Query},
3    routing::{get, post, delete},
4    Json, Router,
5};
6use serde::{Deserialize, Serialize};
7use std::sync::Arc;
8use std::collections::HashMap;
9use crate::manager::LiteFSManager;
10use minifly_core::Error;
11use crate::Result;
12
13#[derive(Clone)]
14pub struct ServerState {
15    manager: Arc<LiteFSManager>,
16}
17
18#[derive(Debug, Serialize)]
19pub struct LiteFSStatus {
20    pub machine_id: String,
21    pub is_running: bool,
22    pub is_primary: bool,
23    pub mount_path: String,
24    pub proxy_url: String,
25}
26
27#[derive(Debug, Deserialize)]
28pub struct StartRequest {
29    pub is_primary: bool,
30}
31
32#[derive(Debug, Serialize)]
33pub struct ApiResponse<T> {
34    pub success: bool,
35    #[serde(skip_serializing_if = "Option::is_none")]
36    pub data: Option<T>,
37    #[serde(skip_serializing_if = "Option::is_none")]
38    pub error: Option<String>,
39}
40
41pub fn create_router(manager: Arc<LiteFSManager>) -> Router {
42    let state = ServerState { manager };
43    
44    Router::new()
45        .route("/health", get(health_check))
46        .route("/instances", get(list_instances))
47        .route("/instances/:machine_id", get(get_instance))
48        .route("/instances/:machine_id/start", post(start_instance))
49        .route("/instances/:machine_id/stop", post(stop_instance))
50        .route("/instances/:machine_id/status", get(get_status))
51        .with_state(state)
52}
53
54async fn health_check() -> Json<ApiResponse<String>> {
55    Json(ApiResponse {
56        success: true,
57        data: Some("LiteFS server is running".to_string()),
58        error: None,
59    })
60}
61
62async fn list_instances(
63    AxumState(state): AxumState<ServerState>,
64) -> Json<ApiResponse<Vec<String>>> {
65    // TODO: Implement listing of all LiteFS instances
66    Json(ApiResponse {
67        success: true,
68        data: Some(vec![]),
69        error: None,
70    })
71}
72
73async fn get_instance(
74    AxumState(state): AxumState<ServerState>,
75    Path(machine_id): Path<String>,
76) -> Json<ApiResponse<LiteFSStatus>> {
77    let is_running = state.manager.is_running(&machine_id).await;
78    
79    if !is_running {
80        return Json(ApiResponse {
81            success: false,
82            data: None,
83            error: Some("LiteFS instance not found".to_string()),
84        });
85    }
86    
87    let status = LiteFSStatus {
88        machine_id: machine_id.clone(),
89        is_running,
90        is_primary: true, // TODO: Get actual primary status
91        mount_path: state.manager.get_mount_path(&machine_id).to_string_lossy().to_string(),
92        proxy_url: state.manager.get_proxy_url(&machine_id),
93    };
94    
95    Json(ApiResponse {
96        success: true,
97        data: Some(status),
98        error: None,
99    })
100}
101
102async fn start_instance(
103    AxumState(state): AxumState<ServerState>,
104    Path(machine_id): Path<String>,
105    Json(req): Json<StartRequest>,
106) -> Json<ApiResponse<LiteFSStatus>> {
107    match state.manager.start_for_machine(&machine_id, req.is_primary).await {
108        Ok(_) => {
109            let status = LiteFSStatus {
110                machine_id: machine_id.clone(),
111                is_running: true,
112                is_primary: req.is_primary,
113                mount_path: state.manager.get_mount_path(&machine_id).to_string_lossy().to_string(),
114                proxy_url: state.manager.get_proxy_url(&machine_id),
115            };
116            
117            Json(ApiResponse {
118                success: true,
119                data: Some(status),
120                error: None,
121            })
122        }
123        Err(e) => {
124            Json(ApiResponse {
125                success: false,
126                data: None,
127                error: Some(e.to_string()),
128            })
129        }
130    }
131}
132
133async fn stop_instance(
134    AxumState(state): AxumState<ServerState>,
135    Path(machine_id): Path<String>,
136) -> Json<ApiResponse<String>> {
137    match state.manager.stop_for_machine(&machine_id).await {
138        Ok(_) => {
139            Json(ApiResponse {
140                success: true,
141                data: Some(format!("LiteFS instance {} stopped", machine_id)),
142                error: None,
143            })
144        }
145        Err(e) => {
146            Json(ApiResponse {
147                success: false,
148                data: None,
149                error: Some(e.to_string()),
150            })
151        }
152    }
153}
154
155async fn get_status(
156    AxumState(state): AxumState<ServerState>,
157    Path(machine_id): Path<String>,
158) -> Json<ApiResponse<bool>> {
159    let is_running = state.manager.is_running(&machine_id).await;
160    
161    Json(ApiResponse {
162        success: true,
163        data: Some(is_running),
164        error: None,
165    })
166}
167
168pub async fn run_server(manager: Arc<LiteFSManager>, port: u16) -> Result<()> {
169    let app = create_router(manager);
170    
171    let addr = std::net::SocketAddr::from(([0, 0, 0, 0], port));
172    
173    let listener = tokio::net::TcpListener::bind(addr).await
174        .map_err(|e| Error::LiteFSError(format!("Failed to bind to port {}: {}", port, e)))?;
175    
176    tracing::info!("LiteFS HTTP server listening on {}", addr);
177    
178    axum::serve(listener, app).await
179        .map_err(|e| Error::LiteFSError(format!("Server error: {}", e)))?;
180    
181    Ok(())
182}