mirror of
https://github.com/Wei-Shaw/claude-relay-service.git
synced 2026-01-23 21:17:30 +00:00
chore: commit all changes
This commit is contained in:
@@ -1,26 +1,29 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Claude Relay Service - 管理后台</title>
|
||||
|
||||
|
||||
<!-- Google Fonts -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
||||
|
||||
|
||||
<!-- Font Awesome -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
|
||||
|
||||
<!-- 预连接到CDN域名,加速资源加载 -->
|
||||
<link rel="preconnect" href="https://cdn.jsdelivr.net" crossorigin>
|
||||
<link rel="preconnect" href="https://cdnjs.cloudflare.com" crossorigin>
|
||||
<link rel="dns-prefetch" href="https://cdn.jsdelivr.net">
|
||||
<link rel="dns-prefetch" href="https://cdnjs.cloudflare.com">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -481,3 +481,10 @@
|
||||
0 10px 15px -3px rgba(0, 0, 0, 0.1),
|
||||
0 4px 6px -2px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.fa-openai {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2NDAgNjQwIj48IS0tIUZvbnQgQXdlc29tZSBGcmVlIHY3LjAuMCBieSBAZm9udGF3ZXNvbWUgLSBodHRwczovL2ZvbnRhd2Vzb21lLmNvbSBMaWNlbnNlIC0gaHR0cHM6Ly9mb250YXdlc29tZS5jb20vbGljZW5zZS9mcmVlIENvcHlyaWdodCAyMDI1IEZvbnRpY29ucywgSW5jLi0tPjxwYXRoIGQ9Ik0yNjAuNCAyNDkuOHYtNDguNmMwLTQuMSAxLjUtNy4yIDUuMS05LjJsOTcuOC01Ni4zYzEzLjMtNy43IDI5LjItMTEuMyA0NS42LTExLjMgNjEuNCAwIDEwMC40IDQ3LjYgMTAwLjQgOTguMyAwIDMuNiAwIDcuNy0uNSAxMS44bC0xMDEuNS01OS40Yy02LjEtMy42LTEyLjMtMy42LTE4LjQgMGwtMTI4LjUgNzQuN3ptMjI4LjMgMTg5LjRWMzIzYzAtNy4yLTMuMS0xMi4zLTkuMi0xNS45TDM1MSAyMzIuNGw0Mi0yNC4xYzMuNi0yIDYuNy0yIDEwLjIgMGw5Ny44IDU2LjRjMjguMiAxNi40IDQ3LjEgNTEuMiA0Ny4xIDg1IDAgMzguOS0yMyA3NC44LTU5LjQgODkuNnpNMjMwLjIgMzM2LjhsLTQyLTI0LjZjLTMuNi0yLTUuMS01LjEtNS4xLTkuMlYxOTAuNGMwLTU0LjggNDItOTYuMyA5OC44LTk2LjMgMjEuNSAwIDQxLjUgNy4yIDU4LjQgMjBsLTEwMC45IDU4LjRjLTYuMSAzLjYtOS4yIDguNy05LjIgMTUuOXYxNDguNXptOTAuNCA1Mi4ybC02MC4yLTMzLjh2LTcxLjdsNjAuMi0zMy44IDYwLjIgMzMuOHY3MS43TDMyMC42IDM4OXptMzguNyAxNTUuN2MtMjEuNSAwLTQxLjUtNy4yLTU4LjQtMjBsMTAwLjktNTguNGM2LjEtMy42IDkuMi04LjcgOS4yLTE1LjlWMzAxLjlsNDIuNSAyNC42YzMuNiAyIDUuMSA1LjEgNS4xIDkuMnYxMTIuNmMwIDU0LjgtNDIuNSA5Ni4zLTk5LjMgOTYuM3pNMjM3LjggNDMwLjVsLTk3LjctNTYuM0MxMTEuOSAzNTcuOCA5MyAzMjMgOTMgMjg5LjJjMC0zOS40IDIzLjYtNzQuOCA1OS45LTg5LjZ2MTE2LjdjMCA3LjIgMy4xIDEyLjMgOS4yIDE1LjlsMTI4IDc0LjItNDIgMjQuMWMtMy42IDItNi43IDItMTAuMiAwem0tNS42IDg0Yy01Ny45IDAtMTAwLjQtNDMuNS0xMDAuNC05Ny4zIDAtNC4xLjUtOC4yIDEtMTIuM2wxMDAuOSA1OC40YzYuMSAzLjYgMTIuMyAzLjYgMTguNCAwbDEyOC41LTc0LjJ2NDguNmMwIDQuMS0xLjUgNy4yLTUuMSA5LjJsLTk3LjggNTYuM2MtMTMuMyA3LjctMjkuMiAxMS4zLTQ1LjYgMTEuM3ptMTI3IDYwLjljNjIgMCAxMTMuNy00NCAxMjUuNC0xMDIuNCA1Ny4zLTE0LjkgOTQuMi02OC42IDk0LjItMTIzLjQgMC0zNS44LTE1LjQtNzAuNy00My05NS43IDIuNi0xMC44IDQuMS0yMS41IDQuMS0zMi4zIDAtNzMuMi01OS40LTEyOC0xMjgtMTI4LTEzLjggMC0yNy4xIDItNDAuNCA2LjctMjMtMjIuNS01NC44LTM2LjktODkuNi0zNi45LTYyIDAtMTEzLjcgNDQtMTI1LjQgMTAyLjQtNTcuMyAxNC44LTk0LjIgNjguNi05NC4yIDEyMy40IDAgMzUuOCAxNS40IDcwLjcgNDMgOTUuNy0yLjYgMTAuOC00LjEgMjEuNS00LjEgMzIuMyAwIDczLjIgNTkuNCAxMjggMTI4IDEyOCAxMy44IDAgMjcuMS0yIDQwLjQtNi43IDIzIDIyLjUgNTQuOCAzNi45IDg5LjYgMzYuOXoiLz48L3N2Zz4=)
|
||||
no-repeat center/100%;
|
||||
}
|
||||
|
||||
@@ -77,6 +77,10 @@
|
||||
<input v-model="form.platform" class="mr-2" type="radio" value="gemini" />
|
||||
<span class="text-sm text-gray-700">Gemini</span>
|
||||
</label>
|
||||
<label class="flex cursor-pointer items-center">
|
||||
<input v-model="form.platform" class="mr-2" type="radio" value="openai" />
|
||||
<span class="text-sm text-gray-700">OpenAI</span>
|
||||
</label>
|
||||
<label class="flex cursor-pointer items-center">
|
||||
<input v-model="form.platform" class="mr-2" type="radio" value="bedrock" />
|
||||
<span class="text-sm text-gray-700">Bedrock</span>
|
||||
@@ -568,6 +572,10 @@
|
||||
请输入有效的 Gemini Access Token。如果您有 Refresh
|
||||
Token,建议也一并填写以支持自动刷新。
|
||||
</p>
|
||||
<p v-else-if="form.platform === 'openai'" class="mb-2 text-sm text-blue-800">
|
||||
请输入有效的 OpenAI Access Token。如果您有 Refresh
|
||||
Token,建议也一并填写以支持自动刷新。
|
||||
</p>
|
||||
<div class="mb-2 mt-2 rounded-lg border border-blue-300 bg-white/80 p-3">
|
||||
<p class="mb-1 text-sm font-medium text-blue-900">
|
||||
<i class="fas fa-folder-open mr-1" />
|
||||
@@ -587,6 +595,10 @@
|
||||
>
|
||||
文件中的凭证。
|
||||
</p>
|
||||
<p v-else-if="form.platform === 'openai'" class="text-xs text-blue-800">
|
||||
请从已登录 OpenAI 账户的机器上获取认证凭证, 或通过 OAuth 授权流程获取 Access
|
||||
Token。
|
||||
</p>
|
||||
</div>
|
||||
<p class="text-xs text-blue-600">
|
||||
💡 如果未填写 Refresh Token,Token 过期后需要手动更新。
|
||||
@@ -1580,11 +1592,16 @@ const handleOAuthSuccess = async (tokenInfo) => {
|
||||
if (form.value.projectId) {
|
||||
data.projectId = form.value.projectId
|
||||
}
|
||||
} else if (form.value.platform === 'openai') {
|
||||
data.openaiOauth = tokenInfo.tokens || tokenInfo
|
||||
data.accountInfo = tokenInfo.accountInfo
|
||||
}
|
||||
|
||||
let result
|
||||
if (form.value.platform === 'claude') {
|
||||
result = await accountsStore.createClaudeAccount(data)
|
||||
} else if (form.value.platform === 'openai') {
|
||||
result = await accountsStore.createOpenAIAccount(data)
|
||||
} else {
|
||||
result = await accountsStore.createGeminiAccount(data)
|
||||
}
|
||||
@@ -1734,6 +1751,8 @@ const createAccount = async () => {
|
||||
result = await accountsStore.createClaudeConsoleAccount(data)
|
||||
} else if (form.value.platform === 'bedrock') {
|
||||
result = await accountsStore.createBedrockAccount(data)
|
||||
} else if (form.value.platform === 'openai') {
|
||||
result = await accountsStore.createOpenAIAccount(data)
|
||||
} else {
|
||||
result = await accountsStore.createGeminiAccount(data)
|
||||
}
|
||||
@@ -1882,6 +1901,8 @@ const updateAccount = async () => {
|
||||
await accountsStore.updateClaudeConsoleAccount(props.account.id, data)
|
||||
} else if (props.account.platform === 'bedrock') {
|
||||
await accountsStore.updateBedrockAccount(props.account.id, data)
|
||||
} else if (props.account.platform === 'openai') {
|
||||
await accountsStore.updateOpenAIAccount(props.account.id, data)
|
||||
} else {
|
||||
await accountsStore.updateGeminiAccount(props.account.id, data)
|
||||
}
|
||||
|
||||
@@ -251,6 +251,131 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- OpenAI OAuth流程 -->
|
||||
<div v-else-if="platform === 'openai'">
|
||||
<div class="rounded-lg border border-orange-200 bg-orange-50 p-6">
|
||||
<div class="flex items-start gap-4">
|
||||
<div
|
||||
class="flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-lg bg-orange-500"
|
||||
>
|
||||
<i class="fas fa-brain text-white" />
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<h4 class="mb-3 font-semibold text-orange-900">OpenAI 账户授权</h4>
|
||||
<p class="mb-4 text-sm text-orange-800">请按照以下步骤完成 OpenAI 账户的授权:</p>
|
||||
|
||||
<div class="space-y-4">
|
||||
<!-- 步骤1: 生成授权链接 -->
|
||||
<div class="rounded-lg border border-orange-300 bg-white/80 p-4">
|
||||
<div class="flex items-start gap-3">
|
||||
<div
|
||||
class="flex h-6 w-6 flex-shrink-0 items-center justify-center rounded-full bg-orange-600 text-xs font-bold text-white"
|
||||
>
|
||||
1
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<p class="mb-2 font-medium text-orange-900">点击下方按钮生成授权链接</p>
|
||||
<button
|
||||
v-if="!authUrl"
|
||||
class="btn btn-primary px-4 py-2 text-sm"
|
||||
:disabled="loading"
|
||||
@click="generateAuthUrl"
|
||||
>
|
||||
<i v-if="!loading" class="fas fa-link mr-2" />
|
||||
<div v-else class="loading-spinner mr-2" />
|
||||
{{ loading ? '生成中...' : '生成授权链接' }}
|
||||
</button>
|
||||
<div v-else class="space-y-3">
|
||||
<div class="flex items-center gap-2">
|
||||
<input
|
||||
class="form-input flex-1 bg-gray-50 font-mono text-xs"
|
||||
readonly
|
||||
type="text"
|
||||
:value="authUrl"
|
||||
/>
|
||||
<button
|
||||
class="rounded-lg bg-gray-100 px-3 py-2 transition-colors hover:bg-gray-200"
|
||||
title="复制链接"
|
||||
@click="copyAuthUrl"
|
||||
>
|
||||
<i :class="copied ? 'fas fa-check text-green-500' : 'fas fa-copy'" />
|
||||
</button>
|
||||
</div>
|
||||
<button
|
||||
class="text-xs text-orange-600 hover:text-orange-700"
|
||||
@click="regenerateAuthUrl"
|
||||
>
|
||||
<i class="fas fa-sync-alt mr-1" />重新生成
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 步骤2: 访问链接并授权 -->
|
||||
<div class="rounded-lg border border-orange-300 bg-white/80 p-4">
|
||||
<div class="flex items-start gap-3">
|
||||
<div
|
||||
class="flex h-6 w-6 flex-shrink-0 items-center justify-center rounded-full bg-orange-600 text-xs font-bold text-white"
|
||||
>
|
||||
2
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<p class="mb-2 font-medium text-orange-900">在浏览器中打开链接并完成授权</p>
|
||||
<p class="mb-2 text-sm text-orange-700">
|
||||
请在新标签页中打开授权链接,登录您的 OpenAI 账户并授权。
|
||||
</p>
|
||||
<div class="rounded border border-yellow-300 bg-yellow-50 p-3">
|
||||
<p class="text-xs text-yellow-800">
|
||||
<i class="fas fa-exclamation-triangle mr-1" />
|
||||
<strong>注意:</strong
|
||||
>如果您设置了代理,请确保浏览器也使用相同的代理访问授权页面。
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 步骤3: 输入授权码 -->
|
||||
<div class="rounded-lg border border-orange-300 bg-white/80 p-4">
|
||||
<div class="flex items-start gap-3">
|
||||
<div
|
||||
class="flex h-6 w-6 flex-shrink-0 items-center justify-center rounded-full bg-orange-600 text-xs font-bold text-white"
|
||||
>
|
||||
3
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<p class="mb-2 font-medium text-orange-900">输入 Authorization Code</p>
|
||||
<p class="mb-3 text-sm text-orange-700">
|
||||
授权完成后,页面会显示一个
|
||||
<strong>Authorization Code</strong>,请将其复制并粘贴到下方输入框:
|
||||
</p>
|
||||
<div class="space-y-3">
|
||||
<div>
|
||||
<label class="mb-2 block text-sm font-semibold text-gray-700">
|
||||
<i class="fas fa-key mr-2 text-orange-500" />Authorization Code
|
||||
</label>
|
||||
<textarea
|
||||
v-model="authCode"
|
||||
class="form-input w-full resize-none font-mono text-sm"
|
||||
placeholder="粘贴从OpenAI页面获取的Authorization Code..."
|
||||
rows="3"
|
||||
/>
|
||||
</div>
|
||||
<p class="mt-2 text-xs text-gray-500">
|
||||
<i class="fas fa-info-circle mr-1" />
|
||||
请粘贴从OpenAI页面复制的Authorization Code
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex gap-3 pt-4">
|
||||
<button
|
||||
class="flex-1 rounded-xl bg-gray-100 px-6 py-3 font-semibold text-gray-700 transition-colors hover:bg-gray-200"
|
||||
@@ -339,8 +464,8 @@ watch(authCode, (newValue) => {
|
||||
console.error('Failed to parse URL:', error)
|
||||
showToast('链接格式错误,请检查是否为完整的 URL', 'error')
|
||||
}
|
||||
} else if (props.platform === 'gemini') {
|
||||
// Gemini 平台可能使用不同的回调URL
|
||||
} else if (props.platform === 'gemini' || props.platform === 'openai') {
|
||||
// Gemini 和 OpenAI 平台可能使用不同的回调URL
|
||||
// 尝试从任何URL中提取code参数
|
||||
try {
|
||||
const url = new URL(trimmedValue)
|
||||
@@ -385,6 +510,10 @@ const generateAuthUrl = async () => {
|
||||
const result = await accountsStore.generateGeminiAuthUrl(proxyConfig)
|
||||
authUrl.value = result.authUrl
|
||||
sessionId.value = result.sessionId
|
||||
} else if (props.platform === 'openai') {
|
||||
const result = await accountsStore.generateOpenAIAuthUrl(proxyConfig)
|
||||
authUrl.value = result.authUrl
|
||||
sessionId.value = result.sessionId
|
||||
}
|
||||
} catch (error) {
|
||||
showToast(error.message || '生成授权链接失败', 'error')
|
||||
@@ -445,6 +574,12 @@ const exchangeCode = async () => {
|
||||
code: authCode.value.trim(),
|
||||
sessionId: sessionId.value
|
||||
}
|
||||
} else if (props.platform === 'openai') {
|
||||
// OpenAI使用code和sessionId
|
||||
data = {
|
||||
code: authCode.value.trim(),
|
||||
sessionId: sessionId.value
|
||||
}
|
||||
}
|
||||
|
||||
// 添加代理配置(如果启用)
|
||||
@@ -463,6 +598,8 @@ const exchangeCode = async () => {
|
||||
tokenInfo = await accountsStore.exchangeClaudeCode(data)
|
||||
} else if (props.platform === 'gemini') {
|
||||
tokenInfo = await accountsStore.exchangeGeminiCode(data)
|
||||
} else if (props.platform === 'openai') {
|
||||
tokenInfo = await accountsStore.exchangeOpenAICode(data)
|
||||
}
|
||||
|
||||
emit('success', tokenInfo)
|
||||
|
||||
@@ -8,6 +8,7 @@ export const useAccountsStore = defineStore('accounts', () => {
|
||||
const claudeConsoleAccounts = ref([])
|
||||
const bedrockAccounts = ref([])
|
||||
const geminiAccounts = ref([])
|
||||
const openaiAccounts = ref([])
|
||||
const loading = ref(false)
|
||||
const error = ref(null)
|
||||
const sortBy = ref('')
|
||||
@@ -91,6 +92,25 @@ export const useAccountsStore = defineStore('accounts', () => {
|
||||
}
|
||||
}
|
||||
|
||||
// 获取OpenAI账户列表
|
||||
const fetchOpenAIAccounts = async () => {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
try {
|
||||
const response = await apiClient.get('/admin/openai-accounts')
|
||||
if (response.success) {
|
||||
openaiAccounts.value = response.data || []
|
||||
} else {
|
||||
throw new Error(response.message || '获取OpenAI账户失败')
|
||||
}
|
||||
} catch (err) {
|
||||
error.value = err.message
|
||||
throw err
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 获取所有账户
|
||||
const fetchAllAccounts = async () => {
|
||||
loading.value = true
|
||||
@@ -100,7 +120,8 @@ export const useAccountsStore = defineStore('accounts', () => {
|
||||
fetchClaudeAccounts(),
|
||||
fetchClaudeConsoleAccounts(),
|
||||
fetchBedrockAccounts(),
|
||||
fetchGeminiAccounts()
|
||||
fetchGeminiAccounts(),
|
||||
fetchOpenAIAccounts()
|
||||
])
|
||||
} catch (err) {
|
||||
error.value = err.message
|
||||
@@ -190,6 +211,26 @@ export const useAccountsStore = defineStore('accounts', () => {
|
||||
}
|
||||
}
|
||||
|
||||
// 创建OpenAI账户
|
||||
const createOpenAIAccount = async (data) => {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
try {
|
||||
const response = await apiClient.post('/admin/openai-accounts', data)
|
||||
if (response.success) {
|
||||
await fetchOpenAIAccounts()
|
||||
return response.data
|
||||
} else {
|
||||
throw new Error(response.message || '创建OpenAI账户失败')
|
||||
}
|
||||
} catch (err) {
|
||||
error.value = err.message
|
||||
throw err
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 更新Claude账户
|
||||
const updateClaudeAccount = async (id, data) => {
|
||||
loading.value = true
|
||||
@@ -270,6 +311,26 @@ export const useAccountsStore = defineStore('accounts', () => {
|
||||
}
|
||||
}
|
||||
|
||||
// 更新OpenAI账户
|
||||
const updateOpenAIAccount = async (id, data) => {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
try {
|
||||
const response = await apiClient.put(`/admin/openai-accounts/${id}`, data)
|
||||
if (response.success) {
|
||||
await fetchOpenAIAccounts()
|
||||
return response
|
||||
} else {
|
||||
throw new Error(response.message || '更新OpenAI账户失败')
|
||||
}
|
||||
} catch (err) {
|
||||
error.value = err.message
|
||||
throw err
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 切换账户状态
|
||||
const toggleAccount = async (platform, id) => {
|
||||
loading.value = true
|
||||
@@ -282,8 +343,10 @@ export const useAccountsStore = defineStore('accounts', () => {
|
||||
endpoint = `/admin/claude-console-accounts/${id}/toggle`
|
||||
} else if (platform === 'bedrock') {
|
||||
endpoint = `/admin/bedrock-accounts/${id}/toggle`
|
||||
} else {
|
||||
} else if (platform === 'gemini') {
|
||||
endpoint = `/admin/gemini-accounts/${id}/toggle`
|
||||
} else {
|
||||
endpoint = `/admin/openai-accounts/${id}/toggle`
|
||||
}
|
||||
|
||||
const response = await apiClient.put(endpoint)
|
||||
@@ -294,8 +357,10 @@ export const useAccountsStore = defineStore('accounts', () => {
|
||||
await fetchClaudeConsoleAccounts()
|
||||
} else if (platform === 'bedrock') {
|
||||
await fetchBedrockAccounts()
|
||||
} else {
|
||||
} else if (platform === 'gemini') {
|
||||
await fetchGeminiAccounts()
|
||||
} else {
|
||||
await fetchOpenAIAccounts()
|
||||
}
|
||||
return response
|
||||
} else {
|
||||
@@ -321,8 +386,10 @@ export const useAccountsStore = defineStore('accounts', () => {
|
||||
endpoint = `/admin/claude-console-accounts/${id}`
|
||||
} else if (platform === 'bedrock') {
|
||||
endpoint = `/admin/bedrock-accounts/${id}`
|
||||
} else {
|
||||
} else if (platform === 'gemini') {
|
||||
endpoint = `/admin/gemini-accounts/${id}`
|
||||
} else {
|
||||
endpoint = `/admin/openai-accounts/${id}`
|
||||
}
|
||||
|
||||
const response = await apiClient.delete(endpoint)
|
||||
@@ -333,8 +400,10 @@ export const useAccountsStore = defineStore('accounts', () => {
|
||||
await fetchClaudeConsoleAccounts()
|
||||
} else if (platform === 'bedrock') {
|
||||
await fetchBedrockAccounts()
|
||||
} else {
|
||||
} else if (platform === 'gemini') {
|
||||
await fetchGeminiAccounts()
|
||||
} else {
|
||||
await fetchOpenAIAccounts()
|
||||
}
|
||||
return response
|
||||
} else {
|
||||
@@ -464,6 +533,36 @@ export const useAccountsStore = defineStore('accounts', () => {
|
||||
}
|
||||
}
|
||||
|
||||
// 生成OpenAI OAuth URL
|
||||
const generateOpenAIAuthUrl = async (proxyConfig) => {
|
||||
try {
|
||||
const response = await apiClient.post('/admin/openai-accounts/generate-auth-url', proxyConfig)
|
||||
if (response.success) {
|
||||
return response.data // 返回整个对象,包含authUrl和sessionId
|
||||
} else {
|
||||
throw new Error(response.message || '生成授权URL失败')
|
||||
}
|
||||
} catch (err) {
|
||||
error.value = err.message
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
// 交换OpenAI OAuth Code
|
||||
const exchangeOpenAICode = async (data) => {
|
||||
try {
|
||||
const response = await apiClient.post('/admin/openai-accounts/exchange-code', data)
|
||||
if (response.success) {
|
||||
return response.data
|
||||
} else {
|
||||
throw new Error(response.message || '交换授权码失败')
|
||||
}
|
||||
} catch (err) {
|
||||
error.value = err.message
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
// 排序账户
|
||||
const sortAccounts = (field) => {
|
||||
if (sortBy.value === field) {
|
||||
@@ -480,6 +579,7 @@ export const useAccountsStore = defineStore('accounts', () => {
|
||||
claudeConsoleAccounts.value = []
|
||||
bedrockAccounts.value = []
|
||||
geminiAccounts.value = []
|
||||
openaiAccounts.value = []
|
||||
loading.value = false
|
||||
error.value = null
|
||||
sortBy.value = ''
|
||||
@@ -492,6 +592,7 @@ export const useAccountsStore = defineStore('accounts', () => {
|
||||
claudeConsoleAccounts,
|
||||
bedrockAccounts,
|
||||
geminiAccounts,
|
||||
openaiAccounts,
|
||||
loading,
|
||||
error,
|
||||
sortBy,
|
||||
@@ -502,15 +603,18 @@ export const useAccountsStore = defineStore('accounts', () => {
|
||||
fetchClaudeConsoleAccounts,
|
||||
fetchBedrockAccounts,
|
||||
fetchGeminiAccounts,
|
||||
fetchOpenAIAccounts,
|
||||
fetchAllAccounts,
|
||||
createClaudeAccount,
|
||||
createClaudeConsoleAccount,
|
||||
createBedrockAccount,
|
||||
createGeminiAccount,
|
||||
createOpenAIAccount,
|
||||
updateClaudeAccount,
|
||||
updateClaudeConsoleAccount,
|
||||
updateBedrockAccount,
|
||||
updateGeminiAccount,
|
||||
updateOpenAIAccount,
|
||||
toggleAccount,
|
||||
deleteAccount,
|
||||
refreshClaudeToken,
|
||||
@@ -520,6 +624,8 @@ export const useAccountsStore = defineStore('accounts', () => {
|
||||
exchangeClaudeSetupTokenCode,
|
||||
generateGeminiAuthUrl,
|
||||
exchangeGeminiCode,
|
||||
generateOpenAIAuthUrl,
|
||||
exchangeOpenAICode,
|
||||
sortAccounts,
|
||||
reset
|
||||
}
|
||||
|
||||
@@ -282,6 +282,15 @@
|
||||
<span class="mx-1 h-4 w-px bg-orange-300" />
|
||||
<span class="text-xs font-medium text-orange-700">AWS</span>
|
||||
</div>
|
||||
<div
|
||||
v-else-if="account.platform === 'openai'"
|
||||
class="flex items-center gap-1.5 rounded-lg border border-gray-700 bg-gray-100 bg-gradient-to-r from-gray-100 to-gray-100 px-2.5 py-1"
|
||||
>
|
||||
<div class="fa-openai" />
|
||||
<span class="text-xs font-semibold text-gray-950">OpenAi</span>
|
||||
<span class="mx-1 h-4 w-px bg-gray-400" />
|
||||
<span class="text-xs font-medium text-gray-950">Oauth</span>
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
class="flex items-center gap-1.5 rounded-lg border border-indigo-200 bg-gradient-to-r from-indigo-100 to-blue-100 px-2.5 py-1"
|
||||
@@ -813,6 +822,7 @@ const platformOptions = ref([
|
||||
{ value: 'claude', label: 'Claude', icon: 'fa-brain' },
|
||||
{ value: 'claude-console', label: 'Claude Console', icon: 'fa-terminal' },
|
||||
{ value: 'gemini', label: 'Gemini', icon: 'fa-robot' },
|
||||
{ value: 'openai', label: 'OpenAi', icon: 'fa-robot' },
|
||||
{ value: 'bedrock', label: 'Bedrock', icon: 'fab fa-aws' }
|
||||
])
|
||||
|
||||
@@ -899,7 +909,8 @@ const loadAccounts = async (forceReload = false) => {
|
||||
apiClient.get('/admin/claude-accounts', { params }),
|
||||
apiClient.get('/admin/claude-console-accounts', { params }),
|
||||
apiClient.get('/admin/bedrock-accounts', { params }),
|
||||
apiClient.get('/admin/gemini-accounts', { params })
|
||||
apiClient.get('/admin/gemini-accounts', { params }),
|
||||
apiClient.get('/admin/openai-accounts', { params })
|
||||
)
|
||||
} else {
|
||||
// 只请求指定平台,其他平台设为null占位
|
||||
@@ -945,7 +956,8 @@ const loadAccounts = async (forceReload = false) => {
|
||||
// 加载分组成员关系(需要在分组数据加载完成后)
|
||||
await loadGroupMembers(forceReload)
|
||||
|
||||
const [claudeData, claudeConsoleData, bedrockData, geminiData] = await Promise.all(requests)
|
||||
const [claudeData, claudeConsoleData, bedrockData, geminiData, openaiData] =
|
||||
await Promise.all(requests)
|
||||
|
||||
const allAccounts = []
|
||||
|
||||
@@ -991,6 +1003,13 @@ const loadAccounts = async (forceReload = false) => {
|
||||
})
|
||||
allAccounts.push(...geminiAccounts)
|
||||
}
|
||||
if (openaiData.success) {
|
||||
const openaiAccounts = (openaiData.data || []).map((acc) => {
|
||||
const groupInfo = accountGroupMap.value.get(acc.id) || null
|
||||
return { ...acc, platform: 'openai', boundApiKeysCount: 0, groupInfo }
|
||||
})
|
||||
allAccounts.push(...openaiAccounts)
|
||||
}
|
||||
|
||||
accounts.value = allAccounts
|
||||
} catch (error) {
|
||||
@@ -1214,6 +1233,8 @@ const deleteAccount = async (account) => {
|
||||
endpoint = `/admin/claude-console-accounts/${account.id}`
|
||||
} else if (account.platform === 'bedrock') {
|
||||
endpoint = `/admin/bedrock-accounts/${account.id}`
|
||||
} else if (account.platform === 'openai') {
|
||||
endpoint = `/admin/openai-accounts/${account.id}`
|
||||
} else {
|
||||
endpoint = `/admin/gemini-accounts/${account.id}`
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user