fix(antigravity): change VALIDATION_REQUIRED 403 to SetError and enrich degraded usage with account error status

- VALIDATION_REQUIRED now marks account as error (permanent) instead of temp unschedulable (10min)
- When usage fetch fails with 401 but account has 403 error, show correct forbidden/banned status instead of needs_reauth
This commit is contained in:
erio
2026-03-10 18:08:58 +08:00
parent e2b9c40093
commit 226844b8b2
2 changed files with 31 additions and 19 deletions
@@ -700,6 +700,7 @@ func (s *AccountUsageService) getAntigravityUsage(ctx context.Context, account *
fetchResult, err := s.antigravityQuotaFetcher.FetchQuota(fetchCtx, account, proxyURL)
if err != nil {
degraded := buildAntigravityDegradedUsage(err)
enrichDegradedWithAccountError(degraded, account)
s.cache.antigravityCache.Store(account.ID, &antigravityUsageCache{
usageInfo: degraded,
timestamp: time.Now(),
@@ -785,6 +786,30 @@ func buildAntigravityDegradedUsage(err error) *UsageInfo {
return info
}
// enrichDegradedWithAccountError 结合账号错误状态修正降级 UsageInfo
// 场景:被封号的账号 OAuth token 失效,FetchAvailableModels 返回 401,
// 降级逻辑设置了 needs_reauth,但账号实际是 403 封号/需验证。
// 此函数用账号的 error_message 覆盖降级信息,显示正确的 forbidden 状态。
func enrichDegradedWithAccountError(info *UsageInfo, account *Account) {
if info == nil || account == nil || account.Status != StatusError {
return
}
msg := strings.ToLower(account.ErrorMessage)
if !strings.Contains(msg, "403") && !strings.Contains(msg, "forbidden") &&
!strings.Contains(msg, "violation") && !strings.Contains(msg, "validation") {
return
}
fbType := classifyForbiddenType(account.ErrorMessage)
info.IsForbidden = true
info.ForbiddenType = fbType
info.ForbiddenReason = account.ErrorMessage
info.NeedsVerify = fbType == forbiddenTypeValidation
info.IsBanned = fbType == forbiddenTypeViolation
info.ValidationURL = extractValidationURL(account.ErrorMessage)
info.ErrorCode = errorCodeForbidden
info.NeedsReauth = false
}
// addWindowStats 为 usage 数据添加窗口期统计
// 使用独立缓存(1 分钟),与 API 缓存分离
func (s *AccountUsageService) addWindowStats(ctx context.Context, account *Account, usage *UsageInfo) {
+6 -19
View File
@@ -616,12 +616,8 @@ func (s *RateLimitService) handleAuthError(ctx context.Context, account *Account
slog.Warn("account_disabled_auth_error", "account_id", account.ID, "error", errorMsg)
}
// antigravityValidationBlockDuration Antigravity validation 类型 403 的临时封禁时长
// 与 Antigravity-Manager 一致(10 分钟)
const antigravityValidationBlockDuration = 10 * time.Minute
// handle403 处理 403 Forbidden 错误
// Antigravity 平台区分 validation(临时不可调度)和 violation(永久 SetError);
// Antigravity 平台区分 validation/violation/generic 三种类型,均 SetError 永久禁用;
// 其他平台保持原有 SetError 行为。
func (s *RateLimitService) handle403(ctx context.Context, account *Account, upstreamMsg string, responseBody []byte) (shouldDisable bool) {
if account.Platform == PlatformAntigravity {
@@ -637,7 +633,7 @@ func (s *RateLimitService) handle403(ctx context.Context, account *Account, upst
}
// handleAntigravity403 处理 Antigravity 平台的 403 错误
// validation(需要验证)→ 临时不可调度(到期自动恢复)
// validation(需要验证)→ 永久 SetError(需人工去 Google 验证后恢复)
// violation(违规封号)→ 永久 SetError(需人工处理)
// generic(通用禁止)→ 永久 SetError
func (s *RateLimitService) handleAntigravity403(ctx context.Context, account *Account, upstreamMsg string, responseBody []byte) (shouldDisable bool) {
@@ -645,21 +641,12 @@ func (s *RateLimitService) handleAntigravity403(ctx context.Context, account *Ac
switch fbType {
case forbiddenTypeValidation:
// VALIDATION_REQUIRED: 临时不可调度,到期自动恢复
until := time.Now().Add(antigravityValidationBlockDuration)
reason := "Validation required (403)"
// VALIDATION_REQUIRED: 永久禁用,需人工去 Google 验证后手动恢复
msg := "Validation required (403): account needs Google verification"
if upstreamMsg != "" {
reason = "Validation required (403): " + upstreamMsg
}
if err := s.accountRepo.SetTempUnschedulable(ctx, account.ID, until, reason); err != nil {
slog.Warn("antigravity_validation_block_failed", "account_id", account.ID, "error", err)
} else {
slog.Info("antigravity_validation_block_set",
"account_id", account.ID,
"until", until,
"forbidden_type", fbType,
)
msg = "Validation required (403): " + upstreamMsg
}
s.handleAuthError(ctx, account, msg)
return true
case forbiddenTypeViolation: