mirror of
https://github.com/Wei-Shaw/claude-relay-service.git
synced 2026-01-22 16:43:35 +00:00
Merge pull request #333 from sczheng189/dev
feat: 添加统一Claude Code User-Agent支持及缓存管理功能(仅支持Claude账户,不支持api)
This commit is contained in:
@@ -6221,4 +6221,54 @@ router.post('/migrate-api-keys-azure', authenticateAdmin, async (req, res) => {
|
||||
}
|
||||
})
|
||||
|
||||
// 📋 获取统一Claude Code User-Agent信息
|
||||
router.get('/claude-code-version', authenticateAdmin, async (req, res) => {
|
||||
try {
|
||||
const CACHE_KEY = 'claude_code_user_agent:daily'
|
||||
|
||||
// 获取缓存的统一User-Agent
|
||||
const unifiedUserAgent = await redis.client.get(CACHE_KEY)
|
||||
const ttl = unifiedUserAgent ? await redis.client.ttl(CACHE_KEY) : 0
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
userAgent: unifiedUserAgent,
|
||||
isActive: !!unifiedUserAgent,
|
||||
ttlSeconds: ttl,
|
||||
lastUpdated: unifiedUserAgent ? new Date().toISOString() : null
|
||||
})
|
||||
} catch (error) {
|
||||
logger.error('❌ Get unified Claude Code User-Agent error:', error)
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: 'Failed to get User-Agent information',
|
||||
error: error.message
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
// 🗑️ 清除统一Claude Code User-Agent缓存
|
||||
router.post('/claude-code-version/clear', authenticateAdmin, async (req, res) => {
|
||||
try {
|
||||
const CACHE_KEY = 'claude_code_user_agent:daily'
|
||||
|
||||
// 删除缓存的统一User-Agent
|
||||
await redis.client.del(CACHE_KEY)
|
||||
|
||||
logger.info(`🗑️ Admin manually cleared unified Claude Code User-Agent cache`)
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: 'Unified User-Agent cache cleared successfully'
|
||||
})
|
||||
} catch (error) {
|
||||
logger.error('❌ Clear unified User-Agent cache error:', error)
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: 'Failed to clear cache',
|
||||
error: error.message
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
module.exports = router
|
||||
|
||||
@@ -59,7 +59,8 @@ class ClaudeAccountService {
|
||||
priority = 50, // 调度优先级 (1-100,数字越小优先级越高)
|
||||
schedulable = true, // 是否可被调度
|
||||
subscriptionInfo = null, // 手动设置的订阅信息
|
||||
autoStopOnWarning = false // 5小时使用量接近限制时自动停止调度
|
||||
autoStopOnWarning = false, // 5小时使用量接近限制时自动停止调度
|
||||
useUnifiedUserAgent = false // 是否使用统一Claude Code版本的User-Agent
|
||||
} = options
|
||||
|
||||
const accountId = uuidv4()
|
||||
@@ -91,6 +92,7 @@ class ClaudeAccountService {
|
||||
errorMessage: '',
|
||||
schedulable: schedulable.toString(), // 是否可被调度
|
||||
autoStopOnWarning: autoStopOnWarning.toString(), // 5小时使用量接近限制时自动停止调度
|
||||
useUnifiedUserAgent: useUnifiedUserAgent.toString(), // 是否使用统一Claude Code版本的User-Agent
|
||||
// 优先使用手动设置的订阅信息,否则使用OAuth数据中的,否则默认为空
|
||||
subscriptionInfo: subscriptionInfo
|
||||
? JSON.stringify(subscriptionInfo)
|
||||
@@ -122,6 +124,7 @@ class ClaudeAccountService {
|
||||
errorMessage: '',
|
||||
schedulable: schedulable.toString(), // 是否可被调度
|
||||
autoStopOnWarning: autoStopOnWarning.toString(), // 5小时使用量接近限制时自动停止调度
|
||||
useUnifiedUserAgent: useUnifiedUserAgent.toString(), // 是否使用统一Claude Code版本的User-Agent
|
||||
// 手动设置的订阅信息
|
||||
subscriptionInfo: subscriptionInfo ? JSON.stringify(subscriptionInfo) : ''
|
||||
}
|
||||
@@ -487,6 +490,8 @@ class ClaudeAccountService {
|
||||
schedulable: account.schedulable !== 'false', // 默认为true,兼容历史数据
|
||||
// 添加自动停止调度设置
|
||||
autoStopOnWarning: account.autoStopOnWarning === 'true', // 默认为false
|
||||
// 添加统一User-Agent设置
|
||||
useUnifiedUserAgent: account.useUnifiedUserAgent === 'true', // 默认为false
|
||||
// 添加停止原因
|
||||
stoppedReason: account.stoppedReason || null
|
||||
}
|
||||
@@ -522,7 +527,8 @@ class ClaudeAccountService {
|
||||
'priority',
|
||||
'schedulable',
|
||||
'subscriptionInfo',
|
||||
'autoStopOnWarning'
|
||||
'autoStopOnWarning',
|
||||
'useUnifiedUserAgent'
|
||||
]
|
||||
const updatedData = { ...accountData }
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ const sessionHelper = require('../utils/sessionHelper')
|
||||
const logger = require('../utils/logger')
|
||||
const config = require('../../config/config')
|
||||
const claudeCodeHeadersService = require('./claudeCodeHeadersService')
|
||||
const redis = require('../models/redis')
|
||||
|
||||
class ClaudeRelayService {
|
||||
constructor() {
|
||||
@@ -610,6 +611,12 @@ class ClaudeRelayService {
|
||||
) {
|
||||
const url = new URL(this.claudeApiUrl)
|
||||
|
||||
// 获取账户信息用于统一 User-Agent
|
||||
const account = await claudeAccountService.getAccount(accountId)
|
||||
|
||||
// 获取统一的 User-Agent
|
||||
const unifiedUA = await this.captureAndGetUnifiedUserAgent(clientHeaders, account)
|
||||
|
||||
// 获取过滤后的客户端 headers
|
||||
const filteredHeaders = this._filterClientHeaders(clientHeaders)
|
||||
|
||||
@@ -656,11 +663,19 @@ class ClaudeRelayService {
|
||||
timeout: config.proxy.timeout
|
||||
}
|
||||
|
||||
// 如果客户端没有提供 User-Agent,使用默认值
|
||||
// 使用统一 User-Agent 或客户端提供的,最后使用默认值
|
||||
if (!options.headers['User-Agent'] && !options.headers['user-agent']) {
|
||||
options.headers['User-Agent'] = 'claude-cli/1.0.57 (external, cli)'
|
||||
const userAgent =
|
||||
unifiedUA ||
|
||||
clientHeaders?.['user-agent'] ||
|
||||
clientHeaders?.['User-Agent'] ||
|
||||
'claude-cli/1.0.102 (external, cli)'
|
||||
options.headers['User-Agent'] = userAgent
|
||||
}
|
||||
|
||||
logger.info(`🔗 指纹是这个: ${options.headers['User-Agent']}`)
|
||||
logger.info(`🔗 指纹是这个: ${options.headers['user-agent']}`)
|
||||
|
||||
// 使用自定义的 betaHeader 或默认值
|
||||
const betaHeader =
|
||||
requestOptions?.betaHeader !== undefined ? requestOptions.betaHeader : this.betaHeader
|
||||
@@ -868,6 +883,12 @@ class ClaudeRelayService {
|
||||
streamTransformer = null,
|
||||
requestOptions = {}
|
||||
) {
|
||||
// 获取账户信息用于统一 User-Agent
|
||||
const account = await claudeAccountService.getAccount(accountId)
|
||||
|
||||
// 获取统一的 User-Agent
|
||||
const unifiedUA = await this.captureAndGetUnifiedUserAgent(clientHeaders, account)
|
||||
|
||||
// 获取过滤后的客户端 headers
|
||||
const filteredHeaders = this._filterClientHeaders(clientHeaders)
|
||||
|
||||
@@ -908,9 +929,14 @@ class ClaudeRelayService {
|
||||
timeout: config.proxy.timeout
|
||||
}
|
||||
|
||||
// 如果客户端没有提供 User-Agent,使用默认值
|
||||
// 使用统一 User-Agent 或客户端提供的,最后使用默认值
|
||||
if (!options.headers['User-Agent'] && !options.headers['user-agent']) {
|
||||
options.headers['User-Agent'] = 'claude-cli/1.0.57 (external, cli)'
|
||||
const userAgent =
|
||||
unifiedUA ||
|
||||
clientHeaders?.['user-agent'] ||
|
||||
clientHeaders?.['User-Agent'] ||
|
||||
'claude-cli/1.0.102 (external, cli)'
|
||||
options.headers['User-Agent'] = userAgent
|
||||
}
|
||||
|
||||
// 使用自定义的 betaHeader 或默认值
|
||||
@@ -1398,7 +1424,12 @@ class ClaudeRelayService {
|
||||
|
||||
// 如果客户端没有提供 User-Agent,使用默认值
|
||||
if (!filteredHeaders['User-Agent'] && !filteredHeaders['user-agent']) {
|
||||
options.headers['User-Agent'] = 'claude-cli/1.0.53 (external, cli)'
|
||||
// 第三个方法不支持统一 User-Agent,使用简化逻辑
|
||||
const userAgent =
|
||||
clientHeaders?.['user-agent'] ||
|
||||
clientHeaders?.['User-Agent'] ||
|
||||
'claude-cli/1.0.102 (external, cli)'
|
||||
options.headers['User-Agent'] = userAgent
|
||||
}
|
||||
|
||||
// 使用自定义的 betaHeader 或默认值
|
||||
@@ -1535,7 +1566,6 @@ class ClaudeRelayService {
|
||||
async recordUnauthorizedError(accountId) {
|
||||
try {
|
||||
const key = `claude_account:${accountId}:401_errors`
|
||||
const redis = require('../models/redis')
|
||||
|
||||
// 增加错误计数,设置5分钟过期时间
|
||||
await redis.client.incr(key)
|
||||
@@ -1551,7 +1581,6 @@ class ClaudeRelayService {
|
||||
async getUnauthorizedErrorCount(accountId) {
|
||||
try {
|
||||
const key = `claude_account:${accountId}:401_errors`
|
||||
const redis = require('../models/redis')
|
||||
|
||||
const count = await redis.client.get(key)
|
||||
return parseInt(count) || 0
|
||||
@@ -1565,7 +1594,6 @@ class ClaudeRelayService {
|
||||
async clearUnauthorizedErrors(accountId) {
|
||||
try {
|
||||
const key = `claude_account:${accountId}:401_errors`
|
||||
const redis = require('../models/redis')
|
||||
|
||||
await redis.client.del(key)
|
||||
logger.info(`✅ Cleared 401 error count for account ${accountId}`)
|
||||
@@ -1574,6 +1602,103 @@ class ClaudeRelayService {
|
||||
}
|
||||
}
|
||||
|
||||
// 🔧 动态捕获并获取统一的 User-Agent
|
||||
async captureAndGetUnifiedUserAgent(clientHeaders, account) {
|
||||
if (account.useUnifiedUserAgent !== 'true') {
|
||||
return null
|
||||
}
|
||||
|
||||
const CACHE_KEY = 'claude_code_user_agent:daily'
|
||||
const TTL = 90000 // 25小时
|
||||
|
||||
// ⚠️ 重要:这里通过 'claude-cli/' 判断是否为 Claude Code 客户端
|
||||
// 如果未来 Claude Code 的 User-Agent 格式发生变化(不再包含 'claude-cli/'),
|
||||
// 需要更新这个判断条件!
|
||||
// 当前已知格式:claude-cli/1.0.102 (external, cli)
|
||||
const CLAUDE_CODE_UA_IDENTIFIER = 'claude-cli/'
|
||||
|
||||
const clientUA = clientHeaders?.['user-agent'] || clientHeaders?.['User-Agent']
|
||||
let cachedUA = await redis.client.get(CACHE_KEY)
|
||||
|
||||
if (clientUA?.includes(CLAUDE_CODE_UA_IDENTIFIER)) {
|
||||
if (!cachedUA) {
|
||||
// 没有缓存,直接存储
|
||||
await redis.client.setex(CACHE_KEY, TTL, clientUA)
|
||||
logger.info(`📱 Captured unified Claude Code User-Agent: ${clientUA}`)
|
||||
cachedUA = clientUA
|
||||
} else {
|
||||
// 有缓存,比较版本号,保存更新的版本
|
||||
const shouldUpdate = this.compareClaudeCodeVersions(clientUA, cachedUA)
|
||||
if (shouldUpdate) {
|
||||
await redis.client.setex(CACHE_KEY, TTL, clientUA)
|
||||
logger.info(`🔄 Updated to newer Claude Code User-Agent: ${clientUA} (was: ${cachedUA})`)
|
||||
cachedUA = clientUA
|
||||
} else {
|
||||
// 当前版本不比缓存版本新,仅刷新TTL
|
||||
await redis.client.expire(CACHE_KEY, TTL)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return cachedUA // 没有缓存返回 null
|
||||
}
|
||||
|
||||
// 🔄 比较Claude Code版本号,判断是否需要更新
|
||||
// 返回 true 表示 newUA 版本更新,需要更新缓存
|
||||
compareClaudeCodeVersions(newUA, cachedUA) {
|
||||
try {
|
||||
// 提取版本号:claude-cli/1.0.102 (external, cli) -> 1.0.102
|
||||
const newVersionMatch = newUA.match(/claude-cli\/([0-9]+\.[0-9]+\.[0-9]+)/)
|
||||
const cachedVersionMatch = cachedUA.match(/claude-cli\/([0-9]+\.[0-9]+\.[0-9]+)/)
|
||||
|
||||
if (!newVersionMatch || !cachedVersionMatch) {
|
||||
// 无法解析版本号,优先使用新的
|
||||
logger.warn(`⚠️ Unable to parse Claude Code versions: new=${newUA}, cached=${cachedUA}`)
|
||||
return true
|
||||
}
|
||||
|
||||
const newVersion = newVersionMatch[1]
|
||||
const cachedVersion = cachedVersionMatch[1]
|
||||
|
||||
// 比较版本号 (semantic version)
|
||||
const compareResult = this.compareSemanticVersions(newVersion, cachedVersion)
|
||||
|
||||
logger.debug(`🔍 Version comparison: ${newVersion} vs ${cachedVersion} = ${compareResult}`)
|
||||
|
||||
return compareResult > 0 // 新版本更大则返回 true
|
||||
} catch (error) {
|
||||
logger.warn(`⚠️ Error comparing Claude Code versions, defaulting to update: ${error.message}`)
|
||||
return true // 出错时优先使用新的
|
||||
}
|
||||
}
|
||||
|
||||
// 🔢 比较版本号
|
||||
// 返回:1 表示 v1 > v2,-1 表示 v1 < v2,0 表示相等
|
||||
compareSemanticVersions(version1, version2) {
|
||||
// 将版本号字符串按"."分割成数字数组
|
||||
const arr1 = version1.split('.')
|
||||
const arr2 = version2.split('.')
|
||||
|
||||
// 获取两个版本号数组中的最大长度
|
||||
const maxLength = Math.max(arr1.length, arr2.length)
|
||||
|
||||
// 循环遍历,逐段比较版本号
|
||||
for (let i = 0; i < maxLength; i++) {
|
||||
// 如果某个版本号的某一段不存在,则视为0
|
||||
const num1 = parseInt(arr1[i] || 0, 10)
|
||||
const num2 = parseInt(arr2[i] || 0, 10)
|
||||
|
||||
if (num1 > num2) {
|
||||
return 1 // version1 大于 version2
|
||||
}
|
||||
if (num1 < num2) {
|
||||
return -1 // version1 小于 version2
|
||||
}
|
||||
}
|
||||
|
||||
return 0 // 两个版本号相等
|
||||
}
|
||||
|
||||
// 🎯 健康检查
|
||||
async healthCheck() {
|
||||
try {
|
||||
|
||||
@@ -854,6 +854,51 @@
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<!-- Claude User-Agent 版本配置 -->
|
||||
<div v-if="form.platform === 'claude'" class="mt-4">
|
||||
<label class="flex items-start">
|
||||
<input
|
||||
v-model="form.useUnifiedUserAgent"
|
||||
class="mt-1 text-blue-600 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700"
|
||||
type="checkbox"
|
||||
/>
|
||||
<div class="ml-3">
|
||||
<span class="text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||
使用统一 Claude Code 版本
|
||||
</span>
|
||||
<p class="mt-1 text-xs text-gray-500 dark:text-gray-400">
|
||||
开启后将使用从真实 Claude Code 客户端捕获的统一 User-Agent,提高兼容性
|
||||
</p>
|
||||
<div v-if="unifiedUserAgent" class="mt-1">
|
||||
<div class="flex items-center justify-between">
|
||||
<p class="text-xs text-green-600 dark:text-green-400">
|
||||
💡 当前统一版本:{{ unifiedUserAgent }}
|
||||
</p>
|
||||
<button
|
||||
class="ml-2 text-xs text-red-500 hover:text-red-700 dark:text-red-400 dark:hover:text-red-300"
|
||||
:disabled="clearingCache"
|
||||
type="button"
|
||||
@click="clearUnifiedCache"
|
||||
>
|
||||
<i v-if="!clearingCache" class="fas fa-trash-alt mr-1"></i>
|
||||
<div v-else class="loading-spinner mr-1"></div>
|
||||
{{ clearingCache ? '清除中...' : '清除缓存' }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="mt-1">
|
||||
<p class="text-xs text-gray-500 dark:text-gray-400">
|
||||
⏳ 等待从 Claude Code 客户端捕获 User-Agent
|
||||
</p>
|
||||
<p class="mt-1 text-xs text-gray-400 dark:text-gray-500">
|
||||
💡 提示:如果长时间未能捕获,请确认有 Claude Code 客户端正在使用此账户,
|
||||
或联系开发者检查 User-Agent 格式是否发生变化
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<!-- 所有平台的优先级设置 -->
|
||||
<div>
|
||||
<label class="mb-3 block text-sm font-semibold text-gray-700 dark:text-gray-300"
|
||||
@@ -1412,6 +1457,51 @@
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<!-- Claude User-Agent 版本配置(编辑模式) -->
|
||||
<div v-if="form.platform === 'claude'" class="mt-4">
|
||||
<label class="flex items-start">
|
||||
<input
|
||||
v-model="form.useUnifiedUserAgent"
|
||||
class="mt-1 text-blue-600 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700"
|
||||
type="checkbox"
|
||||
/>
|
||||
<div class="ml-3">
|
||||
<span class="text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||
使用统一 Claude Code 版本
|
||||
</span>
|
||||
<p class="mt-1 text-xs text-gray-500 dark:text-gray-400">
|
||||
开启后将使用从真实 Claude Code 客户端捕获的统一 User-Agent,提高兼容性
|
||||
</p>
|
||||
<div v-if="unifiedUserAgent" class="mt-1">
|
||||
<div class="flex items-center justify-between">
|
||||
<p class="text-xs text-green-600 dark:text-green-400">
|
||||
💡 当前统一版本:{{ unifiedUserAgent }}
|
||||
</p>
|
||||
<button
|
||||
class="ml-2 text-xs text-red-500 hover:text-red-700 dark:text-red-400 dark:hover:text-red-300"
|
||||
:disabled="clearingCache"
|
||||
type="button"
|
||||
@click="clearUnifiedCache"
|
||||
>
|
||||
<i v-if="!clearingCache" class="fas fa-trash-alt mr-1"></i>
|
||||
<div v-else class="loading-spinner mr-1"></div>
|
||||
{{ clearingCache ? '清除中...' : '清除缓存' }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="mt-1">
|
||||
<p class="text-xs text-gray-500 dark:text-gray-400">
|
||||
⏳ 等待从 Claude Code 客户端捕获 User-Agent
|
||||
</p>
|
||||
<p class="mt-1 text-xs text-gray-400 dark:text-gray-500">
|
||||
💡 提示:如果长时间未能捕获,请确认有 Claude Code 客户端正在使用此账户,
|
||||
或联系开发者检查 User-Agent 格式是否发生变化
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<!-- 所有平台的优先级设置(编辑模式) -->
|
||||
<div>
|
||||
<label class="mb-3 block text-sm font-semibold text-gray-700 dark:text-gray-300"
|
||||
@@ -1904,7 +1994,7 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, watch } from 'vue'
|
||||
import { ref, computed, watch, onMounted } from 'vue'
|
||||
import { showToast } from '@/utils/toast'
|
||||
import { apiClient } from '@/config/api'
|
||||
import { useAccountsStore } from '@/stores/accounts'
|
||||
@@ -1942,6 +2032,10 @@ const setupTokenAuthCode = ref('')
|
||||
const setupTokenCopied = ref(false)
|
||||
const setupTokenSessionId = ref('')
|
||||
|
||||
// Claude Code 统一 User-Agent 信息
|
||||
const unifiedUserAgent = ref('')
|
||||
const clearingCache = ref(false)
|
||||
|
||||
// 初始化代理配置
|
||||
const initProxyConfig = () => {
|
||||
if (props.account?.proxy && props.account.proxy.host && props.account.proxy.port) {
|
||||
@@ -1978,6 +2072,7 @@ const form = ref({
|
||||
accountType: props.account?.accountType || 'shared',
|
||||
subscriptionType: 'claude_max', // 默认为 Claude Max,兼容旧数据
|
||||
autoStopOnWarning: props.account?.autoStopOnWarning || false, // 5小时限制自动停止调度
|
||||
useUnifiedUserAgent: props.account?.useUnifiedUserAgent || false, // 使用统一Claude Code版本
|
||||
groupId: '',
|
||||
groupIds: [],
|
||||
projectId: props.account?.projectId || '',
|
||||
@@ -2255,6 +2350,7 @@ const handleOAuthSuccess = async (tokenInfo) => {
|
||||
data.claudeAiOauth = tokenInfo.claudeAiOauth || tokenInfo
|
||||
data.priority = form.value.priority || 50
|
||||
data.autoStopOnWarning = form.value.autoStopOnWarning || false
|
||||
data.useUnifiedUserAgent = form.value.useUnifiedUserAgent || false
|
||||
// 添加订阅类型信息
|
||||
data.subscriptionInfo = {
|
||||
accountType: form.value.subscriptionType || 'claude_max',
|
||||
@@ -2417,6 +2513,7 @@ const createAccount = async () => {
|
||||
}
|
||||
data.priority = form.value.priority || 50
|
||||
data.autoStopOnWarning = form.value.autoStopOnWarning || false
|
||||
data.useUnifiedUserAgent = form.value.useUnifiedUserAgent || false
|
||||
// 添加订阅类型信息
|
||||
data.subscriptionInfo = {
|
||||
accountType: form.value.subscriptionType || 'claude_max',
|
||||
@@ -2670,6 +2767,7 @@ const updateAccount = async () => {
|
||||
if (props.account.platform === 'claude') {
|
||||
data.priority = form.value.priority || 50
|
||||
data.autoStopOnWarning = form.value.autoStopOnWarning || false
|
||||
data.useUnifiedUserAgent = form.value.useUnifiedUserAgent || false
|
||||
// 更新订阅类型信息
|
||||
data.subscriptionInfo = {
|
||||
accountType: form.value.subscriptionType || 'claude_max',
|
||||
@@ -3071,6 +3169,7 @@ watch(
|
||||
accountType: newAccount.accountType || 'shared',
|
||||
subscriptionType: subscriptionType,
|
||||
autoStopOnWarning: newAccount.autoStopOnWarning || false,
|
||||
useUnifiedUserAgent: newAccount.useUnifiedUserAgent || false,
|
||||
groupId: groupId,
|
||||
groupIds: [],
|
||||
projectId: newAccount.projectId || '',
|
||||
@@ -3149,4 +3248,54 @@ watch(
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
// 获取统一 User-Agent 信息
|
||||
const fetchUnifiedUserAgent = async () => {
|
||||
try {
|
||||
const response = await apiClient.get('/admin/claude-code-version')
|
||||
if (response.success && response.userAgent) {
|
||||
unifiedUserAgent.value = response.userAgent
|
||||
} else {
|
||||
unifiedUserAgent.value = ''
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('Failed to fetch unified User-Agent:', error)
|
||||
unifiedUserAgent.value = ''
|
||||
}
|
||||
}
|
||||
|
||||
// 清除统一 User-Agent 缓存
|
||||
const clearUnifiedCache = async () => {
|
||||
clearingCache.value = true
|
||||
try {
|
||||
const response = await apiClient.post('/admin/claude-code-version/clear')
|
||||
if (response.success) {
|
||||
unifiedUserAgent.value = ''
|
||||
showToast('统一User-Agent缓存已清除', 'success')
|
||||
} else {
|
||||
showToast('清除缓存失败', 'error')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to clear unified User-Agent cache:', error)
|
||||
showToast('清除缓存失败:' + (error.message || '未知错误'), 'error')
|
||||
} finally {
|
||||
clearingCache.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 组件挂载时获取统一 User-Agent 信息
|
||||
onMounted(() => {
|
||||
// 获取Claude Code统一User-Agent信息
|
||||
fetchUnifiedUserAgent()
|
||||
})
|
||||
|
||||
// 监听平台变化,当切换到Claude平台时获取统一User-Agent信息
|
||||
watch(
|
||||
() => form.value.platform,
|
||||
(newPlatform) => {
|
||||
if (newPlatform === 'claude') {
|
||||
fetchUnifiedUserAgent()
|
||||
}
|
||||
}
|
||||
)
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user