feat: 扩展 Rust gateway 全功能模块,新增 billing/crypto/wallet crate 及完整数据层

- 新增 aether-billing、aether-crypto、aether-wallet 独立 crate
- aether-data 扩展 repository 层:announcements、auth_modules、billing、
  candidate_selection、gemini_file_mappings、global_models、management_tokens、
  oauth_providers、proxy_nodes、quota、users、wallet 等模块
- aether-gateway 新增 api/auth/billing/control/middleware/scheduler/usage/
  video_tasks/hooks/maintenance/model_fetch/provider_transport 等功能模块
- 重构 executor decision 和 gateway state 为模块目录结构
- 新增 gateway router、frontdoor 路由层及对应测试
- Python 侧 API 路由重构,新增 compat/support 模块
- 前端 Logo 组件更新及 Provider 管理页面调整
This commit is contained in:
fawney19
2026-03-31 19:19:04 +08:00
parent b5a0070023
commit ddf18fed9a
690 changed files with 235087 additions and 16301 deletions

View File

@@ -1,6 +1,54 @@
use std::error::Error as _;
use http::method::InvalidMethod;
use thiserror::Error;
pub(crate) fn format_upstream_request_error(err: &reqwest::Error) -> String {
let mut kinds = Vec::new();
if err.is_connect() {
kinds.push("connect");
}
if err.is_timeout() {
kinds.push("timeout");
}
if err.is_redirect() {
kinds.push("redirect");
}
if err.is_body() {
kinds.push("body");
}
if err.is_decode() {
kinds.push("decode");
}
if err.is_request() {
kinds.push("request");
}
let mut detail = err.to_string();
let mut source = err.source();
while let Some(cause) = source {
let cause_text = cause.to_string();
if !cause_text.is_empty() && !detail.contains(&cause_text) {
detail.push_str(": ");
detail.push_str(&cause_text);
}
source = cause.source();
}
if let Some(url) = err.url() {
detail.push_str(" [url=");
detail.push_str(url.as_str());
detail.push(']');
}
if !kinds.is_empty() {
detail.push_str(" [kind=");
detail.push_str(&kinds.join(","));
detail.push(']');
}
detail
}
#[derive(Debug, Error)]
pub enum ExecutorClientError {
#[error("executor endpoint is not configured")]
@@ -46,7 +94,7 @@ pub enum ExecutorServiceError {
#[error("executor overloaded: gate {gate} saturated at {limit}")]
Overloaded { gate: &'static str, limit: usize },
#[error("failed to execute upstream request: {0}")]
UpstreamRequest(reqwest::Error),
UpstreamRequest(String),
#[error("hub relay request failed: {0}")]
RelayError(String),
#[error("upstream response is not valid JSON: {0}")]

View File

@@ -14,7 +14,7 @@ use reqwest::tls::Version;
use serde::Serialize;
use serde_json::Value;
use crate::ExecutorServiceError;
use crate::{error::format_upstream_request_error, ExecutorServiceError};
const HUB_RELAY_CONTENT_TYPE: &str = "application/vnd.aether.tunnel-envelope";
const HUB_RELAY_ERROR_HEADER: &str = "x-aether-tunnel-error";
@@ -57,10 +57,9 @@ impl SyncExecutor {
let response = send_request(&plan, body_bytes).await?;
let status_code = response.status().as_u16();
let headers = collect_response_headers(response.headers());
let body_bytes = response
.bytes()
.await
.map_err(ExecutorServiceError::UpstreamRequest)?;
let body_bytes = response.bytes().await.map_err(|err| {
ExecutorServiceError::UpstreamRequest(format_upstream_request_error(&err))
})?;
let elapsed_ms = started_at.elapsed().as_millis() as u64;
let upstream_bytes = body_bytes.len() as u64;
@@ -160,7 +159,7 @@ async fn send_request(
request
.send()
.await
.map_err(ExecutorServiceError::UpstreamRequest)
.map_err(|err| ExecutorServiceError::UpstreamRequest(format_upstream_request_error(&err)))
}
async fn send_via_tunnel_relay(