use axum::{
extract::{Path, State},
http::StatusCode,
Json,
};
use serde::{Deserialize, Serialize};
use serde_json::{json, Value};
use crate::handlers::AdminState;
use crate::models::ApiResponse;
fn encode_path_segment(s: &str) -> String {
s.replace('/', "%2F")
.replace('*', "%2A")
.replace('?', "%3F")
.replace('#', "%23")
.replace('[', "%5B")
.replace(']', "%5D")
}
async fn proxy_to_http_server(
state: &AdminState,
path: &str,
body: Option<Value>,
method: &str,
) -> Result<Json<ApiResponse<Value>>, StatusCode> {
let http_addr = match state.http_server_addr {
Some(addr) => addr,
None => {
return Ok(Json(ApiResponse::error("HTTP server not available".to_string())));
}
};
let url = format!("http://{}/__mockforge/api{}", http_addr, path);
let client = reqwest::Client::new();
let mut request = match (method, body.is_some()) {
("PUT", _) => client.put(&url),
("POST", _) => client.post(&url),
_ => client.get(&url),
};
if let Some(body_value) = body {
request = request.json(&body_value);
}
match request.send().await {
Ok(response) => {
let status = response.status();
match response.json::<Value>().await {
Ok(data) => {
if status.is_success() {
Ok(Json(ApiResponse::success(data)))
} else {
Ok(Json(ApiResponse::error(
data.get("error")
.and_then(|v| v.as_str())
.unwrap_or("Request failed")
.to_string(),
)))
}
}
Err(e) => {
tracing::error!("Failed to parse response: {}", e);
Ok(Json(ApiResponse::error(format!("Failed to parse response: {}", e))))
}
}
}
Err(e) => {
tracing::error!("Failed to proxy request: {}", e);
Ok(Json(ApiResponse::error(format!("Failed to connect to HTTP server: {}", e))))
}
}
}
#[derive(Debug, Deserialize)]
pub struct SetRouteMigrationRequest {
pub mode: String,
}
#[derive(Debug, Deserialize)]
pub struct SetGroupMigrationRequest {
pub mode: String,
}
#[derive(Debug, Serialize)]
pub struct MigrationStatus {
pub total_routes: usize,
pub mock_routes: usize,
pub shadow_routes: usize,
pub real_routes: usize,
pub auto_routes: usize,
pub total_groups: usize,
pub migration_enabled: bool,
}
pub async fn get_migration_routes(
State(state): State<AdminState>,
) -> Result<Json<ApiResponse<Value>>, StatusCode> {
proxy_to_http_server(&state, "/migration/routes", None, "GET").await
}
pub async fn toggle_route_migration(
State(state): State<AdminState>,
Path(pattern): Path<String>,
) -> Result<Json<ApiResponse<Value>>, StatusCode> {
let encoded_pattern = encode_path_segment(&pattern);
proxy_to_http_server(
&state,
&format!("/migration/routes/{}/toggle", encoded_pattern),
None,
"POST",
)
.await
}
pub async fn set_route_migration_mode(
State(state): State<AdminState>,
Path(pattern): Path<String>,
Json(request): Json<SetRouteMigrationRequest>,
) -> Result<Json<ApiResponse<Value>>, StatusCode> {
let encoded_pattern = encode_path_segment(&pattern);
let body = json!({ "mode": request.mode });
proxy_to_http_server(
&state,
&format!("/migration/routes/{}", encoded_pattern),
Some(body),
"PUT",
)
.await
}
pub async fn toggle_group_migration(
State(state): State<AdminState>,
Path(group): Path<String>,
) -> Result<Json<ApiResponse<Value>>, StatusCode> {
let encoded_group = encode_path_segment(&group);
proxy_to_http_server(
&state,
&format!("/migration/groups/{}/toggle", encoded_group),
None,
"POST",
)
.await
}
pub async fn set_group_migration_mode(
State(state): State<AdminState>,
Path(group): Path<String>,
Json(request): Json<SetGroupMigrationRequest>,
) -> Result<Json<ApiResponse<Value>>, StatusCode> {
let encoded_group = encode_path_segment(&group);
let body = json!({ "mode": request.mode });
proxy_to_http_server(&state, &format!("/migration/groups/{}", encoded_group), Some(body), "PUT")
.await
}
pub async fn get_migration_groups(
State(state): State<AdminState>,
) -> Result<Json<ApiResponse<Value>>, StatusCode> {
proxy_to_http_server(&state, "/migration/groups", None, "GET").await
}
pub async fn get_migration_status(
State(state): State<AdminState>,
) -> Result<Json<ApiResponse<Value>>, StatusCode> {
proxy_to_http_server(&state, "/migration/status", None, "GET").await
}