2026-03-24 15:12:56 +08:00
|
|
|
use std::time::{SystemTime, UNIX_EPOCH};
|
|
|
|
|
|
|
|
|
|
use axum::extract::{Path, Query, State};
|
|
|
|
|
use axum::response::IntoResponse;
|
|
|
|
|
use axum::Json;
|
|
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
|
use serde_json::json;
|
|
|
|
|
|
2026-04-05 20:23:16 +08:00
|
|
|
use crate::{AppState, GatewayError};
|
2026-03-24 15:12:56 +08:00
|
|
|
|
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
|
|
|
pub(crate) struct GetRequestCandidateTraceQuery {
|
|
|
|
|
pub(crate) attempted_only: Option<bool>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub(crate) async fn get_request_candidate_trace(
|
|
|
|
|
State(state): State<AppState>,
|
|
|
|
|
Path(request_id): Path<String>,
|
|
|
|
|
Query(query): Query<GetRequestCandidateTraceQuery>,
|
2026-04-07 02:50:19 +08:00
|
|
|
) -> Result<Json<crate::data::candidates::RequestCandidateTrace>, axum::response::Response> {
|
2026-03-24 15:12:56 +08:00
|
|
|
let attempted_only = query.attempted_only.unwrap_or(false);
|
|
|
|
|
let trace = state
|
2026-04-07 02:50:19 +08:00
|
|
|
.data
|
2026-03-24 15:12:56 +08:00
|
|
|
.read_request_candidate_trace(&request_id, attempted_only)
|
|
|
|
|
.await
|
2026-04-07 02:50:19 +08:00
|
|
|
.map_err(|err| GatewayError::Internal(err.to_string()).into_response())?;
|
2026-03-24 15:12:56 +08:00
|
|
|
|
|
|
|
|
match trace {
|
2026-09-04 03:45:52 +08:00
|
|
|
Some(mut trace) => {
|
|
|
|
|
trace.sanitize_sensitive_diagnostics();
|
|
|
|
|
Ok(Json(trace))
|
|
|
|
|
}
|
2026-03-24 15:12:56 +08:00
|
|
|
None => Err((
|
|
|
|
|
axum::http::StatusCode::NOT_FOUND,
|
|
|
|
|
Json(json!({
|
|
|
|
|
"error": {
|
|
|
|
|
"message": "Request not found",
|
|
|
|
|
}
|
|
|
|
|
})),
|
|
|
|
|
)
|
|
|
|
|
.into_response()),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub(crate) async fn get_decision_trace(
|
|
|
|
|
State(state): State<AppState>,
|
|
|
|
|
Path(request_id): Path<String>,
|
|
|
|
|
Query(query): Query<GetRequestCandidateTraceQuery>,
|
2026-04-07 02:50:19 +08:00
|
|
|
) -> Result<Json<crate::data::decision_trace::DecisionTrace>, axum::response::Response> {
|
2026-03-24 15:12:56 +08:00
|
|
|
let attempted_only = query.attempted_only.unwrap_or(false);
|
|
|
|
|
let trace = state
|
2026-04-07 02:50:19 +08:00
|
|
|
.data
|
2026-03-24 15:12:56 +08:00
|
|
|
.read_decision_trace(&request_id, attempted_only)
|
|
|
|
|
.await
|
2026-04-07 02:50:19 +08:00
|
|
|
.map_err(|err| GatewayError::Internal(err.to_string()).into_response())?;
|
2026-03-24 15:12:56 +08:00
|
|
|
|
|
|
|
|
match trace {
|
2026-09-04 03:45:52 +08:00
|
|
|
Some(mut trace) => {
|
|
|
|
|
trace.sanitize_sensitive_diagnostics();
|
|
|
|
|
Ok(Json(trace))
|
|
|
|
|
}
|
2026-03-24 15:12:56 +08:00
|
|
|
None => Err((
|
|
|
|
|
axum::http::StatusCode::NOT_FOUND,
|
|
|
|
|
Json(json!({
|
|
|
|
|
"error": {
|
|
|
|
|
"message": "Decision trace not found",
|
|
|
|
|
}
|
|
|
|
|
})),
|
|
|
|
|
)
|
|
|
|
|
.into_response()),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub(crate) async fn get_auth_api_key_snapshot(
|
|
|
|
|
State(state): State<AppState>,
|
|
|
|
|
Path((user_id, api_key_id)): Path<(String, String)>,
|
2026-04-07 02:50:19 +08:00
|
|
|
) -> Result<Json<crate::data::auth::GatewayAuthApiKeySnapshot>, axum::response::Response> {
|
2026-03-24 15:12:56 +08:00
|
|
|
let snapshot = state
|
2026-04-07 02:50:19 +08:00
|
|
|
.data
|
2026-03-24 15:12:56 +08:00
|
|
|
.read_auth_api_key_snapshot(&user_id, &api_key_id, current_unix_secs())
|
|
|
|
|
.await
|
2026-04-07 02:50:19 +08:00
|
|
|
.map_err(|err| GatewayError::Internal(err.to_string()).into_response())?;
|
2026-03-24 15:12:56 +08:00
|
|
|
|
|
|
|
|
match snapshot {
|
|
|
|
|
Some(snapshot) => Ok(Json(snapshot)),
|
|
|
|
|
None => Err((
|
|
|
|
|
axum::http::StatusCode::NOT_FOUND,
|
|
|
|
|
Json(json!({
|
|
|
|
|
"error": {
|
|
|
|
|
"message": "Auth snapshot not found",
|
|
|
|
|
}
|
|
|
|
|
})),
|
|
|
|
|
)
|
|
|
|
|
.into_response()),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn current_unix_secs() -> u64 {
|
|
|
|
|
SystemTime::now()
|
|
|
|
|
.duration_since(UNIX_EPOCH)
|
|
|
|
|
.unwrap_or_default()
|
|
|
|
|
.as_secs()
|
|
|
|
|
}
|