Files
Aether/apps/aether-gateway/src/admin_api.rs
T
yangrsandClaude Opus 4.7 6c16f399d4 feat: 重要通知模块、Server 酱独立配置与额度提醒
- 新增重要通知统一模块(邮件 + Server 酱)作为后台任务通知出口
- 拆出独立的 Server 酱 配置页(SendKey + Markdown 模板,支持 {title}/{body} 变量替换),通过仪表盘内置工具入口进入
- 新增提供商额度提醒后台 worker:余额低于阈值时通过重要通知推送,提供商配置页加入额度提醒开关与阈值
- 重要通知页加入配置可用性守卫:未配置任一通道时禁用总开关,未配置邮件/SendKey 时禁用对应通道开关
- 测试通知端点支持 channel 过滤(all/email/server_chan),并绕过总开关与通道开关,便于配置阶段先验证通道
- 修复:测试通知路由未在 buffered-body 白名单导致 channel 参数丢失、测试时邮件分支被误触发
- 修复:sub2api 验证响应中 username 为 null 时正确回退到 email,避免误报"验证响应缺少: 用户信息"

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-18 23:51:14 +08:00

110 lines
3.7 KiB
Rust

pub(crate) use crate::handlers::admin::{
admin_provider_ops_local_action_response, admin_provider_pool_config,
build_internal_control_error_response, create_provider_oauth_catalog_key,
find_duplicate_provider_oauth_key, maybe_build_local_admin_pool_response,
maybe_build_local_admin_response, persist_provider_quota_refresh_state,
provider_oauth_maintenance_endpoint_for_provider, provider_oauth_runtime_endpoint_for_provider,
provider_quota_refresh_endpoint_for_provider, provider_type_supports_quota_refresh,
reconcile_admin_fixed_provider_template_endpoints,
refresh_provider_oauth_account_state_after_update, refresh_provider_pool_quota_locally,
store_admin_provider_ops_balance_cache, update_existing_provider_oauth_catalog_key,
AdminAppState, AdminGatewayProviderTransportSnapshot, AdminLocalOAuthRefreshError,
AdminRequestContext, AdminRouteRequest, AdminRouteResponse, AdminRouteResult,
AdminStatsTimeRange, AdminStatsUsageFilter, OAUTH_ACCOUNT_BLOCK_PREFIX,
OAUTH_REQUEST_FAILED_PREFIX,
};
use crate::handlers::admin::{
admin_stats_bad_request_response as admin_stats_bad_request_response_impl,
parse_bounded_u32 as parse_bounded_u32_impl, round_to as round_to_impl,
};
use crate::GatewayError;
use axum::{
body::{Body, Bytes},
response::Response,
};
pub(crate) async fn maybe_build_local_admin_security_response(
state: &AdminAppState<'_>,
request_context: &AdminRequestContext<'_>,
request_body: Option<&Bytes>,
) -> Result<Option<Response<Body>>, GatewayError> {
crate::handlers::admin::maybe_build_local_admin_security_response(
state,
request_context,
request_body,
)
.await
}
pub(crate) async fn build_admin_endpoint_health_status_payload(
state: &AdminAppState<'_>,
lookback_hours: u64,
) -> Option<serde_json::Value> {
crate::handlers::admin::build_admin_endpoint_health_status_payload(state, lookback_hours).await
}
pub(crate) async fn maybe_build_local_admin_video_tasks_response(
state: &AdminAppState<'_>,
request_context: &AdminRequestContext<'_>,
) -> Result<Option<Response<Body>>, GatewayError> {
crate::handlers::admin::maybe_build_local_admin_video_tasks_response(state, request_context)
.await
}
pub(crate) async fn maybe_build_local_admin_usage_response(
state: &AdminAppState<'_>,
request_context: &AdminRequestContext<'_>,
request_body: Option<&Bytes>,
) -> Result<Option<Response<Body>>, GatewayError> {
crate::handlers::admin::maybe_build_local_admin_usage_response(
state,
request_context,
request_body,
)
.await
}
pub(crate) fn admin_stats_bad_request_response(detail: String) -> Response<Body> {
admin_stats_bad_request_response_impl(detail)
}
pub(crate) fn parse_bounded_u32(
field: &str,
value: &str,
min: u32,
max: u32,
) -> Result<u32, String> {
parse_bounded_u32_impl(field, value, min, max)
}
pub(crate) fn round_to(value: f64, decimals: u32) -> f64 {
round_to_impl(value, decimals)
}
pub(crate) async fn maybe_build_local_admin_provider_oauth_response(
state: &AdminAppState<'_>,
request_context: &AdminRequestContext<'_>,
request_body: Option<&Bytes>,
) -> Result<Option<Response<Body>>, GatewayError> {
crate::handlers::admin::maybe_build_local_admin_provider_oauth_response(
state,
request_context,
request_body,
)
.await
}
pub(crate) async fn maybe_build_local_admin_providers_response(
state: &AdminAppState<'_>,
request_context: &AdminRequestContext<'_>,
request_body: Option<&Bytes>,
) -> Result<Option<Response<Body>>, GatewayError> {
crate::handlers::admin::maybe_build_local_admin_providers_response(
state,
request_context,
request_body,
)
.await
}