feat: 全栈功能增强 - 扩展 provider/pool 管理、完善调度与数据层、重构前端 Pool 页面

后端:
- 扩展 pool_admin payloads 和 provider query models,增强 endpoint key 管理
- 完善 scheduler-core 候选排序与请求候选逻辑
- 增强 usage-runtime 写入、provider-transport 网络层与 OAuth 刷新
- 改进 AI pipeline 响应转换与流式处理
- 扩展 global_models/provider_catalog 数据层查询能力
- 增强 video-tasks-core 多 provider 支持
- 新增大量集成测试覆盖 pool/keys/provider_query/frontdoor

前端:
- 重构 PoolManagement 页面,拆分状态管理/对话框逻辑到独立模块
- 新增 poolAdvancedDialog/poolSchedulingDialog/poolManagementState/poolMobilePresentation 工具函数及测试
- 改进 Dialog 组件与 provider tabs 显示

部署:
- 更新 Rust CI workflow 和 Dockerfile 构建配置

Closes #275
Co-authored-by: AAEE86 <[email protected]>
This commit is contained in:
fawney19
2026-04-09 13:51:50 +08:00
parent 4fc95adfb9
commit b0b40c16ff
97 changed files with 5816 additions and 1881 deletions
+83
View File
@@ -1,5 +1,13 @@
use std::path::PathBuf;
use axum::extract::Request;
use axum::http::Method;
use axum::response::{IntoResponse, Response};
use axum::routing::any;
use axum::Router;
use tower::ServiceExt;
use tower_http::services::{ServeDir, ServeFile};
use tracing::warn;
use aether_runtime::{prometheus_response, ConcurrencyError, DistributedConcurrencyError};
@@ -9,6 +17,12 @@ pub fn build_router() -> Result<Router, reqwest::Error> {
Ok(build_router_with_state(AppState::new()?))
}
#[derive(Clone, Debug)]
struct FrontendStaticState {
static_dir: PathBuf,
index_html: PathBuf,
}
pub fn build_router_with_state(state: AppState) -> Router {
let cors_state = state.clone();
let mut router = Router::<AppState>::new();
@@ -32,6 +46,75 @@ pub fn build_router_with_state(state: AppState) -> Router {
router
}
pub fn attach_static_frontend(router: Router, static_dir: impl Into<PathBuf>) -> Router {
let static_dir = static_dir.into();
let index_html = static_dir.join("index.html");
router.layer(axum::middleware::from_fn_with_state(
FrontendStaticState {
static_dir,
index_html,
},
frontend_static_middleware,
))
}
async fn frontend_static_middleware(
axum::extract::State(frontend): axum::extract::State<FrontendStaticState>,
request: Request,
next: axum::middleware::Next,
) -> Response {
let path = request.uri().path().to_string();
if !matches!(request.method(), &Method::GET | &Method::HEAD)
|| frontend_path_bypasses_static(&path)
{
return next.run(request).await;
}
if frontend_path_targets_static_asset(&path) {
return serve_static_asset(&frontend.static_dir, request).await;
}
serve_frontend_index(&frontend.index_html, request).await
}
fn frontend_path_bypasses_static(path: &str) -> bool {
matches!(
path,
"/health" | "/test-connection" | crate::constants::READYZ_PATH
) || path.starts_with("/api/")
|| path.starts_with("/v1/")
|| path.starts_with("/v1beta/")
|| path.starts_with("/upload/")
|| path.starts_with("/_gateway/")
|| path.starts_with("/.well-known/")
}
fn frontend_path_targets_static_asset(path: &str) -> bool {
path.rsplit('/')
.next()
.is_some_and(|segment| !segment.is_empty() && segment.contains('.'))
}
async fn serve_static_asset(static_dir: &PathBuf, request: Request) -> Response {
match ServeDir::new(static_dir).oneshot(request).await {
Ok(response) => response.into_response(),
Err(err) => {
warn!(error = %err, "failed to serve frontend static asset");
axum::http::StatusCode::INTERNAL_SERVER_ERROR.into_response()
}
}
}
async fn serve_frontend_index(index_html: &PathBuf, request: Request) -> Response {
match ServeFile::new(index_html).oneshot(request).await {
Ok(response) => response.into_response(),
Err(err) => {
warn!(error = %err, "failed to serve frontend index");
axum::http::StatusCode::INTERNAL_SERVER_ERROR.into_response()
}
}
}
pub(crate) async fn metrics(
axum::extract::State(state): axum::extract::State<AppState>,
) -> impl axum::response::IntoResponse {