feat(gateway): 添加 Claude Code 客户端最低版本检查功能

- 通过 User-Agent 识别 Claude Code 客户端并提取版本号
- 在网关层验证客户端版本是否满足管理员配置的最低要求
- 在管理后台提供版本要求配置选项(英文/中文双语)
- 实现原子缓存 + singleflight 防止并发问题和 thundering herd
- 使用 context.WithoutCancel 隔离 DB 查询,避免客户端断连影响缓存
- 双 TTL 策略:60s 正常、5s 错误恢复,保证性能与可用性
- 仅检查 Claude Code 客户端,其他客户端不受影响
- 添加完整单元测试覆盖版本提取、比对、上下文操作
This commit is contained in:
QTom
2026-03-01 15:35:46 +08:00
parent f7fa71bc28
commit 4280aca82c
15 changed files with 331 additions and 6 deletions

View File

@@ -616,6 +616,35 @@
</div>
</div>
<!-- Claude Code Settings -->
<div class="card">
<div class="border-b border-gray-100 px-6 py-4 dark:border-dark-700">
<h2 class="text-lg font-semibold text-gray-900 dark:text-white">
{{ t('admin.settings.claudeCode.title') }}
</h2>
<p class="mt-1 text-sm text-gray-500 dark:text-gray-400">
{{ t('admin.settings.claudeCode.description') }}
</p>
</div>
<div class="p-6">
<div>
<label class="mb-2 block text-sm font-medium text-gray-700 dark:text-gray-300">
{{ t('admin.settings.claudeCode.minVersion') }}
</label>
<input
v-model="form.min_claude_code_version"
type="text"
class="input max-w-xs font-mono text-sm"
:placeholder="t('admin.settings.claudeCode.minVersionPlaceholder')"
pattern="\d+\.\d+\.\d+"
/>
<p class="mt-1.5 text-xs text-gray-500 dark:text-gray-400">
{{ t('admin.settings.claudeCode.minVersionHint') }}
</p>
</div>
</div>
</div>
<!-- Site Settings -->
<div class="card">
<div class="border-b border-gray-100 px-6 py-4 dark:border-dark-700">
@@ -1203,7 +1232,9 @@ const form = reactive<SettingsForm>({
ops_monitoring_enabled: true,
ops_realtime_monitoring_enabled: true,
ops_query_mode_default: 'auto',
ops_metrics_interval_seconds: 60
ops_metrics_interval_seconds: 60,
// Claude Code version check
min_claude_code_version: ''
})
// LinuxDo OAuth redirect URL suggestion
@@ -1320,7 +1351,8 @@ async function saveSettings() {
fallback_model_gemini: form.fallback_model_gemini,
fallback_model_antigravity: form.fallback_model_antigravity,
enable_identity_patch: form.enable_identity_patch,
identity_patch_prompt: form.identity_patch_prompt
identity_patch_prompt: form.identity_patch_prompt,
min_claude_code_version: form.min_claude_code_version
}
const updated = await adminAPI.settings.updateSettings(payload)
Object.assign(form, updated)