mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 17:30:23 +08:00
feat(proxy,oauth,pool): H2 头过滤、OAuth 过期分级标记与批量操作进度条
- proxy: 屏蔽 host/content-length 头转发,避免 H2 PROTOCOL_ERROR - oauth: 区分 [REFRESH_FAILED] 与 [OAUTH_EXPIRED] 标记,token 过期 自动阻止调度但不停用账号,便于管理员恢复 - pool/account_state: 识别新增的 OAUTH_EXPIRED/REFRESH_FAILED 前缀 - 前端: 批量操作显示实时进度条,倍率编辑 Escape/blur 竞态修复, OAuth 刷新失败后自动刷新列表
This commit is contained in:
@@ -141,6 +141,42 @@ def _mark_refresh_token_invalid(
|
||||
)
|
||||
|
||||
|
||||
def _mark_oauth_token_expired(key: Any, expires_at: Any) -> None:
|
||||
"""标记 OAuth key 为 Token 已过期且无法续期,阻止后续调度。
|
||||
|
||||
当 refresh token 已失效且 access token 也已过期时调用。
|
||||
使用 [OAUTH_EXPIRED] 前缀,account_state 会将其判定为 blocked。
|
||||
不设置 is_active = False(管理员可通过重新导入凭据恢复)。
|
||||
"""
|
||||
from datetime import datetime, timezone
|
||||
|
||||
# 如果已经有更严重的标记([ACCOUNT_BLOCK]),不降级
|
||||
existing = str(getattr(key, "oauth_invalid_reason", None) or "")
|
||||
if existing.startswith("[ACCOUNT_BLOCK]"):
|
||||
return
|
||||
|
||||
reason = f"[OAUTH_EXPIRED] Token 已过期且续期失败 (expired_at={expires_at})"
|
||||
|
||||
try:
|
||||
key.oauth_invalid_at = datetime.now(timezone.utc)
|
||||
key.oauth_invalid_reason = reason
|
||||
|
||||
sess = object_session(key)
|
||||
if sess is not None:
|
||||
sess.add(key)
|
||||
sess.commit()
|
||||
logger.info(
|
||||
"[OAUTH_EXPIRED] key {} token expired and refresh failed, blocking scheduling",
|
||||
str(getattr(key, "id", "?"))[:8],
|
||||
)
|
||||
except Exception as exc:
|
||||
logger.warning(
|
||||
"[OAUTH_EXPIRED] failed to mark key {} as expired: {}",
|
||||
str(getattr(key, "id", "?"))[:8],
|
||||
str(exc),
|
||||
)
|
||||
|
||||
|
||||
def _get_proxy_config(key: Any, endpoint: Any = None) -> Any:
|
||||
"""获取有效代理配置(Key 级别优先于 Provider 级别)。"""
|
||||
try:
|
||||
@@ -414,6 +450,7 @@ async def get_provider_auth(
|
||||
should_refresh = True
|
||||
|
||||
_refreshed = False
|
||||
_lost_lock = False # 其他实例持有刷新锁,不应标记过期
|
||||
if should_refresh and refresh_token and provider_type:
|
||||
try:
|
||||
from src.core.provider_templates.fixed_providers import FIXED_PROVIDERS
|
||||
@@ -437,10 +474,22 @@ async def get_provider_auth(
|
||||
finally:
|
||||
if got_lock:
|
||||
await _release_refresh_lock(redis, key.id)
|
||||
else:
|
||||
_lost_lock = True
|
||||
except Exception:
|
||||
# 刷新失败不阻断请求;后续由上游返回 401 再触发管理端处理
|
||||
pass
|
||||
|
||||
# Refresh 失败(非锁竞争)且 access token 已过期 → 升级标记为 [OAUTH_EXPIRED]
|
||||
# 注意:未获取到锁说明其他实例正在刷新,不应在此标记为过期
|
||||
if should_refresh and not _refreshed and not _lost_lock and expires_at is not None:
|
||||
try:
|
||||
token_truly_expired = int(time.time()) >= int(expires_at)
|
||||
except Exception:
|
||||
token_truly_expired = False
|
||||
if token_truly_expired:
|
||||
_mark_oauth_token_expired(key, expires_at)
|
||||
|
||||
# 获取最终使用的 access_token
|
||||
# Kiro 优先使用 token_meta 中缓存的 access_token(刷新后会更新到 token_meta)
|
||||
if provider_type == "kiro":
|
||||
|
||||
@@ -10,6 +10,8 @@ from dataclasses import dataclass
|
||||
from typing import Any
|
||||
|
||||
OAUTH_ACCOUNT_BLOCK_PREFIX = "[ACCOUNT_BLOCK] "
|
||||
OAUTH_REFRESH_FAILED_PREFIX = "[REFRESH_FAILED] "
|
||||
OAUTH_EXPIRED_PREFIX = "[OAUTH_EXPIRED] "
|
||||
|
||||
# -- 按原因细分的关键词组 --
|
||||
# 封禁类 (suspended / banned)
|
||||
@@ -184,6 +186,15 @@ def _resolve_from_oauth_invalid_reason(reason: str | None) -> PoolAccountState |
|
||||
reason=cleaned or "账号异常",
|
||||
)
|
||||
|
||||
if text.startswith(OAUTH_EXPIRED_PREFIX):
|
||||
cleaned = text[len(OAUTH_EXPIRED_PREFIX) :].strip()
|
||||
return PoolAccountState(
|
||||
blocked=True,
|
||||
code="oauth_expired",
|
||||
label="Token 失效",
|
||||
reason=cleaned or "OAuth Token 已过期且无法续期",
|
||||
)
|
||||
|
||||
lowered = text.lower()
|
||||
if any(keyword in lowered for keyword in ACCOUNT_BLOCK_REASON_KEYWORDS):
|
||||
code, label = _classify_block_reason(text)
|
||||
@@ -219,6 +230,8 @@ def resolve_pool_account_state(
|
||||
__all__ = [
|
||||
"ACCOUNT_BLOCK_REASON_KEYWORDS",
|
||||
"OAUTH_ACCOUNT_BLOCK_PREFIX",
|
||||
"OAUTH_EXPIRED_PREFIX",
|
||||
"OAUTH_REFRESH_FAILED_PREFIX",
|
||||
"PoolAccountState",
|
||||
"resolve_pool_account_state",
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user