feat(oauth): 完善账号异常识别并在调度/展示层拦截失效 OAuth 密钥

- 新增 aether-admin provider status 模块,统一解析账号状态(禁用/工作区停用等)
- 调度器 runtime 增加 oauth_invalid 判定,跳过刷新失败或已撤销的 OAuth 密钥(REQUEST_FAILED 保留可选)
- gateway state 在 local oauth 刷新返回 4xx 时持久化失败原因并同步状态快照
- admin pool 列表/详情回填 account 状态与 scheduling 阻塞原因(account_blocked)
- 共享 catalog 的 status_snapshot payload 附加 account 字段
This commit is contained in:
fawney19
2026-04-19 20:50:31 +08:00
parent d719a1329c
commit 77aac74590
12 changed files with 1409 additions and 60 deletions
+23 -32
View File
@@ -6,6 +6,8 @@ use chrono::{TimeZone, Utc};
use serde_json::{json, Value};
use std::collections::{BTreeMap, BTreeSet};
use super::status as provider_status;
#[derive(Debug, Default, Clone, serde::Deserialize)]
pub struct AdminPoolResolveSelectionRequest {
#[serde(default)]
@@ -461,40 +463,17 @@ pub fn admin_pool_matches_search(
}
pub fn admin_pool_key_is_known_banned(key: &StoredProviderCatalogKey) -> bool {
if key
.oauth_invalid_reason
.as_deref()
.is_some_and(admin_pool_reason_indicates_ban)
{
let state = provider_status::resolve_pool_account_state(
None,
key.upstream_metadata.as_ref(),
key.oauth_invalid_reason.as_deref(),
);
if provider_status::account_state_indicates_known_ban(&state) {
return true;
}
let Some(account) = key
.status_snapshot
.as_ref()
.and_then(Value::as_object)
.and_then(|snapshot| snapshot.get("account"))
.and_then(Value::as_object)
else {
return false;
};
if !account
.get("blocked")
.and_then(Value::as_bool)
.unwrap_or(false)
{
return false;
}
account
.get("code")
.and_then(Value::as_str)
key.oauth_invalid_reason
.as_deref()
.is_some_and(admin_pool_reason_indicates_ban)
|| account
.get("reason")
.and_then(Value::as_str)
.is_some_and(admin_pool_reason_indicates_ban)
}
pub fn admin_pool_sort_keys(keys: &mut [StoredProviderCatalogKey]) {
@@ -665,7 +644,7 @@ pub fn build_admin_pool_selection_payload(keys: &[StoredProviderCatalogKey]) ->
#[cfg(test)]
#[allow(clippy::items_after_test_module)]
mod tests {
use super::admin_pool_key_account_quota_exhausted;
use super::{admin_pool_key_account_quota_exhausted, admin_pool_key_is_known_banned};
use aether_data_contracts::repository::provider_catalog::StoredProviderCatalogKey;
use serde_json::json;
@@ -790,6 +769,18 @@ mod tests {
"kiro",
));
}
#[test]
fn known_banned_detects_provider_bucket_account_blocks_without_provider_type() {
let key = sample_key(Some(json!({
"codex": {
"account_disabled": true,
"reason": "deactivated_workspace"
}
})));
assert!(admin_pool_key_is_known_banned(&key));
}
}
pub fn build_admin_pool_key_payload(