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

@@ -29,8 +29,10 @@ func SetClaudeCodeClientContext(c *gin.Context, body []byte, parsedReq *service.
if parsedReq != nil {
c.Set(claudeCodeParsedRequestContextKey, parsedReq)
}
ua := c.GetHeader("User-Agent")
// Fast path非 Claude CLI UA 直接判定 false避免热路径二次 JSON 反序列化。
if !claudeCodeValidator.ValidateUserAgent(c.GetHeader("User-Agent")) {
if !claudeCodeValidator.ValidateUserAgent(ua) {
ctx := service.SetClaudeCodeClient(c.Request.Context(), false)
c.Request = c.Request.WithContext(ctx)
return
@@ -54,6 +56,14 @@ func SetClaudeCodeClientContext(c *gin.Context, body []byte, parsedReq *service.
// 更新 request context
ctx := service.SetClaudeCodeClient(c.Request.Context(), isClaudeCode)
// 仅在确认为 Claude Code 客户端时提取版本号写入 context
if isClaudeCode {
if version := claudeCodeValidator.ExtractVersion(ua); version != "" {
ctx = service.SetClaudeCodeVersion(ctx, version)
}
}
c.Request = c.Request.WithContext(ctx)
}