mockforge_ui/handlers/
chains.rs

1//! Chain management proxy handlers
2//!
3//! These handlers proxy chain-related requests from the Admin UI to the main HTTP server
4
5use axum::{
6    extract::{Path, State},
7    http::StatusCode,
8    response::{IntoResponse, Response},
9    Json,
10};
11use serde_json::Value;
12
13use super::AdminState;
14
15/// Proxy chain list requests to the main HTTP server
16pub async fn proxy_chains_list(State(state): State<AdminState>) -> Response {
17    proxy_to_http_server(&state, "/chains", None).await
18}
19
20/// Proxy chain creation requests to the main HTTP server
21pub async fn proxy_chains_create(
22    State(state): State<AdminState>,
23    Json(body): Json<Value>,
24) -> Response {
25    proxy_to_http_server(&state, "/chains", Some(body)).await
26}
27
28/// Proxy get chain requests to the main HTTP server
29pub async fn proxy_chain_get(State(state): State<AdminState>, Path(id): Path<String>) -> Response {
30    proxy_to_http_server(&state, &format!("/chains/{}", id), None).await
31}
32
33/// Proxy chain update requests to the main HTTP server
34pub async fn proxy_chain_update(
35    State(state): State<AdminState>,
36    Path(id): Path<String>,
37    Json(body): Json<Value>,
38) -> Response {
39    proxy_to_http_server(&state, &format!("/chains/{}", id), Some(body)).await
40}
41
42/// Proxy chain delete requests to the main HTTP server
43pub async fn proxy_chain_delete(
44    State(state): State<AdminState>,
45    Path(id): Path<String>,
46) -> Response {
47    proxy_to_http_server(&state, &format!("/chains/{}", id), None).await
48}
49
50/// Proxy chain execute requests to the main HTTP server
51pub async fn proxy_chain_execute(
52    State(state): State<AdminState>,
53    Path(id): Path<String>,
54    Json(body): Json<Value>,
55) -> Response {
56    proxy_to_http_server(&state, &format!("/chains/{}/execute", id), Some(body)).await
57}
58
59/// Proxy chain validate requests to the main HTTP server
60pub async fn proxy_chain_validate(
61    State(state): State<AdminState>,
62    Path(id): Path<String>,
63) -> Response {
64    proxy_to_http_server(&state, &format!("/chains/{}/validate", id), None).await
65}
66
67/// Proxy chain history requests to the main HTTP server
68pub async fn proxy_chain_history(
69    State(state): State<AdminState>,
70    Path(id): Path<String>,
71) -> Response {
72    proxy_to_http_server(&state, &format!("/chains/{}/history", id), None).await
73}
74
75/// Helper function to proxy requests to the main HTTP server
76async fn proxy_to_http_server(state: &AdminState, path: &str, body: Option<Value>) -> Response {
77    let Some(http_addr) = state.http_server_addr else {
78        return (StatusCode::SERVICE_UNAVAILABLE, "HTTP server address not configured")
79            .into_response();
80    };
81
82    let url = format!("http://{}/__mockforge{}", http_addr, path);
83
84    let client = reqwest::Client::new();
85    let mut request_builder = if body.is_some() {
86        client.post(&url)
87    } else {
88        client.get(&url)
89    };
90
91    if let Some(json_body) = body {
92        request_builder = request_builder.json(&json_body);
93    }
94
95    match request_builder.send().await {
96        Ok(response) => {
97            let status = response.status();
98            match response.text().await {
99                Ok(text) => {
100                    // Try to parse as JSON, otherwise return as text
101                    if let Ok(json) = serde_json::from_str::<Value>(&text) {
102                        (
103                            StatusCode::from_u16(status.as_u16()).unwrap_or(StatusCode::OK),
104                            Json(json),
105                        )
106                            .into_response()
107                    } else {
108                        (StatusCode::from_u16(status.as_u16()).unwrap_or(StatusCode::OK), text)
109                            .into_response()
110                    }
111                }
112                Err(e) => {
113                    (StatusCode::INTERNAL_SERVER_ERROR, format!("Failed to read response: {}", e))
114                        .into_response()
115                }
116            }
117        }
118        Err(e) => {
119            (StatusCode::BAD_GATEWAY, format!("Failed to proxy request: {}", e)).into_response()
120        }
121    }
122}
123
124#[cfg(test)]
125mod tests {
126    use super::*;
127    use std::net::SocketAddr;
128
129    fn create_test_state(http_addr: Option<SocketAddr>) -> AdminState {
130        AdminState::new(http_addr, None, None, None, false, 8080)
131    }
132
133    #[tokio::test]
134    async fn test_proxy_to_http_server_no_addr() {
135        let state = create_test_state(None);
136        let response = proxy_to_http_server(&state, "/test", None).await;
137
138        // Response should indicate service unavailable
139        // We can't easily extract status from Response, but we verify it compiles
140        let _ = response;
141    }
142}