1use axum::{
9 extract::{Path, State},
10 http::StatusCode,
11 Json,
12};
13use serde::{Deserialize, Serialize};
14use serde_json::{json, Value};
15
16use crate::handlers::AdminState;
17use crate::models::ApiResponse;
18
19fn encode_path_segment(s: &str) -> String {
22 s.replace('/', "%2F")
24 .replace('*', "%2A")
25 .replace('?', "%3F")
26 .replace('#', "%23")
27 .replace('[', "%5B")
28 .replace(']', "%5D")
29}
30
31async fn proxy_to_http_server(
33 state: &AdminState,
34 path: &str,
35 body: Option<Value>,
36 method: &str,
37) -> Result<Json<ApiResponse<Value>>, StatusCode> {
38 let http_addr = match state.http_server_addr {
39 Some(addr) => addr,
40 None => {
41 return Ok(Json(ApiResponse::error("HTTP server not available".to_string())));
42 }
43 };
44
45 let url = format!("http://{}/__mockforge/api{}", http_addr, path);
46
47 let client = reqwest::Client::new();
48 let mut request = match (method, body.is_some()) {
49 ("PUT", _) => client.put(&url),
50 ("POST", _) => client.post(&url),
51 _ => client.get(&url),
52 };
53
54 if let Some(body_value) = body {
55 request = request.json(&body_value);
56 }
57
58 match request.send().await {
59 Ok(response) => {
60 let status = response.status();
61 match response.json::<Value>().await {
62 Ok(data) => {
63 if status.is_success() {
64 Ok(Json(ApiResponse::success(data)))
65 } else {
66 Ok(Json(ApiResponse::error(
67 data.get("error")
68 .and_then(|v| v.as_str())
69 .unwrap_or("Request failed")
70 .to_string(),
71 )))
72 }
73 }
74 Err(e) => {
75 tracing::error!("Failed to parse response: {}", e);
76 Ok(Json(ApiResponse::error(format!("Failed to parse response: {}", e))))
77 }
78 }
79 }
80 Err(e) => {
81 tracing::error!("Failed to proxy request: {}", e);
82 Ok(Json(ApiResponse::error(format!("Failed to connect to HTTP server: {}", e))))
83 }
84 }
85}
86
87#[derive(Debug, Deserialize)]
89pub struct SetRouteMigrationRequest {
90 pub mode: String,
92}
93
94#[derive(Debug, Deserialize)]
96pub struct SetGroupMigrationRequest {
97 pub mode: String,
99}
100
101#[derive(Debug, Serialize)]
103pub struct MigrationStatus {
104 pub total_routes: usize,
106 pub mock_routes: usize,
108 pub shadow_routes: usize,
110 pub real_routes: usize,
112 pub auto_routes: usize,
114 pub total_groups: usize,
116 pub migration_enabled: bool,
118}
119
120pub async fn get_migration_routes(
122 State(state): State<AdminState>,
123) -> Result<Json<ApiResponse<Value>>, StatusCode> {
124 proxy_to_http_server(&state, "/migration/routes", None, "GET").await
125}
126
127pub async fn toggle_route_migration(
129 State(state): State<AdminState>,
130 Path(pattern): Path<String>,
131) -> Result<Json<ApiResponse<Value>>, StatusCode> {
132 let encoded_pattern = encode_path_segment(&pattern);
133 proxy_to_http_server(
134 &state,
135 &format!("/migration/routes/{}/toggle", encoded_pattern),
136 None,
137 "POST",
138 )
139 .await
140}
141
142pub async fn set_route_migration_mode(
144 State(state): State<AdminState>,
145 Path(pattern): Path<String>,
146 Json(request): Json<SetRouteMigrationRequest>,
147) -> Result<Json<ApiResponse<Value>>, StatusCode> {
148 let encoded_pattern = encode_path_segment(&pattern);
149 let body = json!({ "mode": request.mode });
150 proxy_to_http_server(
151 &state,
152 &format!("/migration/routes/{}", encoded_pattern),
153 Some(body),
154 "PUT",
155 )
156 .await
157}
158
159pub async fn toggle_group_migration(
161 State(state): State<AdminState>,
162 Path(group): Path<String>,
163) -> Result<Json<ApiResponse<Value>>, StatusCode> {
164 let encoded_group = encode_path_segment(&group);
165 proxy_to_http_server(
166 &state,
167 &format!("/migration/groups/{}/toggle", encoded_group),
168 None,
169 "POST",
170 )
171 .await
172}
173
174pub async fn set_group_migration_mode(
176 State(state): State<AdminState>,
177 Path(group): Path<String>,
178 Json(request): Json<SetGroupMigrationRequest>,
179) -> Result<Json<ApiResponse<Value>>, StatusCode> {
180 let encoded_group = encode_path_segment(&group);
181 let body = json!({ "mode": request.mode });
182 proxy_to_http_server(&state, &format!("/migration/groups/{}", encoded_group), Some(body), "PUT")
183 .await
184}
185
186pub async fn get_migration_groups(
188 State(state): State<AdminState>,
189) -> Result<Json<ApiResponse<Value>>, StatusCode> {
190 proxy_to_http_server(&state, "/migration/groups", None, "GET").await
191}
192
193pub async fn get_migration_status(
195 State(state): State<AdminState>,
196) -> Result<Json<ApiResponse<Value>>, StatusCode> {
197 proxy_to_http_server(&state, "/migration/status", None, "GET").await
198}