From 87cf5004b94a1fa469aea816b4e5c2d390f65887 Mon Sep 17 00:00:00 2001 From: xiaoxixi Date: Fri, 24 Jul 2026 18:27:37 +0800 Subject: [PATCH] fix(gateway): stabilize status snapshot output --- src/gateway/http.rs | 92 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 85 insertions(+), 7 deletions(-) diff --git a/src/gateway/http.rs b/src/gateway/http.rs index 54f32db..65d330b 100644 --- a/src/gateway/http.rs +++ b/src/gateway/http.rs @@ -169,6 +169,14 @@ impl ApiError { message: error.to_string(), } } + + fn internal_with_message(error: impl std::fmt::Display, message: impl Into) -> Self { + tracing::error!(error = %error, "WebUI API request failed"); + Self { + status: StatusCode::INTERNAL_SERVER_ERROR, + message: message.into(), + } + } } pub async fn upload_file( @@ -714,6 +722,30 @@ fn scheduler_snapshot(jobs: &[crate::storage::ScheduledJob]) -> Value { }) } +fn scheduler_status_error(error: impl std::fmt::Display) -> ApiError { + ApiError::internal_with_message(error, "failed to load scheduler status") +} + +fn channel_snapshot(mut channels: Vec<(String, bool)>) -> Vec { + channels.sort_by(|left, right| left.0.cmp(&right.0)); + channels + .into_iter() + .map(|(name, running)| { + json!({ + "name": name, + "status": if running { "connected" } else { "stopped" }, + }) + }) + .collect() +} + +fn sorted_providers( + mut providers: Vec, +) -> Vec { + providers.sort_by(|left, right| left.name.cmp(&right.name)); + providers +} + pub async fn get_status(State(state): State>) -> Result, ApiError> { let reload = state.reload.status(); let metrics = crate::observability::metrics::global_metrics().snapshot(); @@ -728,24 +760,22 @@ pub async fn get_status(State(state): State>) -> Result>) -> Result>(), + vec!["alpha", "zeta"] + ); + } + + #[test] + fn scheduler_status_error_hides_internal_details() { + let error = scheduler_status_error("database contained private payload"); + + assert_eq!(error.status, StatusCode::INTERNAL_SERVER_ERROR); + assert_eq!(error.message, "failed to load scheduler status"); + assert!(!error.message.contains("private payload")); + } + #[test] fn secrets_are_redacted_and_restored() { let current = json!({"api_key":"real", "nested":{"access_token":"token"}, "safe":"yes"});