Merge origin/main into main

Integrate upstream updates while preserving the local analytics dashboards and schema-only migration changes.

Combine user account analysis with upstream user/group usage statistics in separate tabs, retain all migration versions, and keep the deleted audit document removed.

Validation: gateway all-target cargo check, frontend type check and 57 focused tests, 48 migration tests, schema composition checks, and diff whitespace checks.
This commit is contained in:
elky
2026-10-02 11:57:18 +08:00
343 changed files with 27929 additions and 2549 deletions
@@ -276,6 +276,10 @@ pub(crate) fn admin_proxy_local_requires_buffered_body(
| (Some("system_manage"), http::Method::POST, Some("config_import"))
| (Some("system_manage"), http::Method::POST, Some("users_import"))
| (Some("system_manage"), http::Method::POST, Some("data_import"))
| (Some("system_manage"), http::Method::POST, Some("cleanup_usage_manual"))
| (Some("system_manage"), http::Method::POST, Some("smtp_test"))
| (Some("system_manage"), http::Method::POST, Some("prepare_update"))
| (Some("system_manage"), http::Method::POST, Some("apply_update"))
| (Some("system_manage"), http::Method::PUT, Some("settings_set"))
| (Some("system_manage"), http::Method::PUT, Some("config_set"))
| (Some("system_manage"), http::Method::PUT, Some("email_template_set"))
@@ -611,4 +615,27 @@ mod tests {
"/v1/chat/completions?key=passthrough"
);
}
#[test]
fn manual_cleanup_route_requires_buffered_body() {
use crate::control::GatewayPublicRequestContext;
let mut decision = GatewayControlDecision::synthetic(
"/api/admin/system/cleanup/usage/manual",
Some("admin_proxy".to_string()),
Some("system_manage".to_string()),
Some("cleanup_usage_manual".to_string()),
Some("system_manage:cleanup_usage_manual".to_string()),
);
decision.route_class = Some("admin_proxy".to_string());
let uri: http::Uri = "/api/admin/system/cleanup/usage/manual".parse().unwrap();
let headers = http::HeaderMap::new();
let context = GatewayPublicRequestContext::from_request_parts(
"trace-manual-cleanup",
&http::Method::POST,
&uri,
&headers,
Some(decision),
);
assert!(super::admin_proxy_local_requires_buffered_body(&context));
}
}