use axum::{
extract::{Path, State},
http::StatusCode,
response::{IntoResponse, Response},
routing::{get, post},
Json, Router,
};
use serde::{Deserialize, Serialize};
use std::sync::Arc;
use crate::GatewayState;
#[derive(Serialize, Deserialize, Debug)]
struct StateSyncResponse {
status: String,
}
#[derive(Serialize, Deserialize, Debug)]
struct StateReadResponse {
workflow_id: String,
state: serde_json::Value,
}
#[derive(Serialize, Deserialize, Debug)]
struct ExecuteManifestResponse {
status: String,
result: serde_json::Value,
}
async fn sync_state(
State(state): State<Arc<GatewayState>>,
Path(workflow_id): Path<String>,
Json(payload): Json<serde_json::Value>,
) -> Response {
let raw = serde_json::to_vec(&payload).unwrap_or_default();
if raw.len() > 256 * 1024 {
return (
StatusCode::PAYLOAD_TOO_LARGE,
Json(serde_json::json!({"detail": "Payload Too Large"})),
)
.into_response();
}
let url = format!("{}/api/v1/state/sync/{}", state.sidecar_url, workflow_id);
match ureq::post(&url).send_json(&payload) {
Ok(res) => {
let status = StatusCode::from_u16(res.status()).unwrap_or(StatusCode::OK);
let body: serde_json::Value = res.into_json().unwrap_or(serde_json::Value::Null);
(status, Json(body)).into_response()
}
Err(ureq::Error::Status(code, res)) => {
let status = StatusCode::from_u16(code).unwrap_or(StatusCode::BAD_REQUEST);
let body: serde_json::Value = res.into_json().unwrap_or(serde_json::Value::Null);
(status, Json(body)).into_response()
}
Err(e) => (
StatusCode::INTERNAL_SERVER_ERROR,
Json(serde_json::json!({
"status": "error",
"message": format!("Failed to proxy state delta sync: {}", e),
})),
)
.into_response(),
}
}
async fn read_state(
State(state): State<Arc<GatewayState>>,
Path(workflow_id): Path<String>,
) -> Response {
let url = format!("{}/api/v1/state/sync/{}", state.sidecar_url, workflow_id);
match ureq::get(&url).call() {
Ok(res) => {
let status = StatusCode::from_u16(res.status()).unwrap_or(StatusCode::OK);
let body: serde_json::Value = res.into_json().unwrap_or(serde_json::Value::Null);
(status, Json(body)).into_response()
}
Err(ureq::Error::Status(code, res)) => {
let status = StatusCode::from_u16(code).unwrap_or(StatusCode::NOT_FOUND);
let body: serde_json::Value = res.into_json().unwrap_or(serde_json::Value::Null);
(status, Json(body)).into_response()
}
Err(e) => (
StatusCode::INTERNAL_SERVER_ERROR,
Json(serde_json::json!({
"status": "error",
"message": format!("Failed to query state from sidecar: {}", e),
})),
)
.into_response(),
}
}
async fn execute_manifest(
State(state): State<Arc<GatewayState>>,
Json(payload): Json<serde_json::Value>,
) -> Response {
let url = format!("{}/api/v1/state/execute", state.sidecar_url);
match ureq::post(&url).send_json(&payload) {
Ok(res) => {
let status = StatusCode::from_u16(res.status()).unwrap_or(StatusCode::OK);
let body: serde_json::Value = res.into_json().unwrap_or(serde_json::Value::Null);
(status, Json(body)).into_response()
}
Err(ureq::Error::Status(code, res)) => {
let status = StatusCode::from_u16(code).unwrap_or(StatusCode::BAD_REQUEST);
let body: serde_json::Value = res.into_json().unwrap_or(serde_json::Value::Null);
(status, Json(body)).into_response()
}
Err(e) => (
StatusCode::INTERNAL_SERVER_ERROR,
Json(serde_json::json!({
"status": "error",
"message": format!("Failed to proxy manifest execution: {}", e),
})),
)
.into_response(),
}
}
pub fn router() -> Router<Arc<GatewayState>> {
Router::new()
.route(
"/api/v1/state/sync/:workflow_id",
post(sync_state).get(read_state),
)
.route("/api/v1/state/execute", post(execute_manifest))
}