mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 17:30:23 +08:00
refactor: 拆分 gateway 单体为独立 crate,新增 systemd 部署方案
将 gateway 内部的 model-fetch、provider-transport、scheduler-core、 usage-runtime、video-tasks-core 模块提取为独立 crate;重构 gateway 内部模块结构(state/router/cache/data/query 等);移除大量遗留模块 文件;新增 systemd 二进制部署骨架及相关文档;更新前端 usage 相关 API 和组件。
This commit is contained in:
131
apps/aether-gateway/src/audit/admin.rs
Normal file
131
apps/aether-gateway/src/audit/admin.rs
Normal file
@@ -0,0 +1,131 @@
|
||||
use axum::body::Body;
|
||||
use axum::http::{self, Response};
|
||||
use tracing::{info, warn};
|
||||
|
||||
use crate::control::GatewayControlDecision;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct AdminAuditEvent {
|
||||
pub(crate) event_name: &'static str,
|
||||
pub(crate) action: &'static str,
|
||||
pub(crate) target_type: &'static str,
|
||||
pub(crate) target_id: String,
|
||||
}
|
||||
|
||||
pub(crate) fn attach_admin_audit_event(
|
||||
response: &mut Response<Body>,
|
||||
event_name: &'static str,
|
||||
action: &'static str,
|
||||
target_type: &'static str,
|
||||
target_id: impl Into<String>,
|
||||
) {
|
||||
response.extensions_mut().insert(AdminAuditEvent {
|
||||
event_name,
|
||||
action,
|
||||
target_type,
|
||||
target_id: target_id.into(),
|
||||
});
|
||||
}
|
||||
|
||||
pub(crate) fn emit_admin_audit(
|
||||
response: &mut Response<Body>,
|
||||
trace_id: &str,
|
||||
method: &http::Method,
|
||||
path_and_query: &str,
|
||||
control_decision: Option<&GatewayControlDecision>,
|
||||
) {
|
||||
let Some(decision) = control_decision else {
|
||||
return;
|
||||
};
|
||||
let Some(admin_principal) = decision.admin_principal.as_ref() else {
|
||||
return;
|
||||
};
|
||||
|
||||
let attached_event = response.extensions_mut().remove::<AdminAuditEvent>();
|
||||
let route_family = decision.route_family.as_deref().unwrap_or("unknown");
|
||||
let route_kind = decision.route_kind.as_deref().unwrap_or("unknown");
|
||||
let status_code = response.status().as_u16();
|
||||
if attached_event.is_none() && !is_admin_mutation_method(method) {
|
||||
return;
|
||||
}
|
||||
let (event_name, action, target_type, target_id) = if let Some(event) = attached_event {
|
||||
(
|
||||
event.event_name,
|
||||
event.action,
|
||||
event.target_type,
|
||||
event.target_id,
|
||||
)
|
||||
} else {
|
||||
(
|
||||
if response.status().is_success() {
|
||||
"admin_mutation_completed"
|
||||
} else {
|
||||
"admin_mutation_failed"
|
||||
},
|
||||
route_kind,
|
||||
default_target_type(route_family),
|
||||
path_and_query.to_string(),
|
||||
)
|
||||
};
|
||||
|
||||
let audit_status = if response.status().is_success() {
|
||||
"completed"
|
||||
} else {
|
||||
"failed"
|
||||
};
|
||||
if response.status().is_success() {
|
||||
info!(
|
||||
event_name,
|
||||
log_type = "audit",
|
||||
status = audit_status,
|
||||
status_code,
|
||||
trace_id = %trace_id,
|
||||
admin_user_id = admin_principal.user_id.as_str(),
|
||||
admin_user_role = admin_principal.user_role.as_str(),
|
||||
admin_session_id = admin_principal.session_id.as_deref().unwrap_or("-"),
|
||||
admin_management_token_id = admin_principal.management_token_id.as_deref().unwrap_or("-"),
|
||||
route_family,
|
||||
route_kind,
|
||||
method = %method,
|
||||
path = %path_and_query,
|
||||
action,
|
||||
target_type,
|
||||
target_id = %target_id,
|
||||
"admin mutation audit event"
|
||||
);
|
||||
} else {
|
||||
warn!(
|
||||
event_name,
|
||||
log_type = "audit",
|
||||
status = audit_status,
|
||||
status_code,
|
||||
trace_id = %trace_id,
|
||||
admin_user_id = admin_principal.user_id.as_str(),
|
||||
admin_user_role = admin_principal.user_role.as_str(),
|
||||
admin_session_id = admin_principal.session_id.as_deref().unwrap_or("-"),
|
||||
admin_management_token_id = admin_principal.management_token_id.as_deref().unwrap_or("-"),
|
||||
route_family,
|
||||
route_kind,
|
||||
method = %method,
|
||||
path = %path_and_query,
|
||||
action,
|
||||
target_type,
|
||||
target_id = %target_id,
|
||||
"admin mutation audit event"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn default_target_type(route_family: &str) -> &str {
|
||||
route_family
|
||||
.strip_suffix("_manage")
|
||||
.unwrap_or(route_family)
|
||||
.trim()
|
||||
}
|
||||
|
||||
fn is_admin_mutation_method(method: &http::Method) -> bool {
|
||||
matches!(
|
||||
*method,
|
||||
http::Method::POST | http::Method::PUT | http::Method::PATCH | http::Method::DELETE
|
||||
)
|
||||
}
|
||||
@@ -6,7 +6,7 @@ use axum::Json;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::json;
|
||||
|
||||
use crate::gateway::{AppState, GatewayError};
|
||||
use crate::{AppState, GatewayError};
|
||||
|
||||
const DEFAULT_RECENT_LIMIT: usize = 20;
|
||||
const MAX_RECENT_LIMIT: usize = 200;
|
||||
@@ -80,7 +80,10 @@ pub(crate) async fn get_request_candidate_trace(
|
||||
State(state): State<AppState>,
|
||||
Path(request_id): Path<String>,
|
||||
Query(query): Query<GetRequestCandidateTraceQuery>,
|
||||
) -> Result<Json<crate::gateway::gateway_data::RequestCandidateTrace>, axum::response::Response> {
|
||||
) -> Result<
|
||||
Json<crate::data::candidates::RequestCandidateTrace>,
|
||||
axum::response::Response,
|
||||
> {
|
||||
let attempted_only = query.attempted_only.unwrap_or(false);
|
||||
let trace = state
|
||||
.read_request_candidate_trace(&request_id, attempted_only)
|
||||
@@ -105,7 +108,10 @@ pub(crate) async fn get_decision_trace(
|
||||
State(state): State<AppState>,
|
||||
Path(request_id): Path<String>,
|
||||
Query(query): Query<GetRequestCandidateTraceQuery>,
|
||||
) -> Result<Json<crate::gateway::gateway_data::DecisionTrace>, axum::response::Response> {
|
||||
) -> Result<
|
||||
Json<crate::data::decision_trace::DecisionTrace>,
|
||||
axum::response::Response,
|
||||
> {
|
||||
let attempted_only = query.attempted_only.unwrap_or(false);
|
||||
let trace = state
|
||||
.read_decision_trace(&request_id, attempted_only)
|
||||
@@ -130,7 +136,7 @@ pub(crate) async fn get_auth_api_key_snapshot(
|
||||
State(state): State<AppState>,
|
||||
Path((user_id, api_key_id)): Path<(String, String)>,
|
||||
) -> Result<
|
||||
Json<crate::gateway::gateway_data::StoredGatewayAuthApiKeySnapshot>,
|
||||
Json<crate::data::auth::GatewayAuthApiKeySnapshot>,
|
||||
axum::response::Response,
|
||||
> {
|
||||
let snapshot = state
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
mod admin;
|
||||
mod http;
|
||||
mod shadow;
|
||||
|
||||
pub(crate) use admin::{attach_admin_audit_event, emit_admin_audit, AdminAuditEvent};
|
||||
pub(crate) use http::get_auth_api_key_snapshot;
|
||||
pub(crate) use http::get_decision_trace;
|
||||
pub(crate) use http::get_request_candidate_trace;
|
||||
|
||||
@@ -8,11 +8,12 @@ use axum::http::header::CONTENT_TYPE;
|
||||
use axum::http::Response;
|
||||
use tracing::warn;
|
||||
|
||||
use crate::gateway::constants::{
|
||||
use crate::constants::{
|
||||
CONTROL_CANDIDATE_ID_HEADER, CONTROL_REQUEST_ID_HEADER, EXECUTION_PATH_CONTROL_EXECUTE_STREAM,
|
||||
EXECUTION_PATH_CONTROL_EXECUTE_SYNC,
|
||||
};
|
||||
use crate::gateway::{AppState, GatewayControlDecision};
|
||||
use crate::control::GatewayControlDecision;
|
||||
use crate::AppState;
|
||||
|
||||
pub(crate) fn record_shadow_result_non_blocking(
|
||||
state: AppState,
|
||||
@@ -130,11 +131,12 @@ mod tests {
|
||||
use axum::http::{Method, Response, StatusCode};
|
||||
|
||||
use super::record_shadow_result_non_blocking;
|
||||
use crate::gateway::constants::{
|
||||
use crate::constants::{
|
||||
CONTROL_REQUEST_ID_HEADER, EXECUTION_PATH_CONTROL_EXECUTE_SYNC,
|
||||
EXECUTION_PATH_EXECUTION_RUNTIME_SYNC,
|
||||
};
|
||||
use crate::gateway::{AppState, GatewayControlDecision};
|
||||
use crate::control::GatewayControlDecision;
|
||||
use crate::AppState;
|
||||
|
||||
fn sample_decision() -> GatewayControlDecision {
|
||||
GatewayControlDecision {
|
||||
|
||||
Reference in New Issue
Block a user