diff --git a/backend/internal/service/ratelimit_service.go b/backend/internal/service/ratelimit_service.go index d570b92e..253faf4f 100644 --- a/backend/internal/service/ratelimit_service.go +++ b/backend/internal/service/ratelimit_service.go @@ -49,6 +49,7 @@ func NewRateLimitService(accountRepo AccountRepository, usageRepo UsageLogReposi func (s *RateLimitService) HandleUpstreamError(ctx context.Context, account *Account, statusCode int, headers http.Header, responseBody []byte) (shouldDisable bool) { // apikey 类型账号:检查自定义错误码配置 // 如果启用且错误码不在列表中,则不处理(不停止调度、不标记限流/过载) + customErrorCodesEnabled := account.IsCustomErrorCodesEnabled() if !account.ShouldHandleErrorCode(statusCode) { log.Printf("Account %d: error %d skipped (not in custom error codes)", account.ID, statusCode) return false @@ -93,11 +94,19 @@ func (s *RateLimitService) HandleUpstreamError(ctx context.Context, account *Acc s.handle529(ctx, account) shouldDisable = false default: - // 其他5xx错误:记录但不停止调度 - if statusCode >= 500 { + // 自定义错误码启用时:在列表中的错误码都应该停止调度 + if customErrorCodesEnabled { + msg := "Custom error code triggered" + if upstreamMsg != "" { + msg = upstreamMsg + } + s.handleCustomErrorCode(ctx, account, statusCode, msg) + shouldDisable = true + } else if statusCode >= 500 { + // 未启用自定义错误码时:仅记录5xx错误 log.Printf("Account %d received upstream error %d", account.ID, statusCode) + shouldDisable = false } - shouldDisable = false } if tempMatched { @@ -273,6 +282,16 @@ func (s *RateLimitService) handleAuthError(ctx context.Context, account *Account log.Printf("Account %d disabled due to auth error: %s", account.ID, errorMsg) } +// handleCustomErrorCode 处理自定义错误码,停止账号调度 +func (s *RateLimitService) handleCustomErrorCode(ctx context.Context, account *Account, statusCode int, errorMsg string) { + msg := "Custom error code " + strconv.Itoa(statusCode) + ": " + errorMsg + if err := s.accountRepo.SetError(ctx, account.ID, msg); err != nil { + log.Printf("SetError failed for account %d: %v", account.ID, err) + return + } + log.Printf("Account %d disabled due to custom error code %d: %s", account.ID, statusCode, errorMsg) +} + // handle429 处理429限流错误 // 解析响应头获取重置时间,标记账号为限流状态 func (s *RateLimitService) handle429(ctx context.Context, account *Account, headers http.Header) { diff --git a/frontend/src/components/account/BulkEditAccountModal.vue b/frontend/src/components/account/BulkEditAccountModal.vue index 51ad32d1..9ccf6130 100644 --- a/frontend/src/components/account/BulkEditAccountModal.vue +++ b/frontend/src/components/account/BulkEditAccountModal.vue @@ -778,6 +778,16 @@ const addPresetMapping = (from: string, to: string) => { const toggleErrorCode = (code: number) => { const index = selectedErrorCodes.value.indexOf(code) if (index === -1) { + // Adding code - check for 429/529 warning + if (code === 429) { + if (!confirm(t('admin.accounts.customErrorCodes429Warning'))) { + return + } + } else if (code === 529) { + if (!confirm(t('admin.accounts.customErrorCodes529Warning'))) { + return + } + } selectedErrorCodes.value.push(code) } else { selectedErrorCodes.value.splice(index, 1) @@ -794,6 +804,16 @@ const addCustomErrorCode = () => { appStore.showInfo(t('admin.accounts.errorCodeExists')) return } + // Check for 429/529 warning + if (code === 429) { + if (!confirm(t('admin.accounts.customErrorCodes429Warning'))) { + return + } + } else if (code === 529) { + if (!confirm(t('admin.accounts.customErrorCodes529Warning'))) { + return + } + } selectedErrorCodes.value.push(code) customErrorCodeInput.value = null } diff --git a/frontend/src/components/account/CreateAccountModal.vue b/frontend/src/components/account/CreateAccountModal.vue index 5833632b..1a4edaf1 100644 --- a/frontend/src/components/account/CreateAccountModal.vue +++ b/frontend/src/components/account/CreateAccountModal.vue @@ -1976,6 +1976,16 @@ const addPresetMapping = (from: string, to: string) => { const toggleErrorCode = (code: number) => { const index = selectedErrorCodes.value.indexOf(code) if (index === -1) { + // Adding code - check for 429/529 warning + if (code === 429) { + if (!confirm(t('admin.accounts.customErrorCodes429Warning'))) { + return + } + } else if (code === 529) { + if (!confirm(t('admin.accounts.customErrorCodes529Warning'))) { + return + } + } selectedErrorCodes.value.push(code) } else { selectedErrorCodes.value.splice(index, 1) @@ -1993,6 +2003,16 @@ const addCustomErrorCode = () => { appStore.showInfo(t('admin.accounts.errorCodeExists')) return } + // Check for 429/529 warning + if (code === 429) { + if (!confirm(t('admin.accounts.customErrorCodes429Warning'))) { + return + } + } else if (code === 529) { + if (!confirm(t('admin.accounts.customErrorCodes529Warning'))) { + return + } + } selectedErrorCodes.value.push(code) customErrorCodeInput.value = null } diff --git a/frontend/src/components/account/EditAccountModal.vue b/frontend/src/components/account/EditAccountModal.vue index 3b36cfbf..7cb740bd 100644 --- a/frontend/src/components/account/EditAccountModal.vue +++ b/frontend/src/components/account/EditAccountModal.vue @@ -936,6 +936,16 @@ const addPresetMapping = (from: string, to: string) => { const toggleErrorCode = (code: number) => { const index = selectedErrorCodes.value.indexOf(code) if (index === -1) { + // Adding code - check for 429/529 warning + if (code === 429) { + if (!confirm(t('admin.accounts.customErrorCodes429Warning'))) { + return + } + } else if (code === 529) { + if (!confirm(t('admin.accounts.customErrorCodes529Warning'))) { + return + } + } selectedErrorCodes.value.push(code) } else { selectedErrorCodes.value.splice(index, 1) @@ -953,6 +963,16 @@ const addCustomErrorCode = () => { appStore.showInfo(t('admin.accounts.errorCodeExists')) return } + // Check for 429/529 warning + if (code === 429) { + if (!confirm(t('admin.accounts.customErrorCodes429Warning'))) { + return + } + } else if (code === 529) { + if (!confirm(t('admin.accounts.customErrorCodes529Warning'))) { + return + } + } selectedErrorCodes.value.push(code) customErrorCodeInput.value = null } diff --git a/frontend/src/i18n/locales/en.ts b/frontend/src/i18n/locales/en.ts index f0e7db55..e23b6bcc 100644 --- a/frontend/src/i18n/locales/en.ts +++ b/frontend/src/i18n/locales/en.ts @@ -1203,6 +1203,10 @@ export default { customErrorCodesHint: 'Only stop scheduling for selected error codes', customErrorCodesWarning: 'Only selected error codes will stop scheduling. Other errors will return 500.', + customErrorCodes429Warning: + '429 already has built-in rate limit handling. Adding it to custom error codes will disable the account instead of temporary rate limiting. Are you sure?', + customErrorCodes529Warning: + '529 already has built-in overload handling. Adding it to custom error codes will disable the account instead of temporary overload marking. Are you sure?', selectedErrorCodes: 'Selected', noneSelectedUsesDefault: 'None selected (uses default policy)', enterErrorCode: 'Enter error code (100-599)', diff --git a/frontend/src/i18n/locales/zh.ts b/frontend/src/i18n/locales/zh.ts index ecdcb13f..e1cf78e9 100644 --- a/frontend/src/i18n/locales/zh.ts +++ b/frontend/src/i18n/locales/zh.ts @@ -1339,6 +1339,10 @@ export default { customErrorCodes: '自定义错误码', customErrorCodesHint: '仅对选中的错误码停止调度', customErrorCodesWarning: '仅选中的错误码会停止调度,其他错误将返回 500。', + customErrorCodes429Warning: + '429 已有内置的限流处理机制。添加到自定义错误码后,将直接停止调度而非临时限流。确定要添加吗?', + customErrorCodes529Warning: + '529 已有内置的过载处理机制。添加到自定义错误码后,将直接停止调度而非临时标记过载。确定要添加吗?', selectedErrorCodes: '已选择', noneSelectedUsesDefault: '未选择(使用默认策略)', enterErrorCode: '输入错误码 (100-599)',