fix(gateway): 自定义错误码触发停止调度

- 修改 HandleUpstreamError 逻辑,启用自定义错误码时所有在列表中的错误码都会停止调度
- 添加 handleCustomErrorCode 函数处理自定义错误码的账号停用
- 前端添加 429/529 错误码的警告提示,因为这些错误码已有内置处理机制
- 更新 EditAccountModal、CreateAccountModal、BulkEditAccountModal 的错误码添加逻辑
This commit is contained in:
ianshaw
2026-01-11 22:20:02 -08:00
parent b76cc583fb
commit 6dcb27632e
6 changed files with 90 additions and 3 deletions

View File

@@ -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) {

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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)',

View File

@@ -1339,6 +1339,10 @@ export default {
customErrorCodes: '自定义错误码',
customErrorCodesHint: '仅对选中的错误码停止调度',
customErrorCodesWarning: '仅选中的错误码会停止调度,其他错误将返回 500。',
customErrorCodes429Warning:
'429 已有内置的限流处理机制。添加到自定义错误码后,将直接停止调度而非临时限流。确定要添加吗?',
customErrorCodes529Warning:
'529 已有内置的过载处理机制。添加到自定义错误码后,将直接停止调度而非临时标记过载。确定要添加吗?',
selectedErrorCodes: '已选择',
noneSelectedUsesDefault: '未选择(使用默认策略)',
enterErrorCode: '输入错误码 (100-599)',