mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 17:30:23 +08:00
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:
@@ -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}")]
|
||||
|
||||
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user