mirror of
https://github.com/fawney19/Aether.git
synced 2026-10-10 19:29:50 +08:00
refactor: 大规模模块拆分与代码精简,新增 ai-pipeline/data-contracts 独立 crate
- 新增 aether-ai-pipeline 和 aether-data-contracts crate,将 pipeline 逻辑与数据契约从 gateway 中解耦 - 重构 admin handlers:拆分单体模块为 auth/billing/endpoint/features/model/observability/provider/system 等独立子模块 - 合并 chat/cli 重复代码路径:精简 conversion、finalize、planner 中的 sync/chat/cli 分支 - 重构 scheduler/executor/data 层,引入 facade 模式降低模块间耦合 - 移除冗余的 intent 模块,将 plan_fallback/policy/stream_path/sync_path 迁移至 executor - 前端适配:调整 admin API 调用和 provider 模型测试对话框
This commit is contained in:
@@ -0,0 +1,335 @@
|
||||
use super::responses::admin_monitoring_bad_request_response;
|
||||
use super::route_filters::{
|
||||
admin_monitoring_escape_like_pattern, parse_admin_monitoring_days,
|
||||
parse_admin_monitoring_event_type_filter, parse_admin_monitoring_hours,
|
||||
parse_admin_monitoring_limit, parse_admin_monitoring_offset,
|
||||
parse_admin_monitoring_username_filter,
|
||||
};
|
||||
use super::usage_helpers::admin_monitoring_usage_is_error;
|
||||
use crate::constants::INTERNAL_GATEWAY_PATH_PREFIXES;
|
||||
use crate::control::GatewayPublicRequestContext;
|
||||
use crate::query::monitoring as monitoring_query;
|
||||
use crate::{AppState, GatewayError};
|
||||
use aether_data_contracts::repository::usage::UsageAuditListQuery;
|
||||
use axum::{
|
||||
body::Body,
|
||||
response::{IntoResponse, Response},
|
||||
Json,
|
||||
};
|
||||
use serde_json::json;
|
||||
|
||||
fn build_admin_monitoring_audit_logs_payload(
|
||||
items: Vec<serde_json::Value>,
|
||||
total: usize,
|
||||
limit: usize,
|
||||
offset: usize,
|
||||
username: Option<String>,
|
||||
event_type: Option<String>,
|
||||
days: i64,
|
||||
) -> Response<Body> {
|
||||
let count = items.len();
|
||||
Json(json!({
|
||||
"items": items,
|
||||
"meta": {
|
||||
"total": total,
|
||||
"limit": limit,
|
||||
"offset": offset,
|
||||
"count": count,
|
||||
},
|
||||
"filters": {
|
||||
"username": username,
|
||||
"event_type": event_type,
|
||||
"days": days,
|
||||
},
|
||||
}))
|
||||
.into_response()
|
||||
}
|
||||
|
||||
fn build_admin_monitoring_suspicious_activities_payload(
|
||||
activities: Vec<serde_json::Value>,
|
||||
hours: i64,
|
||||
) -> Response<Body> {
|
||||
let count = activities.len();
|
||||
Json(json!({
|
||||
"activities": activities,
|
||||
"count": count,
|
||||
"time_range_hours": hours,
|
||||
}))
|
||||
.into_response()
|
||||
}
|
||||
|
||||
fn build_admin_monitoring_user_behavior_payload(
|
||||
user_id: String,
|
||||
days: i64,
|
||||
event_counts: std::collections::BTreeMap<String, u64>,
|
||||
failed_requests: u64,
|
||||
success_requests: u64,
|
||||
suspicious_activities: u64,
|
||||
) -> Response<Body> {
|
||||
let total_requests = success_requests.saturating_add(failed_requests);
|
||||
let success_rate = if total_requests == 0 {
|
||||
0.0
|
||||
} else {
|
||||
success_requests as f64 / total_requests as f64
|
||||
};
|
||||
|
||||
Json(json!({
|
||||
"user_id": user_id,
|
||||
"period_days": days,
|
||||
"event_counts": event_counts,
|
||||
"failed_requests": failed_requests,
|
||||
"success_requests": success_requests,
|
||||
"success_rate": success_rate,
|
||||
"suspicious_activities": suspicious_activities,
|
||||
"analysis_time": chrono::Utc::now().to_rfc3339(),
|
||||
}))
|
||||
.into_response()
|
||||
}
|
||||
|
||||
fn admin_monitoring_user_behavior_user_id_from_path(request_path: &str) -> Option<String> {
|
||||
let value = request_path
|
||||
.strip_prefix("/api/admin/monitoring/user-behavior/")?
|
||||
.trim()
|
||||
.trim_matches('/')
|
||||
.to_string();
|
||||
if value.is_empty() || value.contains('/') {
|
||||
None
|
||||
} else {
|
||||
Some(value)
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) async fn build_admin_monitoring_audit_logs_response(
|
||||
state: &AppState,
|
||||
request_context: &GatewayPublicRequestContext,
|
||||
) -> Result<Response<Body>, GatewayError> {
|
||||
let query = request_context.request_query_string.as_deref();
|
||||
let username = parse_admin_monitoring_username_filter(query);
|
||||
let event_type = parse_admin_monitoring_event_type_filter(query);
|
||||
let limit = match parse_admin_monitoring_limit(query) {
|
||||
Ok(value) => value,
|
||||
Err(detail) => return Ok(admin_monitoring_bad_request_response(detail)),
|
||||
};
|
||||
let offset = match parse_admin_monitoring_offset(query) {
|
||||
Ok(value) => value,
|
||||
Err(detail) => return Ok(admin_monitoring_bad_request_response(detail)),
|
||||
};
|
||||
let days = match parse_admin_monitoring_days(query) {
|
||||
Ok(value) => value,
|
||||
Err(detail) => return Ok(admin_monitoring_bad_request_response(detail)),
|
||||
};
|
||||
|
||||
let Some(pool) = state.postgres_pool() else {
|
||||
return Ok(build_admin_monitoring_audit_logs_payload(
|
||||
Vec::new(),
|
||||
0,
|
||||
limit,
|
||||
offset,
|
||||
username,
|
||||
event_type,
|
||||
days,
|
||||
));
|
||||
};
|
||||
|
||||
let cutoff_time = chrono::Utc::now() - chrono::Duration::days(days);
|
||||
let username_pattern = username
|
||||
.as_deref()
|
||||
.map(admin_monitoring_escape_like_pattern)
|
||||
.map(|value| format!("%{value}%"));
|
||||
|
||||
let (items, total) = monitoring_query::list_admin_audit_logs(
|
||||
&pool,
|
||||
cutoff_time,
|
||||
username_pattern.as_deref(),
|
||||
event_type.as_deref(),
|
||||
limit,
|
||||
offset,
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(build_admin_monitoring_audit_logs_payload(
|
||||
items, total, limit, offset, username, event_type, days,
|
||||
))
|
||||
}
|
||||
|
||||
pub(super) async fn build_admin_monitoring_suspicious_activities_response(
|
||||
state: &AppState,
|
||||
request_context: &GatewayPublicRequestContext,
|
||||
) -> Result<Response<Body>, GatewayError> {
|
||||
let query = request_context.request_query_string.as_deref();
|
||||
let hours = match parse_admin_monitoring_hours(query) {
|
||||
Ok(value) => value,
|
||||
Err(detail) => return Ok(admin_monitoring_bad_request_response(detail)),
|
||||
};
|
||||
|
||||
let Some(pool) = state.postgres_pool() else {
|
||||
return Ok(build_admin_monitoring_suspicious_activities_payload(
|
||||
Vec::new(),
|
||||
hours,
|
||||
));
|
||||
};
|
||||
|
||||
let cutoff_time = chrono::Utc::now() - chrono::Duration::hours(hours);
|
||||
let activities = monitoring_query::list_admin_suspicious_activities(&pool, cutoff_time).await?;
|
||||
|
||||
Ok(build_admin_monitoring_suspicious_activities_payload(
|
||||
activities, hours,
|
||||
))
|
||||
}
|
||||
|
||||
pub(super) async fn build_admin_monitoring_user_behavior_response(
|
||||
state: &AppState,
|
||||
request_context: &GatewayPublicRequestContext,
|
||||
) -> Result<Response<Body>, GatewayError> {
|
||||
let Some(user_id) =
|
||||
admin_monitoring_user_behavior_user_id_from_path(&request_context.request_path)
|
||||
else {
|
||||
return Ok(admin_monitoring_bad_request_response("缺少 user_id"));
|
||||
};
|
||||
let days = match parse_admin_monitoring_days(request_context.request_query_string.as_deref()) {
|
||||
Ok(value) => value,
|
||||
Err(detail) => return Ok(admin_monitoring_bad_request_response(detail)),
|
||||
};
|
||||
|
||||
let Some(pool) = state.postgres_pool() else {
|
||||
return Ok(build_admin_monitoring_user_behavior_payload(
|
||||
user_id,
|
||||
days,
|
||||
std::collections::BTreeMap::new(),
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
));
|
||||
};
|
||||
|
||||
let cutoff_time = chrono::Utc::now() - chrono::Duration::days(days);
|
||||
|
||||
let event_counts =
|
||||
monitoring_query::read_admin_user_behavior_event_counts(&pool, &user_id, cutoff_time)
|
||||
.await?;
|
||||
|
||||
let failed_requests = event_counts
|
||||
.get("request_failed")
|
||||
.copied()
|
||||
.unwrap_or_default();
|
||||
let success_requests = event_counts
|
||||
.get("request_success")
|
||||
.copied()
|
||||
.unwrap_or_default();
|
||||
let suspicious_activities = event_counts
|
||||
.get("suspicious_activity")
|
||||
.copied()
|
||||
.unwrap_or_default()
|
||||
.saturating_add(
|
||||
event_counts
|
||||
.get("unauthorized_access")
|
||||
.copied()
|
||||
.unwrap_or_default(),
|
||||
);
|
||||
|
||||
Ok(build_admin_monitoring_user_behavior_payload(
|
||||
user_id,
|
||||
days,
|
||||
event_counts,
|
||||
failed_requests,
|
||||
success_requests,
|
||||
suspicious_activities,
|
||||
))
|
||||
}
|
||||
|
||||
pub(super) async fn build_admin_monitoring_system_status_response(
|
||||
state: &AppState,
|
||||
) -> Result<Response<Body>, GatewayError> {
|
||||
let now = chrono::Utc::now();
|
||||
let today_start = now
|
||||
.date_naive()
|
||||
.and_hms_opt(0, 0, 0)
|
||||
.expect("midnight should be valid")
|
||||
.and_utc();
|
||||
let recent_error_from = now - chrono::Duration::hours(1);
|
||||
let now_unix_secs = now.timestamp().max(0) as u64;
|
||||
|
||||
let user_summary = state.summarize_export_users().await?;
|
||||
let total_users = user_summary.total;
|
||||
let active_users = user_summary.active;
|
||||
|
||||
let providers = state
|
||||
.data
|
||||
.list_provider_catalog_providers(false)
|
||||
.await
|
||||
.map_err(|err| GatewayError::Internal(err.to_string()))?;
|
||||
let total_providers = providers.len();
|
||||
let active_providers = providers.iter().filter(|item| item.is_active).count();
|
||||
|
||||
let user_api_key_summary = state
|
||||
.summarize_auth_api_key_export_non_standalone_records(now_unix_secs)
|
||||
.await?;
|
||||
let standalone_api_key_summary = state
|
||||
.summarize_auth_api_key_export_standalone_records(now_unix_secs)
|
||||
.await?;
|
||||
let total_api_keys = user_api_key_summary
|
||||
.total
|
||||
.saturating_add(standalone_api_key_summary.total);
|
||||
let active_api_keys = user_api_key_summary
|
||||
.active
|
||||
.saturating_add(standalone_api_key_summary.active);
|
||||
|
||||
let today_usage = state
|
||||
.list_usage_audits(&UsageAuditListQuery {
|
||||
created_from_unix_secs: Some(today_start.timestamp().max(0) as u64),
|
||||
..Default::default()
|
||||
})
|
||||
.await?;
|
||||
let today_requests = today_usage.len();
|
||||
let today_tokens = today_usage
|
||||
.iter()
|
||||
.map(|item| item.total_tokens)
|
||||
.sum::<u64>();
|
||||
let today_cost = today_usage
|
||||
.iter()
|
||||
.map(|item| item.total_cost_usd)
|
||||
.sum::<f64>();
|
||||
|
||||
let recent_errors = state
|
||||
.list_usage_audits(&UsageAuditListQuery {
|
||||
created_from_unix_secs: Some(recent_error_from.timestamp().max(0) as u64),
|
||||
..Default::default()
|
||||
})
|
||||
.await?
|
||||
.into_iter()
|
||||
.filter(admin_monitoring_usage_is_error)
|
||||
.count();
|
||||
let tunnel = state.tunnel.stats();
|
||||
|
||||
Ok(Json(json!({
|
||||
"timestamp": now.to_rfc3339(),
|
||||
"users": {
|
||||
"total": total_users,
|
||||
"active": active_users,
|
||||
},
|
||||
"providers": {
|
||||
"total": total_providers,
|
||||
"active": active_providers,
|
||||
},
|
||||
"api_keys": {
|
||||
"total": total_api_keys,
|
||||
"active": active_api_keys,
|
||||
},
|
||||
"today_stats": {
|
||||
"requests": today_requests,
|
||||
"tokens": today_tokens,
|
||||
"cost_usd": format!("${today_cost:.4}"),
|
||||
},
|
||||
"tunnel": {
|
||||
"proxy_connections": tunnel.proxy_connections,
|
||||
"nodes": tunnel.nodes,
|
||||
"active_streams": tunnel.active_streams,
|
||||
},
|
||||
"internal_gateway": {
|
||||
"status": "rust_native_control_plane",
|
||||
"path_prefixes": INTERNAL_GATEWAY_PATH_PREFIXES,
|
||||
},
|
||||
"recent_errors": recent_errors,
|
||||
}))
|
||||
.into_response())
|
||||
}
|
||||
Reference in New Issue
Block a user