feat(rectifier): 请求整流器增加 API Key 账号签名整流支持

新增独立开关控制 API Key 账号的签名整流功能,支持配置自定义
匹配关键词以捕获不同格式的上游错误响应。

- 新增 apikey_signature_enabled 开关(默认关闭)
- 新增 apikey_signature_patterns 自定义关键词配置
- 内置签名检测规则对 API Key 账号同样生效
- 自定义关键词对完整响应体做不区分大小写匹配
- 重试二阶段检测仅做模式匹配,不重复校验开关
- Handler 层校验关键词数量(≤50)和长度(≤500)
- API 响应 nil patterns 统一序列化为空数组
- OAuth/SetupToken/Upstream/Bedrock 账号行为不变
This commit is contained in:
shaw
2026-03-26 16:43:38 +08:00
parent ce96527dd9
commit d571f300e5
8 changed files with 204 additions and 14 deletions

View File

@@ -454,6 +454,72 @@
</div>
<Toggle v-model="rectifierForm.thinking_budget_enabled" />
</div>
<!-- API Key Signature Rectifier -->
<div class="flex items-center justify-between">
<div>
<label class="text-sm font-medium text-gray-700 dark:text-gray-300">{{
t('admin.settings.rectifier.apikeySignature')
}}</label>
<p class="text-xs text-gray-500 dark:text-gray-400">
{{ t('admin.settings.rectifier.apikeySignatureHint') }}
</p>
</div>
<Toggle v-model="rectifierForm.apikey_signature_enabled" />
</div>
<!-- Custom Patterns (only when apikey_signature_enabled) -->
<div
v-if="rectifierForm.apikey_signature_enabled"
class="ml-4 space-y-3 border-l-2 border-gray-200 pl-4 dark:border-dark-600"
>
<div>
<label class="text-sm font-medium text-gray-700 dark:text-gray-300">{{
t('admin.settings.rectifier.apikeyPatterns')
}}</label>
<p class="text-xs text-gray-500 dark:text-gray-400">
{{ t('admin.settings.rectifier.apikeyPatternsHint') }}
</p>
</div>
<div
v-for="(_, index) in rectifierForm.apikey_signature_patterns"
:key="index"
class="flex items-center gap-2"
>
<input
v-model="rectifierForm.apikey_signature_patterns[index]"
type="text"
class="input input-sm flex-1"
:placeholder="t('admin.settings.rectifier.apikeyPatternPlaceholder')"
/>
<button
type="button"
@click="rectifierForm.apikey_signature_patterns.splice(index, 1)"
class="btn btn-ghost btn-xs text-red-500 hover:text-red-700"
>
<svg
class="h-4 w-4"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M6 18L18 6M6 6l12 12"
/>
</svg>
</button>
</div>
<button
type="button"
@click="rectifierForm.apikey_signature_patterns.push('')"
class="btn btn-ghost btn-xs text-primary-600 dark:text-primary-400"
>
+ {{ t('admin.settings.rectifier.addPattern') }}
</button>
</div>
</div>
<!-- Save Button -->
@@ -2010,7 +2076,9 @@ const rectifierSaving = ref(false)
const rectifierForm = reactive({
enabled: true,
thinking_signature_enabled: true,
thinking_budget_enabled: true
thinking_budget_enabled: true,
apikey_signature_enabled: false,
apikey_signature_patterns: [] as string[]
})
// Beta Policy 状态
@@ -2626,6 +2694,10 @@ async function loadRectifierSettings() {
try {
const settings = await adminAPI.settings.getRectifierSettings()
Object.assign(rectifierForm, settings)
// 确保 patterns 是数组(旧数据可能为 null
if (!Array.isArray(rectifierForm.apikey_signature_patterns)) {
rectifierForm.apikey_signature_patterns = []
}
} catch (error: any) {
console.error('Failed to load rectifier settings:', error)
} finally {
@@ -2639,9 +2711,16 @@ async function saveRectifierSettings() {
const updated = await adminAPI.settings.updateRectifierSettings({
enabled: rectifierForm.enabled,
thinking_signature_enabled: rectifierForm.thinking_signature_enabled,
thinking_budget_enabled: rectifierForm.thinking_budget_enabled
thinking_budget_enabled: rectifierForm.thinking_budget_enabled,
apikey_signature_enabled: rectifierForm.apikey_signature_enabled,
apikey_signature_patterns: rectifierForm.apikey_signature_patterns.filter(
(p) => p.trim() !== ''
)
})
Object.assign(rectifierForm, updated)
if (!Array.isArray(rectifierForm.apikey_signature_patterns)) {
rectifierForm.apikey_signature_patterns = []
}
appStore.showSuccess(t('admin.settings.rectifier.saved'))
} catch (error: any) {
appStore.showError(