mirror of
https://github.com/Wei-Shaw/claude-relay-service.git
synced 2026-01-23 19:46:16 +00:00
Merge branch 'dev' of github.com:Wei-Shaw/claude-relay-service into dev
This commit is contained in:
@@ -629,8 +629,30 @@ class ClaudeRelayService {
|
||||
'transfer-encoding'
|
||||
]
|
||||
|
||||
// 🆕 需要移除的浏览器相关 headers(避免CORS问题)
|
||||
const browserHeaders = [
|
||||
'origin',
|
||||
'referer',
|
||||
'sec-fetch-mode',
|
||||
'sec-fetch-site',
|
||||
'sec-fetch-dest',
|
||||
'sec-ch-ua',
|
||||
'sec-ch-ua-mobile',
|
||||
'sec-ch-ua-platform',
|
||||
'accept-language',
|
||||
'accept-encoding',
|
||||
'accept',
|
||||
'cache-control',
|
||||
'pragma',
|
||||
'anthropic-dangerous-direct-browser-access' // 这个头可能触发CORS检查
|
||||
]
|
||||
|
||||
// 应该保留的 headers(用于会话一致性和追踪)
|
||||
const allowedHeaders = ['x-request-id']
|
||||
const allowedHeaders = [
|
||||
'x-request-id',
|
||||
'anthropic-version', // 保留API版本
|
||||
'anthropic-beta' // 保留beta功能
|
||||
]
|
||||
|
||||
const filteredHeaders = {}
|
||||
|
||||
@@ -641,8 +663,8 @@ class ClaudeRelayService {
|
||||
if (allowedHeaders.includes(lowerKey)) {
|
||||
filteredHeaders[key] = clientHeaders[key]
|
||||
}
|
||||
// 如果不在敏感列表中,也保留
|
||||
else if (!sensitiveHeaders.includes(lowerKey)) {
|
||||
// 如果不在敏感列表和浏览器列表中,也保留
|
||||
else if (!sensitiveHeaders.includes(lowerKey) && !browserHeaders.includes(lowerKey)) {
|
||||
filteredHeaders[key] = clientHeaders[key]
|
||||
}
|
||||
})
|
||||
|
||||
@@ -244,8 +244,8 @@ class UnifiedClaudeScheduler {
|
||||
effectiveModel
|
||||
)
|
||||
if (isAvailable) {
|
||||
// 🚀 智能会话续期:剩余时间少于14天时自动续期到15天
|
||||
await redis.extendSessionAccountMappingTTL(sessionHash)
|
||||
// 🚀 智能会话续期:剩余时间少于14天时自动续期到15天(续期正确的 unified 映射键)
|
||||
await this._extendSessionMappingTTL(sessionHash)
|
||||
logger.info(
|
||||
`🎯 Using sticky session account: ${mappedAccount.accountId} (${mappedAccount.accountType}) for session ${sessionHash}`
|
||||
)
|
||||
@@ -806,9 +806,11 @@ class UnifiedClaudeScheduler {
|
||||
async _setSessionMapping(sessionHash, accountId, accountType) {
|
||||
const client = redis.getClientSafe()
|
||||
const mappingData = JSON.stringify({ accountId, accountType })
|
||||
|
||||
// 设置1小时过期
|
||||
await client.setex(`${this.SESSION_MAPPING_PREFIX}${sessionHash}`, 3600, mappingData)
|
||||
// 依据配置设置TTL(小时)
|
||||
const appConfig = require('../../config/config')
|
||||
const ttlHours = appConfig.session?.stickyTtlHours || 1
|
||||
const ttlSeconds = Math.max(1, Math.floor(ttlHours * 60 * 60))
|
||||
await client.setex(`${this.SESSION_MAPPING_PREFIX}${sessionHash}`, ttlSeconds, mappingData)
|
||||
}
|
||||
|
||||
// 🗑️ 删除会话映射
|
||||
@@ -817,6 +819,50 @@ class UnifiedClaudeScheduler {
|
||||
await client.del(`${this.SESSION_MAPPING_PREFIX}${sessionHash}`)
|
||||
}
|
||||
|
||||
// 🔁 续期统一调度会话映射TTL(针对 unified_claude_session_mapping:* 键),遵循会话配置
|
||||
async _extendSessionMappingTTL(sessionHash) {
|
||||
try {
|
||||
const client = redis.getClientSafe()
|
||||
const key = `${this.SESSION_MAPPING_PREFIX}${sessionHash}`
|
||||
const remainingTTL = await client.ttl(key)
|
||||
|
||||
// -2: key 不存在;-1: 无过期时间
|
||||
if (remainingTTL === -2) {
|
||||
return false
|
||||
}
|
||||
if (remainingTTL === -1) {
|
||||
return true
|
||||
}
|
||||
|
||||
const appConfig = require('../../config/config')
|
||||
const ttlHours = appConfig.session?.stickyTtlHours || 1
|
||||
const renewalThresholdMinutes = appConfig.session?.renewalThresholdMinutes || 0
|
||||
|
||||
// 阈值为0则不续期
|
||||
if (!renewalThresholdMinutes) {
|
||||
return true
|
||||
}
|
||||
|
||||
const fullTTL = Math.max(1, Math.floor(ttlHours * 60 * 60))
|
||||
const threshold = Math.max(0, Math.floor(renewalThresholdMinutes * 60))
|
||||
|
||||
if (remainingTTL < threshold) {
|
||||
await client.expire(key, fullTTL)
|
||||
logger.debug(
|
||||
`🔄 Renewed unified session TTL: ${sessionHash} (was ${Math.round(remainingTTL / 60)}m, renewed to ${ttlHours}h)`
|
||||
)
|
||||
} else {
|
||||
logger.debug(
|
||||
`✅ Unified session TTL sufficient: ${sessionHash} (remaining ${Math.round(remainingTTL / 60)}m)`
|
||||
)
|
||||
}
|
||||
return true
|
||||
} catch (error) {
|
||||
logger.error('❌ Failed to extend unified session TTL:', error)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// 🚫 标记账户为限流状态
|
||||
async markAccountRateLimited(
|
||||
accountId,
|
||||
@@ -989,8 +1035,8 @@ class UnifiedClaudeScheduler {
|
||||
requestedModel
|
||||
)
|
||||
if (isAvailable) {
|
||||
// 🚀 智能会话续期:剩余时间少于14天时自动续期到15天
|
||||
await redis.extendSessionAccountMappingTTL(sessionHash)
|
||||
// 🚀 智能会话续期:续期 unified 映射键
|
||||
await this._extendSessionMappingTTL(sessionHash)
|
||||
logger.info(
|
||||
`🎯 Using sticky session account from group: ${mappedAccount.accountId} (${mappedAccount.accountType}) for session ${sessionHash}`
|
||||
)
|
||||
@@ -1131,8 +1177,8 @@ class UnifiedClaudeScheduler {
|
||||
effectiveModel
|
||||
)
|
||||
if (isAvailable) {
|
||||
// 🚀 智能会话续期:剩余时间少于14天时自动续期到15天
|
||||
await redis.extendSessionAccountMappingTTL(sessionHash)
|
||||
// 🚀 智能会话续期:续期 unified 映射键
|
||||
await this._extendSessionMappingTTL(sessionHash)
|
||||
logger.info(
|
||||
`🎯 Using sticky CCR session account: ${mappedAccount.accountId} for session ${sessionHash}`
|
||||
)
|
||||
|
||||
@@ -61,8 +61,8 @@ class UnifiedGeminiScheduler {
|
||||
mappedAccount.accountType
|
||||
)
|
||||
if (isAvailable) {
|
||||
// 🚀 智能会话续期:剩余时间少于14天时自动续期到15天
|
||||
await redis.extendSessionAccountMappingTTL(sessionHash)
|
||||
// 🚀 智能会话续期(续期 unified 映射键,按配置)
|
||||
await this._extendSessionMappingTTL(sessionHash)
|
||||
logger.info(
|
||||
`🎯 Using sticky session account: ${mappedAccount.accountId} (${mappedAccount.accountType}) for session ${sessionHash}`
|
||||
)
|
||||
@@ -285,9 +285,11 @@ class UnifiedGeminiScheduler {
|
||||
async _setSessionMapping(sessionHash, accountId, accountType) {
|
||||
const client = redis.getClientSafe()
|
||||
const mappingData = JSON.stringify({ accountId, accountType })
|
||||
|
||||
// 设置1小时过期
|
||||
await client.setex(`${this.SESSION_MAPPING_PREFIX}${sessionHash}`, 3600, mappingData)
|
||||
// 依据配置设置TTL(小时)
|
||||
const appConfig = require('../../config/config')
|
||||
const ttlHours = appConfig.session?.stickyTtlHours || 1
|
||||
const ttlSeconds = Math.max(1, Math.floor(ttlHours * 60 * 60))
|
||||
await client.setex(`${this.SESSION_MAPPING_PREFIX}${sessionHash}`, ttlSeconds, mappingData)
|
||||
}
|
||||
|
||||
// 🗑️ 删除会话映射
|
||||
@@ -296,6 +298,47 @@ class UnifiedGeminiScheduler {
|
||||
await client.del(`${this.SESSION_MAPPING_PREFIX}${sessionHash}`)
|
||||
}
|
||||
|
||||
// 🔁 续期统一调度会话映射TTL(针对 unified_gemini_session_mapping:* 键),遵循会话配置
|
||||
async _extendSessionMappingTTL(sessionHash) {
|
||||
try {
|
||||
const client = redis.getClientSafe()
|
||||
const key = `${this.SESSION_MAPPING_PREFIX}${sessionHash}`
|
||||
const remainingTTL = await client.ttl(key)
|
||||
|
||||
if (remainingTTL === -2) {
|
||||
return false
|
||||
}
|
||||
if (remainingTTL === -1) {
|
||||
return true
|
||||
}
|
||||
|
||||
const appConfig = require('../../config/config')
|
||||
const ttlHours = appConfig.session?.stickyTtlHours || 1
|
||||
const renewalThresholdMinutes = appConfig.session?.renewalThresholdMinutes || 0
|
||||
if (!renewalThresholdMinutes) {
|
||||
return true
|
||||
}
|
||||
|
||||
const fullTTL = Math.max(1, Math.floor(ttlHours * 60 * 60))
|
||||
const threshold = Math.max(0, Math.floor(renewalThresholdMinutes * 60))
|
||||
|
||||
if (remainingTTL < threshold) {
|
||||
await client.expire(key, fullTTL)
|
||||
logger.debug(
|
||||
`🔄 Renewed unified Gemini session TTL: ${sessionHash} (was ${Math.round(remainingTTL / 60)}m, renewed to ${ttlHours}h)`
|
||||
)
|
||||
} else {
|
||||
logger.debug(
|
||||
`✅ Unified Gemini session TTL sufficient: ${sessionHash} (remaining ${Math.round(remainingTTL / 60)}m)`
|
||||
)
|
||||
}
|
||||
return true
|
||||
} catch (error) {
|
||||
logger.error('❌ Failed to extend unified Gemini session TTL:', error)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// 🚫 标记账户为限流状态
|
||||
async markAccountRateLimited(accountId, accountType, sessionHash = null) {
|
||||
try {
|
||||
@@ -384,8 +427,8 @@ class UnifiedGeminiScheduler {
|
||||
mappedAccount.accountType
|
||||
)
|
||||
if (isAvailable) {
|
||||
// 🚀 智能会话续期:剩余时间少于14天时自动续期到15天
|
||||
await redis.extendSessionAccountMappingTTL(sessionHash)
|
||||
// 🚀 智能会话续期(续期 unified 映射键,按配置)
|
||||
await this._extendSessionMappingTTL(sessionHash)
|
||||
logger.info(
|
||||
`🎯 Using sticky session account from group: ${mappedAccount.accountId} (${mappedAccount.accountType}) for session ${sessionHash}`
|
||||
)
|
||||
|
||||
@@ -127,8 +127,8 @@ class UnifiedOpenAIScheduler {
|
||||
mappedAccount.accountType
|
||||
)
|
||||
if (isAvailable) {
|
||||
// 🚀 智能会话续期:剩余时间少于14天时自动续期到15天
|
||||
await redis.extendSessionAccountMappingTTL(sessionHash)
|
||||
// 🚀 智能会话续期(续期 unified 映射键,按配置)
|
||||
await this._extendSessionMappingTTL(sessionHash)
|
||||
logger.info(
|
||||
`🎯 Using sticky session account: ${mappedAccount.accountId} (${mappedAccount.accountType}) for session ${sessionHash}`
|
||||
)
|
||||
@@ -380,9 +380,11 @@ class UnifiedOpenAIScheduler {
|
||||
async _setSessionMapping(sessionHash, accountId, accountType) {
|
||||
const client = redis.getClientSafe()
|
||||
const mappingData = JSON.stringify({ accountId, accountType })
|
||||
|
||||
// 设置1小时过期
|
||||
await client.setex(`${this.SESSION_MAPPING_PREFIX}${sessionHash}`, 3600, mappingData)
|
||||
// 依据配置设置TTL(小时)
|
||||
const appConfig = require('../../config/config')
|
||||
const ttlHours = appConfig.session?.stickyTtlHours || 1
|
||||
const ttlSeconds = Math.max(1, Math.floor(ttlHours * 60 * 60))
|
||||
await client.setex(`${this.SESSION_MAPPING_PREFIX}${sessionHash}`, ttlSeconds, mappingData)
|
||||
}
|
||||
|
||||
// 🗑️ 删除会话映射
|
||||
@@ -391,6 +393,47 @@ class UnifiedOpenAIScheduler {
|
||||
await client.del(`${this.SESSION_MAPPING_PREFIX}${sessionHash}`)
|
||||
}
|
||||
|
||||
// 🔁 续期统一调度会话映射TTL(针对 unified_openai_session_mapping:* 键),遵循会话配置
|
||||
async _extendSessionMappingTTL(sessionHash) {
|
||||
try {
|
||||
const client = redis.getClientSafe()
|
||||
const key = `${this.SESSION_MAPPING_PREFIX}${sessionHash}`
|
||||
const remainingTTL = await client.ttl(key)
|
||||
|
||||
if (remainingTTL === -2) {
|
||||
return false
|
||||
}
|
||||
if (remainingTTL === -1) {
|
||||
return true
|
||||
}
|
||||
|
||||
const appConfig = require('../../config/config')
|
||||
const ttlHours = appConfig.session?.stickyTtlHours || 1
|
||||
const renewalThresholdMinutes = appConfig.session?.renewalThresholdMinutes || 0
|
||||
if (!renewalThresholdMinutes) {
|
||||
return true
|
||||
}
|
||||
|
||||
const fullTTL = Math.max(1, Math.floor(ttlHours * 60 * 60))
|
||||
const threshold = Math.max(0, Math.floor(renewalThresholdMinutes * 60))
|
||||
|
||||
if (remainingTTL < threshold) {
|
||||
await client.expire(key, fullTTL)
|
||||
logger.debug(
|
||||
`🔄 Renewed unified OpenAI session TTL: ${sessionHash} (was ${Math.round(remainingTTL / 60)}m, renewed to ${ttlHours}h)`
|
||||
)
|
||||
} else {
|
||||
logger.debug(
|
||||
`✅ Unified OpenAI session TTL sufficient: ${sessionHash} (remaining ${Math.round(remainingTTL / 60)}m)`
|
||||
)
|
||||
}
|
||||
return true
|
||||
} catch (error) {
|
||||
logger.error('❌ Failed to extend unified OpenAI session TTL:', error)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// 🚫 标记账户为限流状态
|
||||
async markAccountRateLimited(accountId, accountType, sessionHash = null, resetsInSeconds = null) {
|
||||
try {
|
||||
@@ -520,8 +563,8 @@ class UnifiedOpenAIScheduler {
|
||||
mappedAccount.accountType
|
||||
)
|
||||
if (isAvailable) {
|
||||
// 🚀 智能会话续期:剩余时间少于14天时自动续期到15天
|
||||
await redis.extendSessionAccountMappingTTL(sessionHash)
|
||||
// 🚀 智能会话续期(续期 unified 映射键,按配置)
|
||||
await this._extendSessionMappingTTL(sessionHash)
|
||||
logger.info(
|
||||
`🎯 Using sticky session account from group: ${mappedAccount.accountId} (${mappedAccount.accountType})`
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user