mirror of
https://github.com/fawney19/Aether.git
synced 2026-10-08 02:17:46 +08:00
Shard and singleflight hot-path caches, batch and prioritize candidate and usage lifecycle persistence, and extend database and pressure-test instrumentation for 20k concurrent streams.
219 lines
7.8 KiB
Rust
219 lines
7.8 KiB
Rust
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 crate::constants::INTERNAL_GATEWAY_PATH_PREFIXES;
|
|
use crate::handlers::admin::request::{AdminAppState, AdminRequestContext};
|
|
use crate::handlers::admin::shared::build_admin_usage_counter_health_payload;
|
|
use crate::GatewayError;
|
|
use aether_admin::observability::monitoring::{
|
|
admin_monitoring_bad_request_response, admin_monitoring_user_behavior_user_id_from_path,
|
|
build_admin_monitoring_audit_logs_payload_response,
|
|
build_admin_monitoring_suspicious_activities_payload_response,
|
|
build_admin_monitoring_system_status_payload_response,
|
|
build_admin_monitoring_user_behavior_payload_response,
|
|
};
|
|
use aether_data_contracts::repository::usage::{
|
|
UsageAuditSummaryQuery, UsageMonitoringErrorCountQuery,
|
|
};
|
|
use axum::{body::Body, response::Response};
|
|
|
|
pub(super) async fn build_admin_monitoring_audit_logs_response(
|
|
state: &AdminAppState<'_>,
|
|
request_context: &AdminRequestContext<'_>,
|
|
) -> Result<Response<Body>, GatewayError> {
|
|
let state = state.as_ref();
|
|
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 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) = state
|
|
.list_admin_audit_logs(
|
|
cutoff_time,
|
|
username_pattern.as_deref(),
|
|
event_type.as_deref(),
|
|
limit,
|
|
offset,
|
|
)
|
|
.await?;
|
|
|
|
Ok(build_admin_monitoring_audit_logs_payload_response(
|
|
items, total, limit, offset, username, event_type, days,
|
|
))
|
|
}
|
|
|
|
pub(super) async fn build_admin_monitoring_suspicious_activities_response(
|
|
state: &AdminAppState<'_>,
|
|
request_context: &AdminRequestContext<'_>,
|
|
) -> Result<Response<Body>, GatewayError> {
|
|
let state = state.as_ref();
|
|
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 cutoff_time = chrono::Utc::now() - chrono::Duration::hours(hours);
|
|
let activities = state.list_admin_suspicious_activities(cutoff_time).await?;
|
|
|
|
Ok(build_admin_monitoring_suspicious_activities_payload_response(activities, hours))
|
|
}
|
|
|
|
pub(super) async fn build_admin_monitoring_user_behavior_response(
|
|
state: &AdminAppState<'_>,
|
|
request_context: &AdminRequestContext<'_>,
|
|
) -> Result<Response<Body>, GatewayError> {
|
|
let state = state.as_ref();
|
|
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 cutoff_time = chrono::Utc::now() - chrono::Duration::days(days);
|
|
|
|
let event_counts = state
|
|
.read_admin_user_behavior_event_counts(&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_response(
|
|
user_id,
|
|
days,
|
|
event_counts,
|
|
failed_requests,
|
|
success_requests,
|
|
suspicious_activities,
|
|
))
|
|
}
|
|
|
|
pub(super) async fn build_admin_monitoring_system_status_response(
|
|
state: &AdminAppState<'_>,
|
|
) -> Result<Response<Body>, GatewayError> {
|
|
let state = state.as_ref();
|
|
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
|
|
.summarize_usage_audits(&UsageAuditSummaryQuery {
|
|
created_from_unix_secs: today_start.timestamp().max(0) as u64,
|
|
created_until_unix_secs: now_unix_secs.saturating_add(1),
|
|
user_id: None,
|
|
provider_name: None,
|
|
model: None,
|
|
})
|
|
.await?;
|
|
let today_requests = usize::try_from(today_usage.total_requests).unwrap_or(usize::MAX);
|
|
let today_tokens = today_usage.recorded_total_tokens;
|
|
let today_cost = today_usage.total_cost_usd;
|
|
|
|
let recent_errors = usize::try_from(
|
|
state
|
|
.count_monitoring_usage_errors(&UsageMonitoringErrorCountQuery {
|
|
created_from_unix_secs: recent_error_from.timestamp().max(0) as u64,
|
|
created_until_unix_secs: now_unix_secs.saturating_add(1),
|
|
})
|
|
.await?,
|
|
)
|
|
.unwrap_or(usize::MAX);
|
|
let tunnel = state.tunnel.stats();
|
|
let usage_counter_snapshot = state
|
|
.read_cached_usage_counter_health()
|
|
.await
|
|
.map_err(|err| GatewayError::Internal(err.to_string()))?;
|
|
let usage_counter =
|
|
build_admin_usage_counter_health_payload(&usage_counter_snapshot, now_unix_secs);
|
|
|
|
Ok(build_admin_monitoring_system_status_payload_response(
|
|
now,
|
|
total_users,
|
|
active_users,
|
|
total_providers,
|
|
active_providers,
|
|
total_api_keys,
|
|
active_api_keys,
|
|
today_requests,
|
|
today_tokens,
|
|
today_cost,
|
|
tunnel.proxy_connections,
|
|
tunnel.nodes,
|
|
tunnel.active_streams,
|
|
INTERNAL_GATEWAY_PATH_PREFIXES,
|
|
recent_errors,
|
|
usage_counter,
|
|
))
|
|
}
|