refactor: 拆分 admin handler 大文件为模块目录,消除 shared.rs 反模式

- endpoint_keys/pool/architecture 等大文件拆分为独立模块目录
- 删除 crud/query/strategy/system 中的 shared.rs,内容归位到各自模块
- endpoints_admin/models/oauth/write 等模块拆出 payloads/responses/support 子文件
- system/core 下多个 *_routes.rs 合并到 system_routes.rs
- 更新 refactor-execution-plan 文档进度
This commit is contained in:
fawney19
2026-04-07 12:15:19 +08:00
parent 29055c575f
commit 4fb9882b54
109 changed files with 8151 additions and 7047 deletions
@@ -1,4 +1,4 @@
use super::{
use super::support::{
admin_pool_provider_id_from_path, build_admin_pool_error_response,
ADMIN_POOL_BANNED_KEY_CLEANUP_EMPTY_MESSAGE,
ADMIN_POOL_PROVIDER_CATALOG_READER_UNAVAILABLE_DETAIL,
@@ -6,7 +6,7 @@ use super::{
};
use super::{payloads as pool_payloads, selection as pool_selection};
use crate::control::GatewayPublicRequestContext;
use crate::handlers::admin::provider::pool::{
use crate::handlers::admin::provider::pool::runtime::{
clear_admin_provider_pool_cooldown, reset_admin_provider_pool_cost,
};
use crate::handlers::admin::shared::{
@@ -1,137 +1,17 @@
use crate::control::GatewayPublicRequestContext;
use crate::handlers::admin::shared::query_param_value;
use crate::{AppState, GatewayError};
use axum::{
body::{Body, Bytes},
http,
response::{IntoResponse, Response},
Json,
response::Response,
};
use serde_json::json;
const ADMIN_POOL_PROVIDER_CATALOG_READER_UNAVAILABLE_DETAIL: &str =
"Admin pool overview requires provider catalog reader";
const ADMIN_POOL_PROVIDER_CATALOG_WRITER_UNAVAILABLE_DETAIL: &str =
"Admin pool cleanup requires provider catalog writer";
const ADMIN_POOL_BANNED_KEY_CLEANUP_EMPTY_MESSAGE: &str = "未发现可清理的异常账号";
mod support;
mod batch_routes;
mod payloads;
mod read_routes;
mod selection;
#[derive(Debug, Default, serde::Deserialize)]
struct AdminPoolResolveSelectionRequest {
#[serde(default)]
search: String,
#[serde(default)]
quick_selectors: Vec<String>,
}
fn build_admin_pool_error_response(
status: http::StatusCode,
detail: impl Into<String>,
) -> Response<Body> {
(status, Json(json!({ "detail": detail.into() }))).into_response()
}
fn parse_admin_pool_page(query: Option<&str>) -> Result<usize, String> {
match query_param_value(query, "page") {
None => Ok(1),
Some(value) => {
let parsed = value
.parse::<usize>()
.map_err(|_| "page must be an integer between 1 and 10000".to_string())?;
if (1..=10_000).contains(&parsed) {
Ok(parsed)
} else {
Err("page must be an integer between 1 and 10000".to_string())
}
}
}
}
fn parse_admin_pool_page_size(query: Option<&str>) -> Result<usize, String> {
match query_param_value(query, "page_size") {
None => Ok(50),
Some(value) => {
let parsed = value
.parse::<usize>()
.map_err(|_| "page_size must be an integer between 1 and 200".to_string())?;
if (1..=200).contains(&parsed) {
Ok(parsed)
} else {
Err("page_size must be an integer between 1 and 200".to_string())
}
}
}
}
fn parse_admin_pool_search(query: Option<&str>) -> Option<String> {
query_param_value(query, "search")
.map(|value| value.trim().to_string())
.filter(|value| !value.is_empty())
}
fn parse_admin_pool_status_filter(query: Option<&str>) -> Result<String, String> {
let value = query_param_value(query, "status")
.unwrap_or_else(|| "all".to_string())
.trim()
.to_ascii_lowercase();
match value.as_str() {
"all" | "active" | "inactive" | "cooldown" => Ok(value),
_ => Err("status must be one of: all, active, cooldown, inactive".to_string()),
}
}
fn admin_pool_provider_id_from_path(request_path: &str) -> Option<String> {
let raw = request_path.strip_prefix("/api/admin/pool/")?;
let mut segments = raw.split('/');
let provider_id = segments.next()?.trim();
let keys_segment = segments.next()?.trim();
if provider_id.is_empty() || keys_segment != "keys" {
None
} else {
Some(provider_id.to_string())
}
}
fn is_admin_pool_route(request_context: &GatewayPublicRequestContext) -> bool {
let normalized_path = request_context.request_path.trim_end_matches('/');
let path = if normalized_path.is_empty() {
request_context.request_path.as_str()
} else {
normalized_path
};
(request_context.request_method == http::Method::GET && path == "/api/admin/pool/overview")
|| (request_context.request_method == http::Method::GET
&& path == "/api/admin/pool/scheduling-presets")
|| (request_context.request_method == http::Method::GET
&& path.starts_with("/api/admin/pool/")
&& path.ends_with("/keys")
&& path.matches('/').count() == 5)
|| (request_context.request_method == http::Method::POST
&& path.starts_with("/api/admin/pool/")
&& path.ends_with("/keys/batch-import")
&& path.matches('/').count() == 6)
|| (request_context.request_method == http::Method::POST
&& path.starts_with("/api/admin/pool/")
&& path.ends_with("/keys/batch-action")
&& path.matches('/').count() == 6)
|| (request_context.request_method == http::Method::POST
&& path.starts_with("/api/admin/pool/")
&& path.ends_with("/keys/resolve-selection")
&& path.matches('/').count() == 6)
|| (request_context.request_method == http::Method::GET
&& path.starts_with("/api/admin/pool/")
&& path.contains("/keys/batch-delete-task/")
&& path.matches('/').count() == 7)
|| (request_context.request_method == http::Method::POST
&& path.starts_with("/api/admin/pool/")
&& path.ends_with("/keys/cleanup-banned")
&& path.matches('/').count() == 6)
}
use self::support::{build_admin_pool_error_response, is_admin_pool_route};
pub(crate) async fn maybe_build_local_admin_pool_response(
state: &AppState,
@@ -1,4 +1,4 @@
use crate::handlers::admin::provider::shared::{
use crate::handlers::admin::provider::shared::support::{
AdminProviderPoolConfig, AdminProviderPoolRuntimeState,
};
use crate::handlers::admin::shared::unix_secs_to_rfc3339;
@@ -1,15 +1,16 @@
use super::{
use super::support::{
admin_pool_provider_id_from_path, build_admin_pool_error_response, parse_admin_pool_page,
parse_admin_pool_page_size, parse_admin_pool_search, parse_admin_pool_status_filter,
AdminPoolResolveSelectionRequest, ADMIN_POOL_PROVIDER_CATALOG_READER_UNAVAILABLE_DETAIL,
};
use super::{payloads as pool_payloads, selection as pool_selection};
use crate::control::GatewayPublicRequestContext;
use crate::handlers::admin::provider::pool::{
admin_provider_pool_config, read_admin_provider_pool_cooldown_counts,
read_admin_provider_pool_cooldown_key_ids, read_admin_provider_pool_runtime_state,
use crate::handlers::admin::provider::pool::config::admin_provider_pool_config;
use crate::handlers::admin::provider::pool::runtime::{
read_admin_provider_pool_cooldown_counts, read_admin_provider_pool_cooldown_key_ids,
read_admin_provider_pool_runtime_state,
};
use crate::handlers::admin::provider::shared::AdminProviderPoolRuntimeState;
use crate::handlers::admin::provider::shared::support::AdminProviderPoolRuntimeState;
use crate::{AppState, GatewayError};
use aether_data_contracts::repository::provider_catalog::ProviderCatalogKeyListQuery;
use axum::{
@@ -0,0 +1,130 @@
use crate::control::GatewayPublicRequestContext;
use crate::handlers::admin::shared::query_param_value;
use crate::{AppState, GatewayError};
use axum::{
body::{Body, Bytes},
http,
response::{IntoResponse, Response},
Json,
};
use serde::Deserialize;
use serde_json::json;
pub(super) const ADMIN_POOL_PROVIDER_CATALOG_READER_UNAVAILABLE_DETAIL: &str =
"Admin pool overview requires provider catalog reader";
pub(super) const ADMIN_POOL_PROVIDER_CATALOG_WRITER_UNAVAILABLE_DETAIL: &str =
"Admin pool cleanup requires provider catalog writer";
pub(super) const ADMIN_POOL_BANNED_KEY_CLEANUP_EMPTY_MESSAGE: &str = "未发现可清理的异常账号";
#[derive(Debug, Default, Deserialize)]
pub(super) struct AdminPoolResolveSelectionRequest {
#[serde(default)]
pub(crate) search: String,
#[serde(default)]
pub(crate) quick_selectors: Vec<String>,
}
pub(super) fn build_admin_pool_error_response(
status: http::StatusCode,
detail: impl Into<String>,
) -> Response<Body> {
(status, Json(json!({ "detail": detail.into() }))).into_response()
}
pub(super) fn parse_admin_pool_page(query: Option<&str>) -> Result<usize, String> {
match query_param_value(query, "page") {
None => Ok(1),
Some(value) => {
let parsed = value
.parse::<usize>()
.map_err(|_| "page must be an integer between 1 and 10000".to_string())?;
if (1..=10_000).contains(&parsed) {
Ok(parsed)
} else {
Err("page must be an integer between 1 and 10000".to_string())
}
}
}
}
pub(super) fn parse_admin_pool_page_size(query: Option<&str>) -> Result<usize, String> {
match query_param_value(query, "page_size") {
None => Ok(50),
Some(value) => {
let parsed = value
.parse::<usize>()
.map_err(|_| "page_size must be an integer between 1 and 200".to_string())?;
if (1..=200).contains(&parsed) {
Ok(parsed)
} else {
Err("page_size must be an integer between 1 and 200".to_string())
}
}
}
}
pub(super) fn parse_admin_pool_search(query: Option<&str>) -> Option<String> {
query_param_value(query, "search")
.map(|value| value.trim().to_string())
.filter(|value| !value.is_empty())
}
pub(super) fn parse_admin_pool_status_filter(query: Option<&str>) -> Result<String, String> {
let value = query_param_value(query, "status")
.unwrap_or_else(|| "all".to_string())
.trim()
.to_ascii_lowercase();
match value.as_str() {
"all" | "active" | "inactive" | "cooldown" => Ok(value),
_ => Err("status must be one of: all, active, cooldown, inactive".to_string()),
}
}
pub(super) fn admin_pool_provider_id_from_path(request_path: &str) -> Option<String> {
let raw = request_path.strip_prefix("/api/admin/pool/")?;
let mut segments = raw.split('/');
let provider_id = segments.next()?.trim();
let keys_segment = segments.next()?.trim();
if provider_id.is_empty() || keys_segment != "keys" {
None
} else {
Some(provider_id.to_string())
}
}
pub(super) fn is_admin_pool_route(request_context: &GatewayPublicRequestContext) -> bool {
let normalized_path = request_context.request_path.trim_end_matches('/');
let path = if normalized_path.is_empty() {
request_context.request_path.as_str()
} else {
normalized_path
};
(request_context.request_method == http::Method::GET && path == "/api/admin/pool/overview")
|| (request_context.request_method == http::Method::GET
&& path == "/api/admin/pool/scheduling-presets")
|| (request_context.request_method == http::Method::GET
&& path.starts_with("/api/admin/pool/")
&& path.ends_with("/keys")
&& path.matches('/').count() == 5)
|| (request_context.request_method == http::Method::POST
&& path.starts_with("/api/admin/pool/")
&& path.ends_with("/keys/batch-import")
&& path.matches('/').count() == 6)
|| (request_context.request_method == http::Method::POST
&& path.starts_with("/api/admin/pool/")
&& path.ends_with("/keys/batch-action")
&& path.matches('/').count() == 6)
|| (request_context.request_method == http::Method::POST
&& path.starts_with("/api/admin/pool/")
&& path.ends_with("/keys/resolve-selection")
&& path.matches('/').count() == 6)
|| (request_context.request_method == http::Method::GET
&& path.starts_with("/api/admin/pool/")
&& path.contains("/keys/batch-delete-task/")
&& path.matches('/').count() == 7)
|| (request_context.request_method == http::Method::POST
&& path.starts_with("/api/admin/pool/")
&& path.ends_with("/keys/cleanup-banned")
&& path.matches('/').count() == 6)
}