feat: 实现 Claude Code headers 动态管理功能

- 创建 claudeCodeHeadersService 管理各账号的 Claude Code headers
- 自动捕获成功请求的 headers 并按账号存储在 Redis
- 智能版本管理,只保留最新版本的 headers
- OpenAI 转发时根据账号动态获取对应的 headers
- 添加管理端点查看和清除各账号的 headers 信息
- 完整支持 Claude Code 必需的 beta headers

解决了 "This credential is only authorized for use with Claude Code" 错误
避免了固定版本号带来的风控问题

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
shaw
2025-07-22 16:03:31 +08:00
parent b2ad2a4a61
commit dabf3bf7ea
6 changed files with 410 additions and 12 deletions

View File

@@ -0,0 +1,212 @@
/**
* Claude Code Headers 管理服务
* 负责存储和管理不同账号使用的 Claude Code headers
*/
const redis = require('../models/redis');
const logger = require('../utils/logger');
class ClaudeCodeHeadersService {
constructor() {
this.defaultHeaders = {
'x-stainless-retry-count': '0',
'x-stainless-timeout': '60',
'x-stainless-lang': 'js',
'x-stainless-package-version': '0.55.1',
'x-stainless-os': 'Windows',
'x-stainless-arch': 'x64',
'x-stainless-runtime': 'node',
'x-stainless-runtime-version': 'v20.19.2',
'anthropic-dangerous-direct-browser-access': 'true',
'x-app': 'cli',
'user-agent': 'claude-cli/1.0.57 (external, cli)',
'accept-language': '*',
'sec-fetch-mode': 'cors'
};
// 需要捕获的 Claude Code 特定 headers
this.claudeCodeHeaderKeys = [
'x-stainless-retry-count',
'x-stainless-timeout',
'x-stainless-lang',
'x-stainless-package-version',
'x-stainless-os',
'x-stainless-arch',
'x-stainless-runtime',
'x-stainless-runtime-version',
'anthropic-dangerous-direct-browser-access',
'x-app',
'user-agent',
'accept-language',
'sec-fetch-mode',
'accept-encoding'
];
}
/**
* 从 user-agent 中提取版本号
*/
extractVersionFromUserAgent(userAgent) {
if (!userAgent) return null;
const match = userAgent.match(/claude-cli\/(\d+\.\d+\.\d+)/);
return match ? match[1] : null;
}
/**
* 比较版本号
* @returns {number} 1 if v1 > v2, -1 if v1 < v2, 0 if equal
*/
compareVersions(v1, v2) {
if (!v1 || !v2) return 0;
const parts1 = v1.split('.').map(Number);
const parts2 = v2.split('.').map(Number);
for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
const p1 = parts1[i] || 0;
const p2 = parts2[i] || 0;
if (p1 > p2) return 1;
if (p1 < p2) return -1;
}
return 0;
}
/**
* 从客户端 headers 中提取 Claude Code 相关的 headers
*/
extractClaudeCodeHeaders(clientHeaders) {
const headers = {};
// 转换所有 header keys 为小写进行比较
const lowerCaseHeaders = {};
Object.keys(clientHeaders || {}).forEach(key => {
lowerCaseHeaders[key.toLowerCase()] = clientHeaders[key];
});
// 提取需要的 headers
this.claudeCodeHeaderKeys.forEach(key => {
const lowerKey = key.toLowerCase();
if (lowerCaseHeaders[lowerKey]) {
headers[key] = lowerCaseHeaders[lowerKey];
}
});
return headers;
}
/**
* 存储账号的 Claude Code headers
*/
async storeAccountHeaders(accountId, clientHeaders) {
try {
const extractedHeaders = this.extractClaudeCodeHeaders(clientHeaders);
// 检查是否有 user-agent
const userAgent = extractedHeaders['user-agent'];
if (!userAgent || !userAgent.includes('claude-cli')) {
// 不是 Claude Code 的请求,不存储
return;
}
const version = this.extractVersionFromUserAgent(userAgent);
if (!version) {
logger.warn(`⚠️ Failed to extract version from user-agent: ${userAgent}`);
return;
}
// 获取当前存储的 headers
const key = `claude_code_headers:${accountId}`;
const currentData = await redis.get(key);
if (currentData) {
const current = JSON.parse(currentData);
const currentVersion = this.extractVersionFromUserAgent(current.headers['user-agent']);
// 只有新版本更高时才更新
if (this.compareVersions(version, currentVersion) <= 0) {
return;
}
}
// 存储新的 headers
const data = {
headers: extractedHeaders,
version: version,
updatedAt: new Date().toISOString()
};
await redis.setex(key, 86400 * 7, JSON.stringify(data)); // 7天过期
logger.info(`✅ Stored Claude Code headers for account ${accountId}, version: ${version}`);
} catch (error) {
logger.error(`❌ Failed to store Claude Code headers for account ${accountId}:`, error);
}
}
/**
* 获取账号的 Claude Code headers
*/
async getAccountHeaders(accountId) {
try {
const key = `claude_code_headers:${accountId}`;
const data = await redis.get(key);
if (data) {
const parsed = JSON.parse(data);
logger.debug(`📋 Retrieved Claude Code headers for account ${accountId}, version: ${parsed.version}`);
return parsed.headers;
}
// 返回默认 headers
logger.debug(`📋 Using default Claude Code headers for account ${accountId}`);
return this.defaultHeaders;
} catch (error) {
logger.error(`❌ Failed to get Claude Code headers for account ${accountId}:`, error);
return this.defaultHeaders;
}
}
/**
* 清除账号的 Claude Code headers
*/
async clearAccountHeaders(accountId) {
try {
const key = `claude_code_headers:${accountId}`;
await redis.del(key);
logger.info(`🗑️ Cleared Claude Code headers for account ${accountId}`);
} catch (error) {
logger.error(`❌ Failed to clear Claude Code headers for account ${accountId}:`, error);
}
}
/**
* 获取所有账号的 headers 信息
*/
async getAllAccountHeaders() {
try {
const pattern = 'claude_code_headers:*';
const keys = await redis.keys(pattern);
const results = {};
for (const key of keys) {
const accountId = key.replace('claude_code_headers:', '');
const data = await redis.get(key);
if (data) {
results[accountId] = JSON.parse(data);
}
}
return results;
} catch (error) {
logger.error('❌ Failed to get all account headers:', error);
return {};
}
}
}
module.exports = new ClaudeCodeHeadersService();