refactor: 大规模模块拆分与重组,新增 aether-admin crate

- 新建独立 aether-admin crate 承载 admin 相关共享契约与纯辅助函数
- 拆分 ai_pipeline 下 kiro/private_envelope/conversion/planner 等大文件为子模块目录
- 重组 admin handlers 各业务域(billing/oauth/provider/system/users 等)为目录结构,移除 shared.rs/builders.rs 等反模式
- 移除 ai_pipeline runtime adapters 旧实现(claude/openai/gemini/kiro/vertex/antigravity 等),改由 provider transport 统一承载
- 移除 control_facade/execution_facade/auth_snapshot_facade 等冗余 facade 层
- 拆分 query/billing 与 query/monitoring 模块、state/runtime/payments 与 security 模块
- 扩展架构测试覆盖 admin_billing/admin_model/admin_users 等新模块
- 删除 docs/architecture/refactor-execution-plan.md 已完成的执行计划文档
This commit is contained in:
fawney19
2026-04-09 00:10:38 +08:00
parent 4fb9882b54
commit 4fc95adfb9
663 changed files with 48471 additions and 40232 deletions
@@ -1,14 +1,8 @@
use super::extractors::{admin_health_key_id, admin_recover_key_id};
use super::health_builders::{
build_admin_endpoint_health_status_payload, build_admin_health_summary_payload,
build_admin_key_health_payload, recover_admin_key_health, recover_all_admin_key_health,
};
use crate::control::GatewayPublicRequestContext;
use crate::handlers::admin::request::{AdminAppState, AdminRequestContext};
use crate::handlers::admin::shared::query_param_value;
use crate::handlers::public::{
build_api_format_health_monitor_payload, ApiFormatHealthMonitorOptions,
};
use crate::{AppState, GatewayError};
use crate::handlers::public::ApiFormatHealthMonitorOptions;
use crate::GatewayError;
use axum::{
body::Body,
http,
@@ -29,21 +23,21 @@ fn build_admin_endpoint_health_data_unavailable_response() -> Response<Body> {
}
pub(super) async fn maybe_build_local_admin_endpoints_health_response(
state: &AppState,
request_context: &GatewayPublicRequestContext,
state: &AdminAppState<'_>,
request_context: &AdminRequestContext<'_>,
) -> Result<Option<Response<Body>>, GatewayError> {
let Some(decision) = request_context.control_decision.as_ref() else {
let Some(decision) = request_context.decision() else {
return Ok(None);
};
if decision.route_family.as_deref() == Some("endpoints_health")
&& decision.route_kind.as_deref() == Some("health_summary")
&& request_context.request_path == "/api/admin/endpoints/health/summary"
&& request_context.path() == "/api/admin/endpoints/health/summary"
{
if !state.has_provider_catalog_data_reader() {
return Ok(Some(build_admin_endpoint_health_data_unavailable_response()));
}
let Some(payload) = build_admin_health_summary_payload(state).await else {
let Some(payload) = state.build_admin_health_summary_payload().await else {
return Ok(Some(build_admin_endpoint_health_data_unavailable_response()));
};
return Ok(Some(Json(payload).into_response()));
@@ -58,7 +52,7 @@ pub(super) async fn maybe_build_local_admin_endpoints_health_response(
if !state.has_provider_catalog_data_reader() {
return Ok(Some(build_admin_endpoint_health_data_unavailable_response()));
}
let Some(key_id) = admin_health_key_id(&request_context.request_path) else {
let Some(key_id) = admin_health_key_id(request_context.path()) else {
return Ok(Some(
(
http::StatusCode::NOT_FOUND,
@@ -67,12 +61,12 @@ pub(super) async fn maybe_build_local_admin_endpoints_health_response(
.into_response(),
));
};
let api_format = query_param_value(
request_context.request_query_string.as_deref(),
"api_format",
);
let api_format = query_param_value(request_context.query_string(), "api_format");
return Ok(Some(
match build_admin_key_health_payload(state, &key_id, api_format.as_deref()).await {
match state
.build_admin_key_health_payload(&key_id, api_format.as_deref())
.await
{
Some(payload) => Json(payload).into_response(),
None => (
http::StatusCode::NOT_FOUND,
@@ -92,7 +86,7 @@ pub(super) async fn maybe_build_local_admin_endpoints_health_response(
if !state.has_provider_catalog_data_reader() || !state.has_provider_catalog_data_writer() {
return Ok(Some(build_admin_endpoint_health_data_unavailable_response()));
}
let Some(key_id) = admin_recover_key_id(&request_context.request_path) else {
let Some(key_id) = admin_recover_key_id(request_context.path()) else {
return Ok(Some(
(
http::StatusCode::NOT_FOUND,
@@ -101,12 +95,12 @@ pub(super) async fn maybe_build_local_admin_endpoints_health_response(
.into_response(),
));
};
let api_format = query_param_value(
request_context.request_query_string.as_deref(),
"api_format",
);
let api_format = query_param_value(request_context.query_string(), "api_format");
return Ok(Some(
match recover_admin_key_health(state, &key_id, api_format.as_deref()).await {
match state
.recover_admin_key_health(&key_id, api_format.as_deref())
.await
{
Some(payload) => Json(payload).into_response(),
None => (
http::StatusCode::NOT_FOUND,
@@ -119,12 +113,12 @@ pub(super) async fn maybe_build_local_admin_endpoints_health_response(
if decision.route_family.as_deref() == Some("endpoints_health")
&& decision.route_kind.as_deref() == Some("recover_all_keys_health")
&& request_context.request_path == "/api/admin/endpoints/health/keys"
&& request_context.path() == "/api/admin/endpoints/health/keys"
{
if !state.has_provider_catalog_data_reader() || !state.has_provider_catalog_data_writer() {
return Ok(Some(build_admin_endpoint_health_data_unavailable_response()));
}
let Some(payload) = recover_all_admin_key_health(state).await else {
let Some(payload) = state.recover_all_admin_key_health().await else {
return Ok(Some(build_admin_endpoint_health_data_unavailable_response()));
};
return Ok(Some(Json(payload).into_response()));
@@ -132,36 +126,31 @@ pub(super) async fn maybe_build_local_admin_endpoints_health_response(
if decision.route_family.as_deref() == Some("endpoints_health")
&& decision.route_kind.as_deref() == Some("health_api_formats")
&& request_context.request_path == "/api/admin/endpoints/health/api-formats"
&& request_context.path() == "/api/admin/endpoints/health/api-formats"
{
if !state.has_provider_catalog_data_reader() || !state.has_request_candidate_data_reader() {
return Ok(Some(build_admin_endpoint_health_data_unavailable_response()));
}
let lookback_hours = query_param_value(
request_context.request_query_string.as_deref(),
"lookback_hours",
)
.and_then(|value| value.parse::<u64>().ok())
.filter(|value| (1..=72).contains(value))
.unwrap_or(6);
let per_format_limit = query_param_value(
request_context.request_query_string.as_deref(),
"per_format_limit",
)
.and_then(|value| value.parse::<usize>().ok())
.filter(|value| (10..=200).contains(value))
.unwrap_or(60);
let Some(payload) = build_api_format_health_monitor_payload(
state,
lookback_hours,
per_format_limit,
ApiFormatHealthMonitorOptions {
include_api_path: false,
include_provider_count: true,
include_key_count: true,
},
)
.await
let lookback_hours = query_param_value(request_context.query_string(), "lookback_hours")
.and_then(|value| value.parse::<u64>().ok())
.filter(|value| (1..=72).contains(value))
.unwrap_or(6);
let per_format_limit =
query_param_value(request_context.query_string(), "per_format_limit")
.and_then(|value| value.parse::<usize>().ok())
.filter(|value| (10..=200).contains(value))
.unwrap_or(60);
let Some(payload) = state
.build_api_format_health_monitor_payload(
lookback_hours,
per_format_limit,
ApiFormatHealthMonitorOptions {
include_api_path: false,
include_provider_count: true,
include_key_count: true,
},
)
.await
else {
return Ok(Some(build_admin_endpoint_health_data_unavailable_response()));
};
@@ -170,19 +159,18 @@ pub(super) async fn maybe_build_local_admin_endpoints_health_response(
if decision.route_family.as_deref() == Some("endpoints_health")
&& decision.route_kind.as_deref() == Some("health_status")
&& request_context.request_path == "/api/admin/endpoints/health/status"
&& request_context.path() == "/api/admin/endpoints/health/status"
{
if !state.has_provider_catalog_data_reader() || !state.has_request_candidate_data_reader() {
return Ok(Some(build_admin_endpoint_health_data_unavailable_response()));
}
let lookback_hours = query_param_value(
request_context.request_query_string.as_deref(),
"lookback_hours",
)
.and_then(|value| value.parse::<u64>().ok())
.filter(|value| (1..=72).contains(value))
.unwrap_or(6);
let Some(payload) = build_admin_endpoint_health_status_payload(state, lookback_hours).await
let lookback_hours = query_param_value(request_context.query_string(), "lookback_hours")
.and_then(|value| value.parse::<u64>().ok())
.filter(|value| (1..=72).contains(value))
.unwrap_or(6);
let Some(payload) = state
.build_admin_endpoint_health_status_payload(lookback_hours)
.await
else {
return Ok(Some(build_admin_endpoint_health_data_unavailable_response()));
};
@@ -1,11 +1,11 @@
use crate::handlers::admin::request::AdminAppState;
use crate::handlers::public::provider_key_api_formats;
use crate::AppState;
use aether_scheduler_core::count_recent_rpm_requests_for_provider_key_since;
use serde_json::json;
use std::time::{SystemTime, UNIX_EPOCH};
pub(crate) async fn build_admin_key_health_payload(
state: &AppState,
state: &AdminAppState<'_>,
key_id: &str,
api_format: Option<&str>,
) -> Option<serde_json::Value> {
@@ -172,7 +172,7 @@ pub(crate) async fn build_admin_key_health_payload(
}
pub(crate) async fn build_admin_key_rpm_payload(
state: &AppState,
state: &AdminAppState<'_>,
key_id: &str,
) -> Option<serde_json::Value> {
if !state.has_provider_catalog_data_reader() {
@@ -225,7 +225,7 @@ fn default_key_circuit_payload() -> serde_json::Value {
}
pub(crate) async fn recover_admin_key_health(
state: &AppState,
state: &AdminAppState<'_>,
key_id: &str,
api_format: Option<&str>,
) -> Option<serde_json::Value> {
@@ -295,7 +295,9 @@ pub(crate) async fn recover_admin_key_health(
}))
}
pub(crate) async fn recover_all_admin_key_health(state: &AppState) -> Option<serde_json::Value> {
pub(crate) async fn recover_all_admin_key_health(
state: &AdminAppState<'_>,
) -> Option<serde_json::Value> {
if !state.has_provider_catalog_data_reader() {
return None;
}
@@ -1,9 +1,9 @@
mod keys;
mod status;
pub(super) use self::keys::{
pub(crate) use self::keys::{
build_admin_key_health_payload, build_admin_key_rpm_payload, recover_admin_key_health,
recover_all_admin_key_health,
};
pub(crate) use self::status::build_admin_endpoint_health_status_payload;
pub(super) use self::status::build_admin_health_summary_payload;
pub(crate) use self::status::build_admin_health_summary_payload;
@@ -1,8 +1,8 @@
use crate::handlers::admin::request::AdminAppState;
use crate::handlers::admin::shared::unix_secs_to_rfc3339;
use crate::handlers::public::{
api_format_display_name, build_public_health_timeline, provider_key_api_formats,
};
use crate::AppState;
use aether_data_contracts::repository::candidates::PublicHealthTimelineBucket;
use aether_scheduler_core::{is_provider_key_circuit_open, provider_key_health_score};
use serde_json::json;
@@ -10,7 +10,7 @@ use std::collections::{BTreeMap, BTreeSet};
use std::time::{SystemTime, UNIX_EPOCH};
pub(crate) async fn build_admin_endpoint_health_status_payload(
state: &AppState,
state: &AdminAppState<'_>,
lookback_hours: u64,
) -> Option<serde_json::Value> {
if !state.has_provider_catalog_data_reader() || !state.has_request_candidate_data_reader() {
@@ -210,7 +210,7 @@ pub(crate) async fn build_admin_endpoint_health_status_payload(
}
pub(crate) async fn build_admin_health_summary_payload(
state: &AppState,
state: &AdminAppState<'_>,
) -> Option<serde_json::Value> {
if !state.has_provider_catalog_data_reader() {
return None;
@@ -1,52 +1,13 @@
use crate::control::GatewayPublicRequestContext;
use crate::handlers::admin::provider::{endpoint_keys, endpoints_admin};
use crate::{AppState, GatewayError};
use axum::body::{Body, Bytes};
use axum::http::Response;
mod extractors;
mod health;
mod health_builders;
mod routes;
mod rpm;
pub(crate) use self::health_builders::build_admin_endpoint_health_status_payload;
pub(crate) async fn maybe_build_local_admin_endpoints_response(
state: &AppState,
request_context: &GatewayPublicRequestContext,
request_body: Option<&Bytes>,
) -> Result<Option<Response<Body>>, GatewayError> {
if let Some(response) =
health::maybe_build_local_admin_endpoints_health_response(state, request_context).await?
{
return Ok(Some(response));
}
if let Some(response) =
rpm::maybe_build_local_admin_endpoints_rpm_response(state, request_context).await?
{
return Ok(Some(response));
}
if let Some(response) = endpoint_keys::maybe_build_local_admin_endpoints_keys_response(
state,
request_context,
request_body,
)
.await?
{
return Ok(Some(response));
}
if let Some(response) = endpoints_admin::maybe_build_local_admin_endpoints_routes_response(
state,
request_context,
request_body,
)
.await?
{
return Ok(Some(response));
}
Ok(None)
}
pub(super) use self::health_builders::build_admin_health_summary_payload;
pub(super) use self::health_builders::build_admin_key_health_payload;
pub(super) use self::health_builders::build_admin_key_rpm_payload;
pub(super) use self::health_builders::recover_admin_key_health;
pub(super) use self::health_builders::recover_all_admin_key_health;
pub(super) use self::routes::maybe_build_local_admin_endpoints_response;
@@ -0,0 +1,47 @@
use super::{health, rpm};
use crate::handlers::admin::provider::{endpoint_keys, endpoints_admin};
use crate::handlers::admin::request::{AdminRouteRequest, AdminRouteResult};
pub(crate) async fn maybe_build_local_admin_endpoints_response(
request: AdminRouteRequest<'_>,
) -> AdminRouteResult {
if let Some(response) = health::maybe_build_local_admin_endpoints_health_response(
&request.state(),
&request.request_context(),
)
.await?
{
return Ok(Some(response));
}
if let Some(response) = rpm::maybe_build_local_admin_endpoints_rpm_response(
&request.state(),
&request.request_context(),
)
.await?
{
return Ok(Some(response));
}
if let Some(response) = endpoint_keys::maybe_build_local_admin_endpoints_keys_response(
&request.state(),
&request.request_context(),
request.request_body(),
)
.await?
{
return Ok(Some(response));
}
if let Some(response) = endpoints_admin::maybe_build_local_admin_endpoints_routes_response(
&request.state(),
&request.request_context(),
request.request_body(),
)
.await?
{
return Ok(Some(response));
}
Ok(None)
}
@@ -1,7 +1,6 @@
use super::extractors::admin_rpm_key_id;
use super::health_builders::build_admin_key_rpm_payload;
use crate::control::GatewayPublicRequestContext;
use crate::{AppState, GatewayError};
use crate::handlers::admin::request::{AdminAppState, AdminRequestContext};
use crate::GatewayError;
use axum::{
body::Body,
http,
@@ -12,20 +11,20 @@ use serde_json::json;
use std::time::{SystemTime, UNIX_EPOCH};
pub(super) async fn maybe_build_local_admin_endpoints_rpm_response(
state: &AppState,
request_context: &GatewayPublicRequestContext,
state: &AdminAppState<'_>,
request_context: &AdminRequestContext<'_>,
) -> Result<Option<Response<Body>>, GatewayError> {
let Some(decision) = request_context.control_decision.as_ref() else {
let Some(decision) = request_context.decision() else {
return Ok(None);
};
if decision.route_family.as_deref() == Some("endpoints_rpm")
&& decision.route_kind.as_deref() == Some("key_rpm")
&& request_context
.request_path
.path()
.starts_with("/api/admin/endpoints/rpm/key/")
{
let Some(key_id) = admin_rpm_key_id(&request_context.request_path) else {
let Some(key_id) = admin_rpm_key_id(request_context.path()) else {
return Ok(Some(
(
http::StatusCode::NOT_FOUND,
@@ -35,7 +34,7 @@ pub(super) async fn maybe_build_local_admin_endpoints_rpm_response(
));
};
return Ok(Some(
match build_admin_key_rpm_payload(state, &key_id).await {
match state.build_admin_key_rpm_payload(&key_id).await {
Some(payload) => Json(payload).into_response(),
None => (
http::StatusCode::NOT_FOUND,
@@ -48,12 +47,12 @@ pub(super) async fn maybe_build_local_admin_endpoints_rpm_response(
if decision.route_family.as_deref() == Some("endpoints_rpm")
&& decision.route_kind.as_deref() == Some("reset_key_rpm")
&& request_context.request_method == http::Method::DELETE
&& request_context.method() == http::Method::DELETE
&& request_context
.request_path
.path()
.starts_with("/api/admin/endpoints/rpm/key/")
{
let Some(key_id) = admin_rpm_key_id(&request_context.request_path) else {
let Some(key_id) = admin_rpm_key_id(request_context.path()) else {
return Ok(Some(
(
http::StatusCode::NOT_FOUND,