mirror of
https://github.com/Wei-Shaw/claude-relay-service.git
synced 2026-01-23 09:38:02 +00:00
fix: 修复CCR账户表单验证和平台分组逻辑
- 修复CCR账户创建时的表单字段验证 - 统一CCR与Claude Console的处理逻辑 - 修复账户删除前的API Key绑定检查 - 修复Claude Console账户绑定的API Key计数 - 优化平台分组判断逻辑 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -456,10 +456,10 @@
|
|||||||
v-if="
|
v-if="
|
||||||
!isEdit &&
|
!isEdit &&
|
||||||
form.platform !== 'claude-console' &&
|
form.platform !== 'claude-console' &&
|
||||||
|
form.platform !== 'ccr' &&
|
||||||
form.platform !== 'bedrock' &&
|
form.platform !== 'bedrock' &&
|
||||||
form.platform !== 'azure_openai' &&
|
form.platform !== 'azure_openai' &&
|
||||||
form.platform !== 'openai-responses' &&
|
form.platform !== 'openai-responses'
|
||||||
form.platform !== 'ccr'
|
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
<label class="mb-3 block text-sm font-semibold text-gray-700 dark:text-gray-300"
|
<label class="mb-3 block text-sm font-semibold text-gray-700 dark:text-gray-300"
|
||||||
@@ -950,8 +950,11 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Claude Console 特定字段 -->
|
<!-- Claude Console 和 CCR 特定字段 -->
|
||||||
<div v-if="form.platform === 'claude-console' && !isEdit" class="space-y-4">
|
<div
|
||||||
|
v-if="(form.platform === 'claude-console' || form.platform === 'ccr') && !isEdit"
|
||||||
|
class="space-y-4"
|
||||||
|
>
|
||||||
<div>
|
<div>
|
||||||
<label class="mb-3 block text-sm font-semibold text-gray-700 dark:text-gray-300"
|
<label class="mb-3 block text-sm font-semibold text-gray-700 dark:text-gray-300"
|
||||||
>API URL *</label
|
>API URL *</label
|
||||||
@@ -1405,6 +1408,7 @@
|
|||||||
v-if="
|
v-if="
|
||||||
form.addType === 'manual' &&
|
form.addType === 'manual' &&
|
||||||
form.platform !== 'claude-console' &&
|
form.platform !== 'claude-console' &&
|
||||||
|
form.platform !== 'ccr' &&
|
||||||
form.platform !== 'bedrock' &&
|
form.platform !== 'bedrock' &&
|
||||||
form.platform !== 'azure_openai' &&
|
form.platform !== 'azure_openai' &&
|
||||||
form.platform !== 'openai-responses'
|
form.platform !== 'openai-responses'
|
||||||
@@ -1565,6 +1569,7 @@
|
|||||||
v-if="
|
v-if="
|
||||||
(form.addType === 'oauth' || form.addType === 'setup-token') &&
|
(form.addType === 'oauth' || form.addType === 'setup-token') &&
|
||||||
form.platform !== 'claude-console' &&
|
form.platform !== 'claude-console' &&
|
||||||
|
form.platform !== 'ccr' &&
|
||||||
form.platform !== 'bedrock' &&
|
form.platform !== 'bedrock' &&
|
||||||
form.platform !== 'azure_openai' &&
|
form.platform !== 'azure_openai' &&
|
||||||
form.platform !== 'openai-responses'
|
form.platform !== 'openai-responses'
|
||||||
@@ -2080,8 +2085,11 @@
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Claude Console 特定字段(编辑模式)-->
|
<!-- Claude Console 和 CCR 特定字段(编辑模式)-->
|
||||||
<div v-if="form.platform === 'claude-console'" class="space-y-4">
|
<div
|
||||||
|
v-if="form.platform === 'claude-console' || form.platform === 'ccr'"
|
||||||
|
class="space-y-4"
|
||||||
|
>
|
||||||
<div>
|
<div>
|
||||||
<label class="mb-3 block text-sm font-semibold text-gray-700">API URL</label>
|
<label class="mb-3 block text-sm font-semibold text-gray-700">API URL</label>
|
||||||
<input
|
<input
|
||||||
@@ -2608,6 +2616,7 @@
|
|||||||
<div
|
<div
|
||||||
v-if="
|
v-if="
|
||||||
form.platform !== 'claude-console' &&
|
form.platform !== 'claude-console' &&
|
||||||
|
form.platform !== 'ccr' &&
|
||||||
form.platform !== 'bedrock' &&
|
form.platform !== 'bedrock' &&
|
||||||
form.platform !== 'azure_openai' &&
|
form.platform !== 'azure_openai' &&
|
||||||
form.platform !== 'openai-responses'
|
form.platform !== 'openai-responses'
|
||||||
@@ -2752,7 +2761,7 @@ const platformGroup = ref('')
|
|||||||
|
|
||||||
// 根据现有平台确定分组
|
// 根据现有平台确定分组
|
||||||
const determinePlatformGroup = (platform) => {
|
const determinePlatformGroup = (platform) => {
|
||||||
if (['claude', 'claude-console', 'bedrock'].includes(platform)) {
|
if (['claude', 'claude-console', 'ccr', 'bedrock'].includes(platform)) {
|
||||||
return 'claude'
|
return 'claude'
|
||||||
} else if (['openai', 'openai-responses', 'azure_openai'].includes(platform)) {
|
} else if (['openai', 'openai-responses', 'azure_openai'].includes(platform)) {
|
||||||
return 'openai'
|
return 'openai'
|
||||||
@@ -3238,6 +3247,18 @@ const createAccount = async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CCR (Claude Code Router) 验证 - 使用与 Claude Console 相同的字段
|
||||||
|
if (form.value.platform === 'ccr') {
|
||||||
|
if (!form.value.apiUrl || form.value.apiUrl.trim() === '') {
|
||||||
|
errors.value.apiUrl = '请填写 API URL'
|
||||||
|
hasError = true
|
||||||
|
}
|
||||||
|
if (!form.value.apiKey || form.value.apiKey.trim() === '') {
|
||||||
|
errors.value.apiKey = '请填写 API Key'
|
||||||
|
hasError = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// OpenAI-Responses 验证
|
// OpenAI-Responses 验证
|
||||||
if (form.value.platform === 'openai-responses') {
|
if (form.value.platform === 'openai-responses') {
|
||||||
if (!form.value.baseApi || form.value.baseApi.trim() === '') {
|
if (!form.value.baseApi || form.value.baseApi.trim() === '') {
|
||||||
@@ -3277,7 +3298,7 @@ const createAccount = async () => {
|
|||||||
hasError = true
|
hasError = true
|
||||||
}
|
}
|
||||||
} else if (form.value.addType === 'manual') {
|
} else if (form.value.addType === 'manual') {
|
||||||
// 手动模式验证
|
// 手动模式验证 - 只有部分平台需要验证 Token
|
||||||
if (form.value.platform === 'openai') {
|
if (form.value.platform === 'openai') {
|
||||||
// OpenAI 平台必须有 Refresh Token
|
// OpenAI 平台必须有 Refresh Token
|
||||||
if (!form.value.refreshToken || form.value.refreshToken.trim() === '') {
|
if (!form.value.refreshToken || form.value.refreshToken.trim() === '') {
|
||||||
@@ -3285,13 +3306,20 @@ const createAccount = async () => {
|
|||||||
hasError = true
|
hasError = true
|
||||||
}
|
}
|
||||||
// Access Token 可选,如果没有会通过 Refresh Token 获取
|
// Access Token 可选,如果没有会通过 Refresh Token 获取
|
||||||
} else {
|
} else if (form.value.platform === 'gemini') {
|
||||||
// 其他平台(Gemini)需要 Access Token
|
// Gemini 平台需要 Access Token
|
||||||
|
if (!form.value.accessToken || form.value.accessToken.trim() === '') {
|
||||||
|
errors.value.accessToken = '请填写 Access Token'
|
||||||
|
hasError = true
|
||||||
|
}
|
||||||
|
} else if (form.value.platform === 'claude') {
|
||||||
|
// Claude 平台需要 Access Token
|
||||||
if (!form.value.accessToken || form.value.accessToken.trim() === '') {
|
if (!form.value.accessToken || form.value.accessToken.trim() === '') {
|
||||||
errors.value.accessToken = '请填写 Access Token'
|
errors.value.accessToken = '请填写 Access Token'
|
||||||
hasError = true
|
hasError = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Claude Console、CCR、OpenAI-Responses 等其他平台不需要 Token 验证
|
||||||
}
|
}
|
||||||
|
|
||||||
// 分组类型验证 - 创建账户流程修复
|
// 分组类型验证 - 创建账户流程修复
|
||||||
@@ -3413,8 +3441,8 @@ const createAccount = async () => {
|
|||||||
data.needsImmediateRefresh = true
|
data.needsImmediateRefresh = true
|
||||||
data.requireRefreshSuccess = true // 必须刷新成功才能创建账户
|
data.requireRefreshSuccess = true // 必须刷新成功才能创建账户
|
||||||
data.priority = form.value.priority || 50
|
data.priority = form.value.priority || 50
|
||||||
} else if (form.value.platform === 'claude-console') {
|
} else if (form.value.platform === 'claude-console' || form.value.platform === 'ccr') {
|
||||||
// Claude Console 账户特定数据
|
// Claude Console 和 CCR 账户特定数据(CCR 使用 Claude Console 的后端逻辑)
|
||||||
data.apiUrl = form.value.apiUrl
|
data.apiUrl = form.value.apiUrl
|
||||||
data.apiKey = form.value.apiKey
|
data.apiKey = form.value.apiKey
|
||||||
data.priority = form.value.priority || 50
|
data.priority = form.value.priority || 50
|
||||||
@@ -3464,7 +3492,8 @@ const createAccount = async () => {
|
|||||||
let result
|
let result
|
||||||
if (form.value.platform === 'claude') {
|
if (form.value.platform === 'claude') {
|
||||||
result = await accountsStore.createClaudeAccount(data)
|
result = await accountsStore.createClaudeAccount(data)
|
||||||
} else if (form.value.platform === 'claude-console') {
|
} else if (form.value.platform === 'claude-console' || form.value.platform === 'ccr') {
|
||||||
|
// CCR 使用 Claude Console 的后端 API
|
||||||
result = await accountsStore.createClaudeConsoleAccount(data)
|
result = await accountsStore.createClaudeConsoleAccount(data)
|
||||||
} else if (form.value.platform === 'openai-responses') {
|
} else if (form.value.platform === 'openai-responses') {
|
||||||
result = await accountsStore.createOpenAIResponsesAccount(data)
|
result = await accountsStore.createOpenAIResponsesAccount(data)
|
||||||
@@ -3841,8 +3870,8 @@ const showGroupManagement = ref(false)
|
|||||||
// 根据平台筛选分组
|
// 根据平台筛选分组
|
||||||
const filteredGroups = computed(() => {
|
const filteredGroups = computed(() => {
|
||||||
let platformFilter = form.value.platform
|
let platformFilter = form.value.platform
|
||||||
// Claude Console 使用 Claude 分组
|
// Claude Console 和 CCR 使用 Claude 分组
|
||||||
if (form.value.platform === 'claude-console') {
|
if (form.value.platform === 'claude-console' || form.value.platform === 'ccr') {
|
||||||
platformFilter = 'claude'
|
platformFilter = 'claude'
|
||||||
}
|
}
|
||||||
// OpenAI-Responses 使用 OpenAI 分组
|
// OpenAI-Responses 使用 OpenAI 分组
|
||||||
@@ -3889,10 +3918,11 @@ watch(
|
|||||||
// 处理添加方式的自动切换
|
// 处理添加方式的自动切换
|
||||||
if (
|
if (
|
||||||
newPlatform === 'claude-console' ||
|
newPlatform === 'claude-console' ||
|
||||||
|
newPlatform === 'ccr' ||
|
||||||
newPlatform === 'bedrock' ||
|
newPlatform === 'bedrock' ||
|
||||||
newPlatform === 'openai-responses'
|
newPlatform === 'openai-responses'
|
||||||
) {
|
) {
|
||||||
form.value.addType = 'manual' // Claude Console、Bedrock 和 OpenAI-Responses 只支持手动模式
|
form.value.addType = 'manual' // Claude Console、CCR、Bedrock 和 OpenAI-Responses 只支持手动模式
|
||||||
} else if (newPlatform === 'claude') {
|
} else if (newPlatform === 'claude') {
|
||||||
// 切换到 Claude 时,使用 Setup Token 作为默认方式
|
// 切换到 Claude 时,使用 Setup Token 作为默认方式
|
||||||
form.value.addType = 'setup-token'
|
form.value.addType = 'setup-token'
|
||||||
|
|||||||
@@ -346,7 +346,7 @@ const submit = async () => {
|
|||||||
}
|
}
|
||||||
const res = await apiClient.put(`/admin/ccr-accounts/${props.account.id}`, updates)
|
const res = await apiClient.put(`/admin/ccr-accounts/${props.account.id}`, updates)
|
||||||
if (res.success) {
|
if (res.success) {
|
||||||
showToast('保存成功', 'success')
|
// 不在这里显示 toast,由父组件统一处理
|
||||||
emit('success')
|
emit('success')
|
||||||
} else {
|
} else {
|
||||||
showToast(res.message || '保存失败', 'error')
|
showToast(res.message || '保存失败', 'error')
|
||||||
@@ -369,7 +369,7 @@ const submit = async () => {
|
|||||||
}
|
}
|
||||||
const res = await apiClient.post('/admin/ccr-accounts', payload)
|
const res = await apiClient.post('/admin/ccr-accounts', payload)
|
||||||
if (res.success) {
|
if (res.success) {
|
||||||
showToast('创建成功', 'success')
|
// 不在这里显示 toast,由父组件统一处理
|
||||||
emit('success')
|
emit('success')
|
||||||
} else {
|
} else {
|
||||||
showToast(res.message || '创建失败', 'error')
|
showToast(res.message || '创建失败', 'error')
|
||||||
|
|||||||
@@ -492,6 +492,7 @@
|
|||||||
account.platform === 'bedrock' ||
|
account.platform === 'bedrock' ||
|
||||||
account.platform === 'gemini' ||
|
account.platform === 'gemini' ||
|
||||||
account.platform === 'openai' ||
|
account.platform === 'openai' ||
|
||||||
|
account.platform === 'openai-responses' ||
|
||||||
account.platform === 'azure_openai' ||
|
account.platform === 'azure_openai' ||
|
||||||
account.platform === 'ccr'
|
account.platform === 'ccr'
|
||||||
"
|
"
|
||||||
@@ -1295,9 +1296,12 @@ const loadAccounts = async (forceReload = false) => {
|
|||||||
|
|
||||||
if (claudeConsoleData.success) {
|
if (claudeConsoleData.success) {
|
||||||
const claudeConsoleAccounts = (claudeConsoleData.data || []).map((acc) => {
|
const claudeConsoleAccounts = (claudeConsoleData.data || []).map((acc) => {
|
||||||
// Claude Console账户暂时不支持直接绑定
|
// 计算每个Claude Console账户绑定的API Key数量
|
||||||
|
const boundApiKeysCount = apiKeys.value.filter(
|
||||||
|
(key) => key.claudeConsoleAccountId === acc.id
|
||||||
|
).length
|
||||||
// 后端已经包含了groupInfos,直接使用
|
// 后端已经包含了groupInfos,直接使用
|
||||||
return { ...acc, platform: 'claude-console', boundApiKeysCount: 0 }
|
return { ...acc, platform: 'claude-console', boundApiKeysCount }
|
||||||
})
|
})
|
||||||
allAccounts.push(...claudeConsoleAccounts)
|
allAccounts.push(...claudeConsoleAccounts)
|
||||||
}
|
}
|
||||||
@@ -1594,8 +1598,11 @@ const deleteAccount = async (account) => {
|
|||||||
const boundKeysCount = apiKeys.value.filter(
|
const boundKeysCount = apiKeys.value.filter(
|
||||||
(key) =>
|
(key) =>
|
||||||
key.claudeAccountId === account.id ||
|
key.claudeAccountId === account.id ||
|
||||||
|
key.claudeConsoleAccountId === account.id ||
|
||||||
key.geminiAccountId === account.id ||
|
key.geminiAccountId === account.id ||
|
||||||
key.openaiAccountId === account.id
|
key.openaiAccountId === account.id ||
|
||||||
|
key.azureOpenaiAccountId === account.id ||
|
||||||
|
key.openaiAccountId === `responses:${account.id}`
|
||||||
).length
|
).length
|
||||||
|
|
||||||
if (boundKeysCount > 0) {
|
if (boundKeysCount > 0) {
|
||||||
|
|||||||
Reference in New Issue
Block a user