use axum::body::Body;
use axum::extract::State;
use axum::http::{Method, Request};
use axum::response::IntoResponse;
use axum::routing::Router;
use axum::Json;
use serde::{Deserialize, Serialize};
use std::sync::OnceLock;
use tower::ServiceExt;
use crate::api::openai_compat::AppState;
static ROUTER: OnceLock<Router> = OnceLock::new();
pub fn set_global_router(router: Router) {
let _ = ROUTER.set(router);
}
pub fn router_ready() -> bool {
ROUTER.get().is_some()
}
#[derive(Debug, Deserialize)]
pub struct RequestEnvelope {
pub id: String,
#[serde(default = "default_method")]
pub method: String,
pub path: String,
#[serde(default)]
pub headers: std::collections::HashMap<String, String>,
#[serde(default)]
pub body: serde_json::Value,
}
fn default_method() -> String {
"CALL".into()
}
#[derive(Debug, Serialize)]
pub struct ResponseEnvelope {
pub id: String,
pub status: u16,
#[serde(skip_serializing_if = "Option::is_none")]
pub headers: Option<std::collections::HashMap<String, String>>,
pub body: serde_json::Value,
}
pub async fn rpc_endpoint(
State(state): State<AppState>,
Json(envelope): Json<RequestEnvelope>,
) -> impl IntoResponse {
let token = state.runtime_token.clone().unwrap_or_default();
Json(dispatch_internal(envelope, &token).await)
}
pub async fn dispatch_internal(envelope: RequestEnvelope, token: &str) -> ResponseEnvelope {
let router = match ROUTER.get() {
Some(r) => r.clone(),
None => {
return ResponseEnvelope {
id: envelope.id,
status: 503,
headers: None,
body: serde_json::json!({ "error": "router not initialized" }),
};
}
};
let method = match envelope.method.to_uppercase().as_str() {
"QUERY" | "GET" => Method::GET,
"DELETE" => Method::DELETE,
"PUT" => Method::PUT,
_ => Method::POST,
};
let path = if envelope.path.starts_with('/') {
envelope.path.clone()
} else {
format!("/{}", envelope.path)
};
let body_bytes = serde_json::to_vec(&envelope.body).unwrap_or_default();
let mut req_builder = Request::builder()
.method(method)
.uri(&path)
.header("content-type", "application/json")
.header("authorization", format!("Bearer {}", token));
for (k, v) in &envelope.headers {
if k.to_lowercase() != "authorization" {
req_builder = req_builder.header(k.as_str(), v.as_str());
}
}
let internal_req = match req_builder.body(Body::from(body_bytes)) {
Ok(r) => r,
Err(e) => {
return ResponseEnvelope {
id: envelope.id,
status: 400,
headers: None,
body: serde_json::json!({ "error": format!("bad request: {e}") }),
};
}
};
match router.oneshot(internal_req).await {
Ok(response) => {
let status = response.status().as_u16();
let body_bytes = axum::body::to_bytes(response.into_body(), 10 * 1024 * 1024)
.await
.unwrap_or_default();
let body: serde_json::Value =
serde_json::from_slice(&body_bytes).unwrap_or(serde_json::Value::Null);
ResponseEnvelope {
id: envelope.id,
status,
headers: None,
body,
}
}
Err(e) => ResponseEnvelope {
id: envelope.id,
status: 500,
headers: None,
body: serde_json::json!({ "error": format!("dispatch error: {e}") }),
},
}
}