mirror of
https://github.com/Wei-Shaw/claude-relay-service.git
synced 2026-04-28 11:08:38 +00:00
1
This commit is contained in:
@@ -477,6 +477,36 @@
|
||||
<i class="fas fa-check text-xs text-white"></i>
|
||||
</div>
|
||||
</label>
|
||||
<label
|
||||
class="group relative flex cursor-pointer items-center rounded-md border p-2 transition-all"
|
||||
:class="[
|
||||
form.platform === 'gemini-antigravity'
|
||||
? 'border-purple-500 bg-purple-50 dark:border-purple-400 dark:bg-purple-900/30'
|
||||
: 'border-gray-300 bg-white hover:border-purple-400 hover:bg-purple-50/50 dark:border-gray-600 dark:bg-gray-700 dark:hover:border-purple-500 dark:hover:bg-purple-900/20'
|
||||
]"
|
||||
>
|
||||
<input
|
||||
v-model="form.platform"
|
||||
class="sr-only"
|
||||
type="radio"
|
||||
value="gemini-antigravity"
|
||||
/>
|
||||
<div class="flex items-center gap-2">
|
||||
<i class="fas fa-rocket text-sm text-purple-600 dark:text-purple-400"></i>
|
||||
<div>
|
||||
<span class="block text-xs font-medium text-gray-900 dark:text-gray-100"
|
||||
>Antigravity</span
|
||||
>
|
||||
<span class="text-xs text-gray-500 dark:text-gray-400">OAuth</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="form.platform === 'gemini-antigravity'"
|
||||
class="absolute right-1 top-1 flex h-4 w-4 items-center justify-center rounded-full bg-purple-500"
|
||||
>
|
||||
<i class="fas fa-check text-xs text-white"></i>
|
||||
</div>
|
||||
</label>
|
||||
|
||||
<label
|
||||
class="group relative flex cursor-pointer items-center rounded-md border p-2 transition-all"
|
||||
@@ -772,7 +802,7 @@
|
||||
</div>
|
||||
|
||||
<!-- Gemini 项目 ID 字段 -->
|
||||
<div v-if="form.platform === 'gemini'">
|
||||
<div v-if="form.platform === 'gemini' || form.platform === 'gemini-antigravity'">
|
||||
<label class="mb-3 block text-sm font-semibold text-gray-700 dark:text-gray-300"
|
||||
>项目 ID (可选)</label
|
||||
>
|
||||
@@ -822,41 +852,194 @@
|
||||
</div>
|
||||
|
||||
<!-- Bedrock 特定字段 -->
|
||||
<div v-if="form.platform === 'bedrock' && !isEdit" class="space-y-4">
|
||||
<div v-if="form.platform === 'bedrock'" class="space-y-4">
|
||||
<!-- 凭证类型选择器 -->
|
||||
<div>
|
||||
<label class="mb-3 block text-sm font-semibold text-gray-700 dark:text-gray-300"
|
||||
>AWS 访问密钥 ID *</label
|
||||
>凭证类型 *</label
|
||||
>
|
||||
<input
|
||||
v-model="form.accessKeyId"
|
||||
class="form-input w-full border-gray-300 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-200 dark:placeholder-gray-400"
|
||||
:class="{ 'border-red-500': errors.accessKeyId }"
|
||||
placeholder="请输入 AWS Access Key ID"
|
||||
required
|
||||
type="text"
|
||||
/>
|
||||
<p v-if="errors.accessKeyId" class="mt-1 text-xs text-red-500">
|
||||
{{ errors.accessKeyId }}
|
||||
</p>
|
||||
<div v-if="!isEdit" class="flex gap-4">
|
||||
<label class="flex cursor-pointer items-center">
|
||||
<input
|
||||
v-model="form.credentialType"
|
||||
class="mr-2 text-blue-600 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700"
|
||||
type="radio"
|
||||
value="access_key"
|
||||
/>
|
||||
<span class="text-sm text-gray-700 dark:text-gray-300"
|
||||
>AWS Access Key(访问密钥)</span
|
||||
>
|
||||
</label>
|
||||
<label class="flex cursor-pointer items-center">
|
||||
<input
|
||||
v-model="form.credentialType"
|
||||
class="mr-2 text-blue-600 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700"
|
||||
type="radio"
|
||||
value="bearer_token"
|
||||
/>
|
||||
<span class="text-sm text-gray-700 dark:text-gray-300"
|
||||
>Bearer Token(长期令牌)</span
|
||||
>
|
||||
</label>
|
||||
</div>
|
||||
<div v-else class="flex gap-4">
|
||||
<label class="flex items-center opacity-60">
|
||||
<input
|
||||
v-model="form.credentialType"
|
||||
class="mr-2 text-blue-600 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700"
|
||||
disabled
|
||||
type="radio"
|
||||
value="access_key"
|
||||
/>
|
||||
<span class="text-sm text-gray-700 dark:text-gray-300"
|
||||
>AWS Access Key(访问密钥)</span
|
||||
>
|
||||
</label>
|
||||
<label class="flex items-center opacity-60">
|
||||
<input
|
||||
v-model="form.credentialType"
|
||||
class="mr-2 text-blue-600 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700"
|
||||
disabled
|
||||
type="radio"
|
||||
value="bearer_token"
|
||||
/>
|
||||
<span class="text-sm text-gray-700 dark:text-gray-300"
|
||||
>Bearer Token(长期令牌)</span
|
||||
>
|
||||
</label>
|
||||
</div>
|
||||
<div
|
||||
class="mt-2 rounded-lg border border-blue-200 bg-blue-50 p-3 dark:border-blue-700 dark:bg-blue-900/30"
|
||||
>
|
||||
<div class="flex items-start gap-2">
|
||||
<i class="fas fa-info-circle mt-0.5 text-blue-600 dark:text-blue-400" />
|
||||
<div class="text-xs text-blue-700 dark:text-blue-300">
|
||||
<p v-if="form.credentialType === 'access_key'" class="font-medium">
|
||||
使用 AWS Access Key ID 和 Secret Access Key 进行身份验证(支持临时凭证)
|
||||
</p>
|
||||
<p v-else class="font-medium">
|
||||
使用 AWS Bedrock API Keys 生成的 Bearer Token
|
||||
进行身份验证,更简单、权限范围更小
|
||||
</p>
|
||||
<p v-if="isEdit" class="mt-1 text-xs italic">
|
||||
💡 编辑模式下凭证类型不可更改,如需切换类型请重新创建账户
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<!-- AWS Access Key 字段(仅在 access_key 模式下显示)-->
|
||||
<div v-if="form.credentialType === 'access_key'">
|
||||
<div>
|
||||
<label class="mb-3 block text-sm font-semibold text-gray-700 dark:text-gray-300"
|
||||
>AWS 访问密钥 ID {{ isEdit ? '' : '*' }}</label
|
||||
>
|
||||
<input
|
||||
v-model="form.accessKeyId"
|
||||
class="form-input w-full border-gray-300 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-200 dark:placeholder-gray-400"
|
||||
:class="{ 'border-red-500': errors.accessKeyId }"
|
||||
:placeholder="isEdit ? '留空则保持原有凭证不变' : '请输入 AWS Access Key ID'"
|
||||
:required="!isEdit"
|
||||
type="text"
|
||||
/>
|
||||
<p v-if="errors.accessKeyId" class="mt-1 text-xs text-red-500">
|
||||
{{ errors.accessKeyId }}
|
||||
</p>
|
||||
<p v-if="isEdit" class="mt-1 text-xs text-gray-500 dark:text-gray-400">
|
||||
💡 编辑模式下,留空则保持原有 Access Key ID 不变
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="mb-3 block text-sm font-semibold text-gray-700 dark:text-gray-300"
|
||||
>AWS 秘密访问密钥 {{ isEdit ? '' : '*' }}</label
|
||||
>
|
||||
<input
|
||||
v-model="form.secretAccessKey"
|
||||
class="form-input w-full border-gray-300 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-200 dark:placeholder-gray-400"
|
||||
:class="{ 'border-red-500': errors.secretAccessKey }"
|
||||
:placeholder="
|
||||
isEdit ? '留空则保持原有凭证不变' : '请输入 AWS Secret Access Key'
|
||||
"
|
||||
:required="!isEdit"
|
||||
type="password"
|
||||
/>
|
||||
<p v-if="errors.secretAccessKey" class="mt-1 text-xs text-red-500">
|
||||
{{ errors.secretAccessKey }}
|
||||
</p>
|
||||
<p v-if="isEdit" class="mt-1 text-xs text-gray-500 dark:text-gray-400">
|
||||
💡 编辑模式下,留空则保持原有 Secret Access Key 不变
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="mb-3 block text-sm font-semibold text-gray-700 dark:text-gray-300"
|
||||
>会话令牌 (可选)</label
|
||||
>
|
||||
<input
|
||||
v-model="form.sessionToken"
|
||||
class="form-input w-full border-gray-300 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-200 dark:placeholder-gray-400"
|
||||
:placeholder="
|
||||
isEdit
|
||||
? '留空则保持原有 Session Token 不变'
|
||||
: '如果使用临时凭证,请输入会话令牌'
|
||||
"
|
||||
type="password"
|
||||
/>
|
||||
<p class="mt-1 text-xs text-gray-500 dark:text-gray-400">
|
||||
仅在使用临时 AWS 凭证时需要填写
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Bearer Token 字段(仅在 bearer_token 模式下显示)-->
|
||||
<div v-if="form.credentialType === 'bearer_token'">
|
||||
<label class="mb-3 block text-sm font-semibold text-gray-700 dark:text-gray-300"
|
||||
>AWS 秘密访问密钥 *</label
|
||||
>Bearer Token {{ isEdit ? '' : '*' }}</label
|
||||
>
|
||||
<input
|
||||
v-model="form.secretAccessKey"
|
||||
v-model="form.bearerToken"
|
||||
class="form-input w-full border-gray-300 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-200 dark:placeholder-gray-400"
|
||||
:class="{ 'border-red-500': errors.secretAccessKey }"
|
||||
placeholder="请输入 AWS Secret Access Key"
|
||||
required
|
||||
:class="{ 'border-red-500': errors.bearerToken }"
|
||||
:placeholder="
|
||||
isEdit ? '留空则保持原有 Bearer Token 不变' : '请输入 AWS Bearer Token'
|
||||
"
|
||||
:required="!isEdit"
|
||||
type="password"
|
||||
/>
|
||||
<p v-if="errors.secretAccessKey" class="mt-1 text-xs text-red-500">
|
||||
{{ errors.secretAccessKey }}
|
||||
<p v-if="errors.bearerToken" class="mt-1 text-xs text-red-500">
|
||||
{{ errors.bearerToken }}
|
||||
</p>
|
||||
<p v-if="isEdit" class="mt-1 text-xs text-gray-500 dark:text-gray-400">
|
||||
💡 编辑模式下,留空则保持原有 Bearer Token 不变
|
||||
</p>
|
||||
<div
|
||||
class="mt-2 rounded-lg border border-green-200 bg-green-50 p-3 dark:border-green-700 dark:bg-green-900/30"
|
||||
>
|
||||
<div class="flex items-start gap-2">
|
||||
<i class="fas fa-key mt-0.5 text-green-600 dark:text-green-400" />
|
||||
<div class="text-xs text-green-700 dark:text-green-300">
|
||||
<p class="mb-1 font-medium">Bearer Token 说明:</p>
|
||||
<ul class="list-inside list-disc space-y-1 text-xs">
|
||||
<li>输入 AWS Bedrock API Keys 生成的 Bearer Token</li>
|
||||
<li>Bearer Token 仅限 Bedrock 服务访问,权限范围更小</li>
|
||||
<li>相比 Access Key 更简单,无需 Secret Key</li>
|
||||
<li>
|
||||
参考:<a
|
||||
class="text-green-600 underline dark:text-green-400"
|
||||
href="https://aws.amazon.com/cn/blogs/machine-learning/accelerate-ai-development-with-amazon-bedrock-api-keys/"
|
||||
target="_blank"
|
||||
>AWS 官方文档</a
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- AWS 区域(两种凭证类型都需要)-->
|
||||
<div>
|
||||
<label class="mb-3 block text-sm font-semibold text-gray-700 dark:text-gray-300"
|
||||
>AWS 区域 *</label
|
||||
@@ -872,10 +1055,12 @@
|
||||
<p v-if="errors.region" class="mt-1 text-xs text-red-500">
|
||||
{{ errors.region }}
|
||||
</p>
|
||||
<div class="mt-2 rounded-lg border border-blue-200 bg-blue-50 p-3">
|
||||
<div
|
||||
class="mt-2 rounded-lg border border-blue-200 bg-blue-50 p-3 dark:border-blue-700 dark:bg-blue-900/30"
|
||||
>
|
||||
<div class="flex items-start gap-2">
|
||||
<i class="fas fa-info-circle mt-0.5 text-blue-600" />
|
||||
<div class="text-xs text-blue-700">
|
||||
<i class="fas fa-info-circle mt-0.5 text-blue-600 dark:text-blue-400" />
|
||||
<div class="text-xs text-blue-700 dark:text-blue-300">
|
||||
<p class="mb-1 font-medium">常用 AWS 区域参考:</p>
|
||||
<div class="grid grid-cols-2 gap-1 text-xs">
|
||||
<span>• us-east-1 (美国东部)</span>
|
||||
@@ -885,27 +1070,14 @@
|
||||
<span>• ap-northeast-1 (东京)</span>
|
||||
<span>• eu-central-1 (法兰克福)</span>
|
||||
</div>
|
||||
<p class="mt-2 text-blue-600">💡 请输入完整的区域代码,如 us-east-1</p>
|
||||
<p class="mt-2 text-blue-600 dark:text-blue-400">
|
||||
💡 请输入完整的区域代码,如 us-east-1
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="mb-3 block text-sm font-semibold text-gray-700 dark:text-gray-300"
|
||||
>会话令牌 (可选)</label
|
||||
>
|
||||
<input
|
||||
v-model="form.sessionToken"
|
||||
class="form-input w-full border-gray-300 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-200 dark:placeholder-gray-400"
|
||||
placeholder="如果使用临时凭证,请输入会话令牌"
|
||||
type="password"
|
||||
/>
|
||||
<p class="mt-1 text-xs text-gray-500 dark:text-gray-400">
|
||||
仅在使用临时 AWS 凭证时需要填写
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="mb-3 block text-sm font-semibold text-gray-700 dark:text-gray-300"
|
||||
>默认主模型 (可选)</label
|
||||
@@ -1824,7 +1996,7 @@
|
||||
Token,建议也一并填写以支持自动刷新。
|
||||
</p>
|
||||
<p
|
||||
v-else-if="form.platform === 'gemini'"
|
||||
v-else-if="form.platform === 'gemini' || form.platform === 'gemini-antigravity'"
|
||||
class="mb-2 text-sm text-blue-800 dark:text-blue-300"
|
||||
>
|
||||
请输入有效的 Gemini Access Token。如果您有 Refresh
|
||||
@@ -1861,7 +2033,9 @@
|
||||
文件中的凭证, 请勿使用 Claude 官网 API Keys 页面的密钥。
|
||||
</p>
|
||||
<p
|
||||
v-else-if="form.platform === 'gemini'"
|
||||
v-else-if="
|
||||
form.platform === 'gemini' || form.platform === 'gemini-antigravity'
|
||||
"
|
||||
class="text-xs text-blue-800 dark:text-blue-300"
|
||||
>
|
||||
请从已登录 Gemini CLI 的机器上获取
|
||||
@@ -2591,7 +2765,7 @@
|
||||
</div>
|
||||
|
||||
<!-- Gemini 项目 ID 字段 -->
|
||||
<div v-if="form.platform === 'gemini'">
|
||||
<div v-if="form.platform === 'gemini' || form.platform === 'gemini-antigravity'">
|
||||
<label class="mb-3 block text-sm font-semibold text-gray-700 dark:text-gray-300"
|
||||
>项目 ID (可选)</label
|
||||
>
|
||||
@@ -3805,10 +3979,9 @@
|
||||
<script setup>
|
||||
import { ref, computed, watch, onMounted } from 'vue'
|
||||
import { showToast } from '@/utils/tools'
|
||||
import * as httpApi from '@/utils/http_apis'
|
||||
import { getModels } from '@/utils/http_apis'
|
||||
|
||||
import * as httpApis from '@/utils/http_apis'
|
||||
import { useAccountsStore } from '@/stores/accounts'
|
||||
import { useConfirm } from '@/composables/useConfirm'
|
||||
import ProxyConfig from './ProxyConfig.vue'
|
||||
import OAuthFlow from './OAuthFlow.vue'
|
||||
import ConfirmModal from '@/components/common/ConfirmModal.vue'
|
||||
@@ -3825,7 +3998,28 @@ const props = defineProps({
|
||||
const emit = defineEmits(['close', 'success', 'platform-changed'])
|
||||
|
||||
const accountsStore = useAccountsStore()
|
||||
const { showConfirmModal, confirmOptions, showConfirm, handleConfirm, handleCancel } = useConfirm()
|
||||
|
||||
// 确认弹窗状态
|
||||
const showConfirmModal = ref(false)
|
||||
const confirmOptions = ref({ title: '', message: '', confirmText: '继续', cancelText: '取消' })
|
||||
let confirmResolve = null
|
||||
const showConfirm = (title, message, confirmText = '继续', cancelText = '取消') => {
|
||||
return new Promise((resolve) => {
|
||||
confirmOptions.value = { title, message, confirmText, cancelText }
|
||||
confirmResolve = resolve
|
||||
showConfirmModal.value = true
|
||||
})
|
||||
}
|
||||
const handleConfirm = () => {
|
||||
showConfirmModal.value = false
|
||||
confirmResolve?.(true)
|
||||
confirmResolve = null
|
||||
}
|
||||
const handleCancel = () => {
|
||||
showConfirmModal.value = false
|
||||
confirmResolve?.(false)
|
||||
confirmResolve = null
|
||||
}
|
||||
|
||||
// 是否为编辑模式
|
||||
const isEdit = computed(() => !!props.account)
|
||||
@@ -3881,7 +4075,7 @@ const determinePlatformGroup = (platform) => {
|
||||
return 'claude'
|
||||
} else if (['openai', 'openai-responses', 'azure_openai'].includes(platform)) {
|
||||
return 'openai'
|
||||
} else if (['gemini', 'gemini-api'].includes(platform)) {
|
||||
} else if (['gemini', 'gemini-antigravity', 'gemini-api'].includes(platform)) {
|
||||
return 'gemini'
|
||||
} else if (platform === 'droid') {
|
||||
return 'droid'
|
||||
@@ -4016,7 +4210,8 @@ const form = ref({
|
||||
platform: props.account?.platform || 'claude',
|
||||
addType: (() => {
|
||||
const platform = props.account?.platform || 'claude'
|
||||
if (platform === 'gemini' || platform === 'openai') return 'oauth'
|
||||
if (platform === 'gemini' || platform === 'gemini-antigravity' || platform === 'openai')
|
||||
return 'oauth'
|
||||
if (platform === 'claude') return 'oauth'
|
||||
return 'manual'
|
||||
})(),
|
||||
@@ -4073,10 +4268,12 @@ const form = ref({
|
||||
// 并发控制字段
|
||||
maxConcurrentTasks: props.account?.maxConcurrentTasks || 0,
|
||||
// Bedrock 特定字段
|
||||
credentialType: props.account?.credentialType || 'access_key', // 'access_key' 或 'bearer_token'
|
||||
accessKeyId: props.account?.accessKeyId || '',
|
||||
secretAccessKey: props.account?.secretAccessKey || '',
|
||||
region: props.account?.region || '',
|
||||
sessionToken: props.account?.sessionToken || '',
|
||||
bearerToken: props.account?.bearerToken || '', // Bearer Token 字段
|
||||
defaultModel: props.account?.defaultModel || '',
|
||||
smallFastModel: props.account?.smallFastModel || '',
|
||||
// Azure OpenAI 特定字段
|
||||
@@ -4117,7 +4314,7 @@ const commonModels = ref([])
|
||||
// 加载模型列表
|
||||
const loadCommonModels = async () => {
|
||||
try {
|
||||
const result = await getModels()
|
||||
const result = await httpApis.getModelsApi()
|
||||
if (result.success && result.data?.all) {
|
||||
commonModels.value = result.data.all
|
||||
}
|
||||
@@ -4239,6 +4436,7 @@ const errors = ref({
|
||||
accessKeyId: '',
|
||||
secretAccessKey: '',
|
||||
region: '',
|
||||
bearerToken: '',
|
||||
azureEndpoint: '',
|
||||
deploymentName: ''
|
||||
})
|
||||
@@ -4331,7 +4529,7 @@ const loadAccountUsage = async () => {
|
||||
if (!isEdit.value || !props.account?.id) return
|
||||
|
||||
try {
|
||||
const response = await httpApi.get(`/admin/claude-console-accounts/${props.account.id}/usage`)
|
||||
const response = await httpApis.getClaudeConsoleAccountUsageApi(props.account.id)
|
||||
if (response) {
|
||||
// 更新表单中的使用量数据
|
||||
form.value.dailyUsage = response.dailyUsage || 0
|
||||
@@ -4358,7 +4556,7 @@ const selectPlatformGroup = (group) => {
|
||||
} else if (group === 'openai') {
|
||||
form.value.platform = 'openai'
|
||||
} else if (group === 'gemini') {
|
||||
form.value.platform = 'gemini'
|
||||
form.value.platform = 'gemini' // Default to Gemini CLI, user can select Antigravity
|
||||
} else if (group === 'droid') {
|
||||
form.value.platform = 'droid'
|
||||
}
|
||||
@@ -4395,7 +4593,11 @@ const nextStep = async () => {
|
||||
}
|
||||
|
||||
// 对于Gemini账户,检查项目 ID
|
||||
if (form.value.platform === 'gemini' && oauthStep.value === 1 && form.value.addType === 'oauth') {
|
||||
if (
|
||||
(form.value.platform === 'gemini' || form.value.platform === 'gemini-antigravity') &&
|
||||
oauthStep.value === 1 &&
|
||||
form.value.addType === 'oauth'
|
||||
) {
|
||||
if (!form.value.projectId || form.value.projectId.trim() === '') {
|
||||
// 使用自定义确认弹窗
|
||||
const confirmed = await showConfirm(
|
||||
@@ -4768,9 +4970,14 @@ const handleOAuthSuccess = async (tokenInfoOrList) => {
|
||||
hasClaudePro: form.value.subscriptionType === 'claude_pro',
|
||||
manuallySet: true // 标记为手动设置
|
||||
}
|
||||
} else if (currentPlatform === 'gemini') {
|
||||
// Gemini使用geminiOauth字段
|
||||
} else if (currentPlatform === 'gemini' || currentPlatform === 'gemini-antigravity') {
|
||||
// Gemini/Antigravity使用geminiOauth字段
|
||||
data.geminiOauth = tokenInfo.tokens || tokenInfo
|
||||
// 根据 platform 设置 oauthProvider
|
||||
data.oauthProvider =
|
||||
currentPlatform === 'gemini-antigravity'
|
||||
? 'antigravity'
|
||||
: tokenInfo.oauthProvider || 'gemini-cli'
|
||||
if (form.value.projectId) {
|
||||
data.projectId = form.value.projectId
|
||||
}
|
||||
@@ -4942,14 +5149,27 @@ const createAccount = async () => {
|
||||
hasError = true
|
||||
}
|
||||
} else if (form.value.platform === 'bedrock') {
|
||||
// Bedrock 验证
|
||||
if (!form.value.accessKeyId || form.value.accessKeyId.trim() === '') {
|
||||
errors.value.accessKeyId = '请填写 AWS 访问密钥 ID'
|
||||
hasError = true
|
||||
}
|
||||
if (!form.value.secretAccessKey || form.value.secretAccessKey.trim() === '') {
|
||||
errors.value.secretAccessKey = '请填写 AWS 秘密访问密钥'
|
||||
hasError = true
|
||||
// Bedrock 验证 - 根据凭证类型进行不同验证
|
||||
if (form.value.credentialType === 'access_key') {
|
||||
// Access Key 模式:创建时必填,编辑时可选(留空则保持原有凭证)
|
||||
if (!isEdit.value) {
|
||||
if (!form.value.accessKeyId || form.value.accessKeyId.trim() === '') {
|
||||
errors.value.accessKeyId = '请填写 AWS 访问密钥 ID'
|
||||
hasError = true
|
||||
}
|
||||
if (!form.value.secretAccessKey || form.value.secretAccessKey.trim() === '') {
|
||||
errors.value.secretAccessKey = '请填写 AWS 秘密访问密钥'
|
||||
hasError = true
|
||||
}
|
||||
}
|
||||
} else if (form.value.credentialType === 'bearer_token') {
|
||||
// Bearer Token 模式:创建时必填,编辑时可选(留空则保持原有凭证)
|
||||
if (!isEdit.value) {
|
||||
if (!form.value.bearerToken || form.value.bearerToken.trim() === '') {
|
||||
errors.value.bearerToken = '请填写 Bearer Token'
|
||||
hasError = true
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!form.value.region || form.value.region.trim() === '') {
|
||||
errors.value.region = '请选择 AWS 区域'
|
||||
@@ -5192,6 +5412,10 @@ const createAccount = async () => {
|
||||
data.rateLimitDuration = 60 // 默认值60,不从用户输入获取
|
||||
data.dailyQuota = form.value.dailyQuota || 0
|
||||
data.quotaResetTime = form.value.quotaResetTime || '00:00'
|
||||
} else if (form.value.platform === 'gemini-antigravity') {
|
||||
// Antigravity OAuth - set oauthProvider, submission happens below
|
||||
data.oauthProvider = 'antigravity'
|
||||
data.priority = form.value.priority || 50
|
||||
} else if (form.value.platform === 'gemini-api') {
|
||||
// Gemini API 账户特定数据
|
||||
data.baseUrl = form.value.baseUrl || 'https://generativelanguage.googleapis.com'
|
||||
@@ -5201,12 +5425,21 @@ const createAccount = async () => {
|
||||
? form.value.supportedModels
|
||||
: []
|
||||
} else if (form.value.platform === 'bedrock') {
|
||||
// Bedrock 账户特定数据 - 构造 awsCredentials 对象
|
||||
data.awsCredentials = {
|
||||
accessKeyId: form.value.accessKeyId,
|
||||
secretAccessKey: form.value.secretAccessKey,
|
||||
sessionToken: form.value.sessionToken || null
|
||||
// Bedrock 账户特定数据
|
||||
data.credentialType = form.value.credentialType || 'access_key'
|
||||
|
||||
// 根据凭证类型构造不同的凭证对象
|
||||
if (form.value.credentialType === 'access_key') {
|
||||
data.awsCredentials = {
|
||||
accessKeyId: form.value.accessKeyId,
|
||||
secretAccessKey: form.value.secretAccessKey,
|
||||
sessionToken: form.value.sessionToken || null
|
||||
}
|
||||
} else if (form.value.credentialType === 'bearer_token') {
|
||||
// Bearer Token 模式:必须传递 Bearer Token
|
||||
data.bearerToken = form.value.bearerToken
|
||||
}
|
||||
|
||||
data.region = form.value.region
|
||||
data.defaultModel = form.value.defaultModel || null
|
||||
data.smallFastModel = form.value.smallFastModel || null
|
||||
@@ -5243,7 +5476,7 @@ const createAccount = async () => {
|
||||
result = await accountsStore.createOpenAIAccount(data)
|
||||
} else if (form.value.platform === 'azure_openai') {
|
||||
result = await accountsStore.createAzureOpenAIAccount(data)
|
||||
} else if (form.value.platform === 'gemini') {
|
||||
} else if (form.value.platform === 'gemini' || form.value.platform === 'gemini-antigravity') {
|
||||
result = await accountsStore.createGeminiAccount(data)
|
||||
} else if (form.value.platform === 'gemini-api') {
|
||||
result = await accountsStore.createGeminiApiAccount(data)
|
||||
@@ -5534,19 +5767,33 @@ const updateAccount = async () => {
|
||||
|
||||
// Bedrock 特定更新
|
||||
if (props.account.platform === 'bedrock') {
|
||||
// 只有当有凭证变更时才构造 awsCredentials 对象
|
||||
if (form.value.accessKeyId || form.value.secretAccessKey || form.value.sessionToken) {
|
||||
data.awsCredentials = {}
|
||||
if (form.value.accessKeyId) {
|
||||
data.awsCredentials.accessKeyId = form.value.accessKeyId
|
||||
// 更新凭证类型
|
||||
if (form.value.credentialType) {
|
||||
data.credentialType = form.value.credentialType
|
||||
}
|
||||
|
||||
// 根据凭证类型更新凭证
|
||||
if (form.value.credentialType === 'access_key') {
|
||||
// 只有当有凭证变更时才构造 awsCredentials 对象
|
||||
if (form.value.accessKeyId || form.value.secretAccessKey || form.value.sessionToken) {
|
||||
data.awsCredentials = {}
|
||||
if (form.value.accessKeyId) {
|
||||
data.awsCredentials.accessKeyId = form.value.accessKeyId
|
||||
}
|
||||
if (form.value.secretAccessKey) {
|
||||
data.awsCredentials.secretAccessKey = form.value.secretAccessKey
|
||||
}
|
||||
if (form.value.sessionToken !== undefined) {
|
||||
data.awsCredentials.sessionToken = form.value.sessionToken || null
|
||||
}
|
||||
}
|
||||
if (form.value.secretAccessKey) {
|
||||
data.awsCredentials.secretAccessKey = form.value.secretAccessKey
|
||||
}
|
||||
if (form.value.sessionToken !== undefined) {
|
||||
data.awsCredentials.sessionToken = form.value.sessionToken || null
|
||||
} else if (form.value.credentialType === 'bearer_token') {
|
||||
// Bearer Token 模式:更新 Bearer Token(编辑时可选,留空则保留原有凭证)
|
||||
if (form.value.bearerToken && form.value.bearerToken.trim()) {
|
||||
data.bearerToken = form.value.bearerToken
|
||||
}
|
||||
}
|
||||
|
||||
if (form.value.region) {
|
||||
data.region = form.value.region
|
||||
}
|
||||
@@ -5734,7 +5981,7 @@ const filteredGroups = computed(() => {
|
||||
const loadGroups = async () => {
|
||||
loadingGroups.value = true
|
||||
try {
|
||||
const response = await httpApi.get('/admin/account-groups')
|
||||
const response = await httpApis.getAccountGroupsApi()
|
||||
groups.value = response.data || []
|
||||
} catch (error) {
|
||||
showToast('加载分组列表失败', 'error')
|
||||
@@ -6187,7 +6434,7 @@ watch(
|
||||
// 否则查找账户所属的分组
|
||||
const checkPromises = groups.value.map(async (group) => {
|
||||
try {
|
||||
const response = await httpApi.get(`/admin/account-groups/${group.id}/members`)
|
||||
const response = await httpApis.getAccountGroupMembersApi(group.id)
|
||||
const members = response.data || []
|
||||
if (members.some((m) => m.id === newAccount.id)) {
|
||||
foundGroupIds.push(group.id)
|
||||
@@ -6215,7 +6462,7 @@ watch(
|
||||
// 获取统一 User-Agent 信息
|
||||
const fetchUnifiedUserAgent = async () => {
|
||||
try {
|
||||
const response = await httpApi.get('/admin/claude-code-version')
|
||||
const response = await httpApis.getClaudeCodeVersionApi()
|
||||
if (response.success && response.userAgent) {
|
||||
unifiedUserAgent.value = response.userAgent
|
||||
} else {
|
||||
@@ -6231,7 +6478,7 @@ const fetchUnifiedUserAgent = async () => {
|
||||
const clearUnifiedCache = async () => {
|
||||
clearingCache.value = true
|
||||
try {
|
||||
const response = await httpApi.post('/admin/claude-code-version/clear')
|
||||
const response = await httpApis.clearClaudeCodeVersionApi()
|
||||
if (response.success) {
|
||||
unifiedUserAgent.value = ''
|
||||
showToast('统一User-Agent缓存已清除', 'success')
|
||||
|
||||
Reference in New Issue
Block a user