mirror of
https://github.com/fawney19/Aether.git
synced 2026-10-12 04:09:48 +08:00
refactor: 大规模模块拆分与代码精简,新增 ai-pipeline/data-contracts 独立 crate
- 新增 aether-ai-pipeline 和 aether-data-contracts crate,将 pipeline 逻辑与数据契约从 gateway 中解耦 - 重构 admin handlers:拆分单体模块为 auth/billing/endpoint/features/model/observability/provider/system 等独立子模块 - 合并 chat/cli 重复代码路径:精简 conversion、finalize、planner 中的 sync/chat/cli 分支 - 重构 scheduler/executor/data 层,引入 facade 模式降低模块间耦合 - 移除冗余的 intent 模块,将 plan_fallback/policy/stream_path/sync_path 迁移至 executor - 前端适配:调整 admin API 调用和 provider 模型测试对话框
This commit is contained in:
@@ -0,0 +1,325 @@
|
||||
use super::oauth_config::{
|
||||
admin_oauth_provider_type_from_path, admin_oauth_test_provider_type_from_path,
|
||||
build_admin_oauth_provider_payload, build_admin_oauth_supported_types_payload,
|
||||
build_admin_oauth_upsert_record, build_proxy_error_response, AdminOAuthProviderUpsertRequest,
|
||||
};
|
||||
use crate::control::GatewayPublicRequestContext;
|
||||
use crate::handlers::admin::shared::attach_admin_audit_response;
|
||||
use crate::{AppState, GatewayError};
|
||||
use axum::{
|
||||
body::{Body, Bytes},
|
||||
http,
|
||||
response::{IntoResponse, Response},
|
||||
Json,
|
||||
};
|
||||
use serde_json::json;
|
||||
|
||||
pub(crate) async fn maybe_build_local_admin_oauth_response(
|
||||
state: &AppState,
|
||||
request_context: &GatewayPublicRequestContext,
|
||||
request_body: Option<&Bytes>,
|
||||
) -> Result<Option<Response<Body>>, GatewayError> {
|
||||
let Some(decision) = request_context.control_decision.as_ref() else {
|
||||
return Ok(None);
|
||||
};
|
||||
if decision.route_family.as_deref() != Some("oauth_manage") {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
if decision.route_kind.as_deref() == Some("supported_types")
|
||||
&& request_context.request_method == http::Method::GET
|
||||
&& request_context.request_path == "/api/admin/oauth/supported-types"
|
||||
{
|
||||
return Ok(Some(
|
||||
Json(build_admin_oauth_supported_types_payload()).into_response(),
|
||||
));
|
||||
}
|
||||
|
||||
if decision.route_kind.as_deref() == Some("list_providers")
|
||||
&& request_context.request_method == http::Method::GET
|
||||
&& matches!(
|
||||
request_context.request_path.as_str(),
|
||||
"/api/admin/oauth/providers" | "/api/admin/oauth/providers/"
|
||||
)
|
||||
{
|
||||
let providers = state.list_oauth_provider_configs().await?;
|
||||
return Ok(Some(attach_admin_audit_response(
|
||||
Json(
|
||||
providers
|
||||
.iter()
|
||||
.map(build_admin_oauth_provider_payload)
|
||||
.collect::<Vec<_>>(),
|
||||
)
|
||||
.into_response(),
|
||||
"admin_oauth_provider_configs_viewed",
|
||||
"list_oauth_provider_configs",
|
||||
"oauth_provider",
|
||||
"all",
|
||||
)));
|
||||
}
|
||||
|
||||
if decision.route_kind.as_deref() == Some("get_provider")
|
||||
&& request_context.request_method == http::Method::GET
|
||||
{
|
||||
let Some(provider_type) =
|
||||
admin_oauth_provider_type_from_path(&request_context.request_path)
|
||||
else {
|
||||
return Ok(Some(
|
||||
(
|
||||
http::StatusCode::NOT_FOUND,
|
||||
Json(json!({ "detail": "Provider 配置不存在" })),
|
||||
)
|
||||
.into_response(),
|
||||
));
|
||||
};
|
||||
return Ok(Some(
|
||||
match state.get_oauth_provider_config(&provider_type).await? {
|
||||
Some(provider) => attach_admin_audit_response(
|
||||
Json(build_admin_oauth_provider_payload(&provider)).into_response(),
|
||||
"admin_oauth_provider_config_viewed",
|
||||
"view_oauth_provider_config",
|
||||
"oauth_provider",
|
||||
&provider_type,
|
||||
),
|
||||
None => (
|
||||
http::StatusCode::NOT_FOUND,
|
||||
Json(json!({ "detail": "Provider 配置不存在" })),
|
||||
)
|
||||
.into_response(),
|
||||
},
|
||||
));
|
||||
}
|
||||
|
||||
if decision.route_kind.as_deref() == Some("upsert_provider")
|
||||
&& request_context.request_method == http::Method::PUT
|
||||
{
|
||||
let Some(provider_type) =
|
||||
admin_oauth_provider_type_from_path(&request_context.request_path)
|
||||
else {
|
||||
return Ok(Some(build_proxy_error_response(
|
||||
http::StatusCode::BAD_REQUEST,
|
||||
"invalid_request",
|
||||
"Provider 配置不存在",
|
||||
None,
|
||||
)));
|
||||
};
|
||||
let Some(request_body) = request_body else {
|
||||
return Ok(Some(build_proxy_error_response(
|
||||
http::StatusCode::BAD_REQUEST,
|
||||
"invalid_request",
|
||||
"请求数据验证失败",
|
||||
None,
|
||||
)));
|
||||
};
|
||||
let payload = match serde_json::from_slice::<AdminOAuthProviderUpsertRequest>(request_body)
|
||||
{
|
||||
Ok(payload) => payload,
|
||||
Err(_) => {
|
||||
return Ok(Some(build_proxy_error_response(
|
||||
http::StatusCode::BAD_REQUEST,
|
||||
"invalid_request",
|
||||
"请求数据验证失败",
|
||||
None,
|
||||
)));
|
||||
}
|
||||
};
|
||||
let existing = state.get_oauth_provider_config(&provider_type).await?;
|
||||
let ldap_exclusive = state.get_ldap_module_config().await?.is_some_and(|config| {
|
||||
config.is_enabled
|
||||
&& config.is_exclusive
|
||||
&& config
|
||||
.bind_password_encrypted
|
||||
.as_deref()
|
||||
.map(str::trim)
|
||||
.is_some_and(|value| !value.is_empty())
|
||||
});
|
||||
if existing
|
||||
.as_ref()
|
||||
.is_some_and(|provider| provider.is_enabled && !payload.is_enabled)
|
||||
{
|
||||
let affected_count = state
|
||||
.count_locked_users_if_oauth_provider_disabled(&provider_type, ldap_exclusive)
|
||||
.await?;
|
||||
if affected_count > 0 && !payload.force {
|
||||
return Ok(Some(build_proxy_error_response(
|
||||
http::StatusCode::CONFLICT,
|
||||
"confirmation_required",
|
||||
format!("禁用该 Provider 会导致 {affected_count} 个用户无法登录"),
|
||||
Some(json!({
|
||||
"affected_count": affected_count,
|
||||
"action": "disable_oauth_provider",
|
||||
})),
|
||||
)));
|
||||
}
|
||||
}
|
||||
let record = match build_admin_oauth_upsert_record(state, &provider_type, payload) {
|
||||
Ok(record) => record,
|
||||
Err(message) => {
|
||||
return Ok(Some(build_proxy_error_response(
|
||||
http::StatusCode::BAD_REQUEST,
|
||||
"invalid_request",
|
||||
message,
|
||||
None,
|
||||
)));
|
||||
}
|
||||
};
|
||||
let Some(provider) = state.upsert_oauth_provider_config(&record).await? else {
|
||||
return Ok(None);
|
||||
};
|
||||
return Ok(Some(
|
||||
Json(build_admin_oauth_provider_payload(&provider)).into_response(),
|
||||
));
|
||||
}
|
||||
|
||||
if decision.route_kind.as_deref() == Some("delete_provider")
|
||||
&& request_context.request_method == http::Method::DELETE
|
||||
{
|
||||
let Some(provider_type) =
|
||||
admin_oauth_provider_type_from_path(&request_context.request_path)
|
||||
else {
|
||||
return Ok(Some(build_proxy_error_response(
|
||||
http::StatusCode::BAD_REQUEST,
|
||||
"invalid_request",
|
||||
"Provider 配置不存在",
|
||||
None,
|
||||
)));
|
||||
};
|
||||
let Some(existing) = state.get_oauth_provider_config(&provider_type).await? else {
|
||||
return Ok(Some(build_proxy_error_response(
|
||||
http::StatusCode::BAD_REQUEST,
|
||||
"invalid_request",
|
||||
"Provider 配置不存在",
|
||||
None,
|
||||
)));
|
||||
};
|
||||
if existing.is_enabled {
|
||||
let ldap_exclusive = state.get_ldap_module_config().await?.is_some_and(|config| {
|
||||
config.is_enabled
|
||||
&& config.is_exclusive
|
||||
&& config
|
||||
.bind_password_encrypted
|
||||
.as_deref()
|
||||
.map(str::trim)
|
||||
.is_some_and(|value| !value.is_empty())
|
||||
});
|
||||
let affected_count = state
|
||||
.count_locked_users_if_oauth_provider_disabled(&provider_type, ldap_exclusive)
|
||||
.await?;
|
||||
if affected_count > 0 {
|
||||
return Ok(Some(build_proxy_error_response(
|
||||
http::StatusCode::BAD_REQUEST,
|
||||
"invalid_request",
|
||||
format!(
|
||||
"删除该 Provider 会导致部分用户无法登录(数量: {affected_count}),已阻止操作"
|
||||
),
|
||||
None,
|
||||
)));
|
||||
}
|
||||
}
|
||||
let deleted = state.delete_oauth_provider_config(&provider_type).await?;
|
||||
if !deleted {
|
||||
return Ok(Some(build_proxy_error_response(
|
||||
http::StatusCode::BAD_REQUEST,
|
||||
"invalid_request",
|
||||
"Provider 配置不存在",
|
||||
None,
|
||||
)));
|
||||
}
|
||||
return Ok(Some(Json(json!({ "message": "删除成功" })).into_response()));
|
||||
}
|
||||
|
||||
if decision.route_kind.as_deref() == Some("test_provider")
|
||||
&& request_context.request_method == http::Method::POST
|
||||
{
|
||||
let Some(provider_type) =
|
||||
admin_oauth_test_provider_type_from_path(&request_context.request_path)
|
||||
else {
|
||||
return Ok(Some(
|
||||
(
|
||||
http::StatusCode::NOT_FOUND,
|
||||
Json(json!({ "detail": "Provider 配置不存在" })),
|
||||
)
|
||||
.into_response(),
|
||||
));
|
||||
};
|
||||
let Some(request_body) = request_body else {
|
||||
return Ok(Some(
|
||||
(
|
||||
http::StatusCode::BAD_REQUEST,
|
||||
Json(json!({ "detail": "请求数据验证失败" })),
|
||||
)
|
||||
.into_response(),
|
||||
));
|
||||
};
|
||||
let payload = match serde_json::from_slice::<serde_json::Value>(request_body) {
|
||||
Ok(payload) => payload,
|
||||
Err(_) => {
|
||||
return Ok(Some(
|
||||
(
|
||||
http::StatusCode::BAD_REQUEST,
|
||||
Json(json!({ "detail": "请求数据验证失败" })),
|
||||
)
|
||||
.into_response(),
|
||||
));
|
||||
}
|
||||
};
|
||||
let client_id = payload
|
||||
.get("client_id")
|
||||
.and_then(serde_json::Value::as_str)
|
||||
.map(str::trim)
|
||||
.filter(|value| !value.is_empty());
|
||||
let redirect_uri = payload
|
||||
.get("redirect_uri")
|
||||
.and_then(serde_json::Value::as_str)
|
||||
.map(str::trim)
|
||||
.filter(|value| !value.is_empty());
|
||||
if client_id.is_none() || redirect_uri.is_none() {
|
||||
return Ok(Some(
|
||||
(
|
||||
http::StatusCode::BAD_REQUEST,
|
||||
Json(json!({ "detail": "请求数据验证失败" })),
|
||||
)
|
||||
.into_response(),
|
||||
));
|
||||
}
|
||||
let provided_secret = payload
|
||||
.get("client_secret")
|
||||
.and_then(serde_json::Value::as_str)
|
||||
.map(str::trim)
|
||||
.filter(|value| !value.is_empty());
|
||||
let persisted_secret = state
|
||||
.get_oauth_provider_config(&provider_type)
|
||||
.await?
|
||||
.and_then(|provider| provider.client_secret_encrypted);
|
||||
let supported_provider = provider_type.eq_ignore_ascii_case("linuxdo");
|
||||
let secret_status = if supported_provider {
|
||||
if provided_secret.is_some() || persisted_secret.is_some() {
|
||||
"unsupported"
|
||||
} else {
|
||||
"not_provided"
|
||||
}
|
||||
} else {
|
||||
"unknown"
|
||||
};
|
||||
let details = if supported_provider {
|
||||
"OAuth 配置测试仅支持 Rust execution runtime"
|
||||
} else {
|
||||
"provider 未安装/不可用"
|
||||
};
|
||||
return Ok(Some(attach_admin_audit_response(
|
||||
Json(json!({
|
||||
"authorization_url_reachable": false,
|
||||
"token_url_reachable": false,
|
||||
"secret_status": secret_status,
|
||||
"details": details,
|
||||
}))
|
||||
.into_response(),
|
||||
"admin_oauth_provider_tested",
|
||||
"test_oauth_provider_config",
|
||||
"oauth_provider",
|
||||
&provider_type,
|
||||
)));
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
Reference in New Issue
Block a user