mirror of
https://github.com/fawney19/Aether.git
synced 2026-10-09 10:57:03 +08:00
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:
@@ -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)
|
||||
}
|
||||
Reference in New Issue
Block a user