mirror of
https://github.com/Wei-Shaw/claude-relay-service.git
synced 2026-01-23 09:38:02 +00:00
feat: 新增支持Azure OpenAI账户
This commit is contained in:
@@ -105,6 +105,15 @@
|
||||
/>
|
||||
<span class="text-sm text-gray-700 dark:text-gray-300">OpenAI</span>
|
||||
</label>
|
||||
<label class="flex cursor-pointer items-center">
|
||||
<input
|
||||
v-model="form.platform"
|
||||
class="mr-2 text-blue-600 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700"
|
||||
type="radio"
|
||||
value="azure_openai"
|
||||
/>
|
||||
<span class="text-sm text-gray-700 dark:text-gray-300">Azure OpenAI</span>
|
||||
</label>
|
||||
<label class="flex cursor-pointer items-center">
|
||||
<input
|
||||
v-model="form.platform"
|
||||
@@ -118,7 +127,12 @@
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="!isEdit && form.platform !== 'claude-console' && form.platform !== 'bedrock'"
|
||||
v-if="
|
||||
!isEdit &&
|
||||
form.platform !== 'claude-console' &&
|
||||
form.platform !== 'bedrock' &&
|
||||
form.platform !== 'azure_openai'
|
||||
"
|
||||
>
|
||||
<label class="mb-3 block text-sm font-semibold text-gray-700 dark:text-gray-300"
|
||||
>添加方式</label
|
||||
@@ -429,7 +443,111 @@
|
||||
用于快速响应的轻量级模型,留空将使用系统默认
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Azure OpenAI 特定字段 -->
|
||||
<div v-if="form.platform === 'azure_openai' && !isEdit" class="space-y-4">
|
||||
<div>
|
||||
<label class="mb-3 block text-sm font-semibold text-gray-700 dark:text-gray-300"
|
||||
>Azure Endpoint *</label
|
||||
>
|
||||
<input
|
||||
v-model="form.azureEndpoint"
|
||||
class="form-input w-full dark:border-gray-600 dark:bg-gray-700 dark:text-gray-200 dark:placeholder-gray-400"
|
||||
:class="{ 'border-red-500': errors.azureEndpoint }"
|
||||
placeholder="https://your-resource.openai.azure.com"
|
||||
required
|
||||
type="url"
|
||||
/>
|
||||
<p v-if="errors.azureEndpoint" class="mt-1 text-xs text-red-500">
|
||||
{{ errors.azureEndpoint }}
|
||||
</p>
|
||||
<p class="mt-1 text-xs text-gray-500 dark:text-gray-400">
|
||||
Azure OpenAI 资源的终结点 URL,格式:https://your-resource.openai.azure.com
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="mb-3 block text-sm font-semibold text-gray-700 dark:text-gray-300"
|
||||
>API 版本</label
|
||||
>
|
||||
<input
|
||||
v-model="form.apiVersion"
|
||||
class="form-input w-full dark:border-gray-600 dark:bg-gray-700 dark:text-gray-200 dark:placeholder-gray-400"
|
||||
placeholder="2024-02-01"
|
||||
type="text"
|
||||
/>
|
||||
<p class="mt-1 text-xs text-gray-500 dark:text-gray-400">
|
||||
Azure OpenAI API 版本,默认使用最新稳定版本 2024-02-01
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="mb-3 block text-sm font-semibold text-gray-700 dark:text-gray-300"
|
||||
>部署名称 *</label
|
||||
>
|
||||
<input
|
||||
v-model="form.deploymentName"
|
||||
class="form-input w-full dark:border-gray-600 dark:bg-gray-700 dark:text-gray-200 dark:placeholder-gray-400"
|
||||
:class="{ 'border-red-500': errors.deploymentName }"
|
||||
placeholder="gpt-4"
|
||||
required
|
||||
type="text"
|
||||
/>
|
||||
<p v-if="errors.deploymentName" class="mt-1 text-xs text-red-500">
|
||||
{{ errors.deploymentName }}
|
||||
</p>
|
||||
<p class="mt-1 text-xs text-gray-500 dark:text-gray-400">
|
||||
在 Azure OpenAI Studio 中创建的部署名称
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="mb-3 block text-sm font-semibold text-gray-700 dark:text-gray-300"
|
||||
>API Key *</label
|
||||
>
|
||||
<input
|
||||
v-model="form.apiKey"
|
||||
class="form-input w-full dark:border-gray-600 dark:bg-gray-700 dark:text-gray-200 dark:placeholder-gray-400"
|
||||
:class="{ 'border-red-500': errors.apiKey }"
|
||||
placeholder="请输入 Azure OpenAI API Key"
|
||||
required
|
||||
type="password"
|
||||
/>
|
||||
<p v-if="errors.apiKey" class="mt-1 text-xs text-red-500">
|
||||
{{ errors.apiKey }}
|
||||
</p>
|
||||
<p class="mt-1 text-xs text-gray-500 dark:text-gray-400">
|
||||
从 Azure 门户获取的 API 密钥
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="mb-3 block text-sm font-semibold text-gray-700 dark:text-gray-300"
|
||||
>支持的模型</label
|
||||
>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<label
|
||||
v-for="model in ['gpt-4', 'gpt-4-turbo', 'gpt-35-turbo', 'gpt-35-turbo-16k']"
|
||||
:key="model"
|
||||
class="flex cursor-pointer items-center"
|
||||
>
|
||||
<input
|
||||
v-model="form.supportedModels"
|
||||
class="mr-2 text-blue-600 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700"
|
||||
type="checkbox"
|
||||
:value="model"
|
||||
/>
|
||||
<span class="text-sm text-gray-700 dark:text-gray-300">{{ model }}</span>
|
||||
</label>
|
||||
</div>
|
||||
<p class="mt-1 text-xs text-gray-500 dark:text-gray-400">
|
||||
选择此部署支持的模型类型
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="form.platform === 'bedrock' && !isEdit">
|
||||
<div>
|
||||
<label class="mb-3 block text-sm font-semibold text-gray-700 dark:text-gray-300"
|
||||
>限流机制</label
|
||||
|
||||
@@ -9,6 +9,7 @@ export const useAccountsStore = defineStore('accounts', () => {
|
||||
const bedrockAccounts = ref([])
|
||||
const geminiAccounts = ref([])
|
||||
const openaiAccounts = ref([])
|
||||
const azureOpenaiAccounts = ref([])
|
||||
const loading = ref(false)
|
||||
const error = ref(null)
|
||||
const sortBy = ref('')
|
||||
@@ -111,6 +112,25 @@ export const useAccountsStore = defineStore('accounts', () => {
|
||||
}
|
||||
}
|
||||
|
||||
// 获取Azure OpenAI账户列表
|
||||
const fetchAzureOpenAIAccounts = async () => {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
try {
|
||||
const response = await apiClient.get('/admin/azure-openai-accounts')
|
||||
if (response.success) {
|
||||
azureOpenaiAccounts.value = response.data || []
|
||||
} else {
|
||||
throw new Error(response.message || '获取Azure OpenAI账户失败')
|
||||
}
|
||||
} catch (err) {
|
||||
error.value = err.message
|
||||
throw err
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 获取所有账户
|
||||
const fetchAllAccounts = async () => {
|
||||
loading.value = true
|
||||
@@ -121,7 +141,8 @@ export const useAccountsStore = defineStore('accounts', () => {
|
||||
fetchClaudeConsoleAccounts(),
|
||||
fetchBedrockAccounts(),
|
||||
fetchGeminiAccounts(),
|
||||
fetchOpenAIAccounts()
|
||||
fetchOpenAIAccounts(),
|
||||
fetchAzureOpenAIAccounts()
|
||||
])
|
||||
} catch (err) {
|
||||
error.value = err.message
|
||||
@@ -231,6 +252,26 @@ export const useAccountsStore = defineStore('accounts', () => {
|
||||
}
|
||||
}
|
||||
|
||||
// 创建Azure OpenAI账户
|
||||
const createAzureOpenAIAccount = async (data) => {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
try {
|
||||
const response = await apiClient.post('/admin/azure-openai-accounts', data)
|
||||
if (response.success) {
|
||||
await fetchAzureOpenAIAccounts()
|
||||
return response.data
|
||||
} else {
|
||||
throw new Error(response.message || '创建Azure OpenAI账户失败')
|
||||
}
|
||||
} catch (err) {
|
||||
error.value = err.message
|
||||
throw err
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 更新Claude账户
|
||||
const updateClaudeAccount = async (id, data) => {
|
||||
loading.value = true
|
||||
@@ -331,6 +372,26 @@ export const useAccountsStore = defineStore('accounts', () => {
|
||||
}
|
||||
}
|
||||
|
||||
// 更新Azure OpenAI账户
|
||||
const updateAzureOpenAIAccount = async (id, data) => {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
try {
|
||||
const response = await apiClient.put(`/admin/azure-openai-accounts/${id}`, data)
|
||||
if (response.success) {
|
||||
await fetchAzureOpenAIAccounts()
|
||||
return response
|
||||
} else {
|
||||
throw new Error(response.message || '更新Azure OpenAI账户失败')
|
||||
}
|
||||
} catch (err) {
|
||||
error.value = err.message
|
||||
throw err
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 切换账户状态
|
||||
const toggleAccount = async (platform, id) => {
|
||||
loading.value = true
|
||||
@@ -345,6 +406,10 @@ export const useAccountsStore = defineStore('accounts', () => {
|
||||
endpoint = `/admin/bedrock-accounts/${id}/toggle`
|
||||
} else if (platform === 'gemini') {
|
||||
endpoint = `/admin/gemini-accounts/${id}/toggle`
|
||||
} else if (platform === 'openai') {
|
||||
endpoint = `/admin/openai-accounts/${id}/toggle`
|
||||
} else if (platform === 'azure_openai') {
|
||||
endpoint = `/admin/azure-openai-accounts/${id}/toggle`
|
||||
} else {
|
||||
endpoint = `/admin/openai-accounts/${id}/toggle`
|
||||
}
|
||||
@@ -359,6 +424,10 @@ export const useAccountsStore = defineStore('accounts', () => {
|
||||
await fetchBedrockAccounts()
|
||||
} else if (platform === 'gemini') {
|
||||
await fetchGeminiAccounts()
|
||||
} else if (platform === 'openai') {
|
||||
await fetchOpenAIAccounts()
|
||||
} else if (platform === 'azure_openai') {
|
||||
await fetchAzureOpenAIAccounts()
|
||||
} else {
|
||||
await fetchOpenAIAccounts()
|
||||
}
|
||||
@@ -388,6 +457,10 @@ export const useAccountsStore = defineStore('accounts', () => {
|
||||
endpoint = `/admin/bedrock-accounts/${id}`
|
||||
} else if (platform === 'gemini') {
|
||||
endpoint = `/admin/gemini-accounts/${id}`
|
||||
} else if (platform === 'openai') {
|
||||
endpoint = `/admin/openai-accounts/${id}`
|
||||
} else if (platform === 'azure_openai') {
|
||||
endpoint = `/admin/azure-openai-accounts/${id}`
|
||||
} else {
|
||||
endpoint = `/admin/openai-accounts/${id}`
|
||||
}
|
||||
@@ -402,6 +475,10 @@ export const useAccountsStore = defineStore('accounts', () => {
|
||||
await fetchBedrockAccounts()
|
||||
} else if (platform === 'gemini') {
|
||||
await fetchGeminiAccounts()
|
||||
} else if (platform === 'openai') {
|
||||
await fetchOpenAIAccounts()
|
||||
} else if (platform === 'azure_openai') {
|
||||
await fetchAzureOpenAIAccounts()
|
||||
} else {
|
||||
await fetchOpenAIAccounts()
|
||||
}
|
||||
@@ -580,6 +657,7 @@ export const useAccountsStore = defineStore('accounts', () => {
|
||||
bedrockAccounts.value = []
|
||||
geminiAccounts.value = []
|
||||
openaiAccounts.value = []
|
||||
azureOpenaiAccounts.value = []
|
||||
loading.value = false
|
||||
error.value = null
|
||||
sortBy.value = ''
|
||||
@@ -593,6 +671,7 @@ export const useAccountsStore = defineStore('accounts', () => {
|
||||
bedrockAccounts,
|
||||
geminiAccounts,
|
||||
openaiAccounts,
|
||||
azureOpenaiAccounts,
|
||||
loading,
|
||||
error,
|
||||
sortBy,
|
||||
@@ -604,17 +683,20 @@ export const useAccountsStore = defineStore('accounts', () => {
|
||||
fetchBedrockAccounts,
|
||||
fetchGeminiAccounts,
|
||||
fetchOpenAIAccounts,
|
||||
fetchAzureOpenAIAccounts,
|
||||
fetchAllAccounts,
|
||||
createClaudeAccount,
|
||||
createClaudeConsoleAccount,
|
||||
createBedrockAccount,
|
||||
createGeminiAccount,
|
||||
createOpenAIAccount,
|
||||
createAzureOpenAIAccount,
|
||||
updateClaudeAccount,
|
||||
updateClaudeConsoleAccount,
|
||||
updateBedrockAccount,
|
||||
updateGeminiAccount,
|
||||
updateOpenAIAccount,
|
||||
updateAzureOpenAIAccount,
|
||||
toggleAccount,
|
||||
deleteAccount,
|
||||
refreshClaudeToken,
|
||||
|
||||
@@ -19,6 +19,8 @@ export const useDashboardStore = defineStore('dashboard', () => {
|
||||
claude: { total: 0, normal: 0, abnormal: 0, paused: 0, rateLimited: 0 },
|
||||
'claude-console': { total: 0, normal: 0, abnormal: 0, paused: 0, rateLimited: 0 },
|
||||
gemini: { total: 0, normal: 0, abnormal: 0, paused: 0, rateLimited: 0 },
|
||||
openai: { total: 0, normal: 0, abnormal: 0, paused: 0, rateLimited: 0 },
|
||||
azure_openai: { total: 0, normal: 0, abnormal: 0, paused: 0, rateLimited: 0 },
|
||||
bedrock: { total: 0, normal: 0, abnormal: 0, paused: 0, rateLimited: 0 }
|
||||
},
|
||||
todayRequests: 0,
|
||||
@@ -174,6 +176,8 @@ export const useDashboardStore = defineStore('dashboard', () => {
|
||||
claude: { total: 0, normal: 0, abnormal: 0, paused: 0, rateLimited: 0 },
|
||||
'claude-console': { total: 0, normal: 0, abnormal: 0, paused: 0, rateLimited: 0 },
|
||||
gemini: { total: 0, normal: 0, abnormal: 0, paused: 0, rateLimited: 0 },
|
||||
openai: { total: 0, normal: 0, abnormal: 0, paused: 0, rateLimited: 0 },
|
||||
azure_openai: { total: 0, normal: 0, abnormal: 0, paused: 0, rateLimited: 0 },
|
||||
bedrock: { total: 0, normal: 0, abnormal: 0, paused: 0, rateLimited: 0 }
|
||||
},
|
||||
todayRequests: recentActivity.requestsToday || 0,
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
账户管理
|
||||
</h3>
|
||||
<p class="text-sm text-gray-600 dark:text-gray-400 sm:text-base">
|
||||
管理您的 Claude 和 Gemini 账户及代理配置
|
||||
管理您的 Claude、Gemini、OpenAI 和 Azure OpenAI 账户及代理配置
|
||||
</p>
|
||||
</div>
|
||||
<div class="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
|
||||
@@ -298,6 +298,19 @@
|
||||
<span class="mx-1 h-4 w-px bg-gray-400" />
|
||||
<span class="text-xs font-medium text-gray-950">{{ getOpenAIAuthType() }}</span>
|
||||
</div>
|
||||
<div
|
||||
v-else-if="account.platform === 'azure_openai'"
|
||||
class="flex items-center gap-1.5 rounded-lg border border-blue-200 bg-gradient-to-r from-blue-100 to-cyan-100 px-2.5 py-1 dark:border-blue-700 dark:from-blue-900/20 dark:to-cyan-900/20"
|
||||
>
|
||||
<i class="fab fa-microsoft text-xs text-blue-700 dark:text-blue-400" />
|
||||
<span class="text-xs font-semibold text-blue-800 dark:text-blue-300"
|
||||
>Azure OpenAI</span
|
||||
>
|
||||
<span class="mx-1 h-4 w-px bg-blue-300 dark:bg-blue-600" />
|
||||
<span class="text-xs font-medium text-blue-700 dark:text-blue-400"
|
||||
>API Key</span
|
||||
>
|
||||
</div>
|
||||
<div
|
||||
v-else-if="account.platform === 'claude' || account.platform === 'claude-oauth'"
|
||||
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"
|
||||
@@ -581,7 +594,11 @@
|
||||
? 'bg-gradient-to-br from-purple-500 to-purple-600'
|
||||
: account.platform === 'bedrock'
|
||||
? 'bg-gradient-to-br from-orange-500 to-red-600'
|
||||
: 'bg-gradient-to-br from-blue-500 to-blue-600'
|
||||
: account.platform === 'azure_openai'
|
||||
? 'bg-gradient-to-br from-blue-500 to-cyan-600'
|
||||
: account.platform === 'openai'
|
||||
? 'bg-gradient-to-br from-gray-600 to-gray-700'
|
||||
: 'bg-gradient-to-br from-blue-500 to-blue-600'
|
||||
]"
|
||||
>
|
||||
<i
|
||||
@@ -591,7 +608,11 @@
|
||||
? 'fas fa-brain'
|
||||
: account.platform === 'bedrock'
|
||||
? 'fab fa-aws'
|
||||
: 'fas fa-robot'
|
||||
: account.platform === 'azure_openai'
|
||||
? 'fab fa-microsoft'
|
||||
: account.platform === 'openai'
|
||||
? 'fas fa-openai'
|
||||
: 'fas fa-robot'
|
||||
]"
|
||||
/>
|
||||
</div>
|
||||
@@ -820,6 +841,7 @@ const platformOptions = ref([
|
||||
{ value: 'claude-console', label: 'Claude Console', icon: 'fa-terminal' },
|
||||
{ value: 'gemini', label: 'Gemini', icon: 'fa-google' },
|
||||
{ value: 'openai', label: 'OpenAi', icon: 'fa-openai' },
|
||||
{ value: 'azure_openai', label: 'Azure OpenAI', icon: 'fab fa-microsoft' },
|
||||
{ value: 'bedrock', label: 'Bedrock', icon: 'fab fa-aws' }
|
||||
])
|
||||
|
||||
@@ -912,7 +934,8 @@ const loadAccounts = async (forceReload = false) => {
|
||||
apiClient.get('/admin/claude-console-accounts', { params }),
|
||||
apiClient.get('/admin/bedrock-accounts', { params }),
|
||||
apiClient.get('/admin/gemini-accounts', { params }),
|
||||
apiClient.get('/admin/openai-accounts', { params })
|
||||
apiClient.get('/admin/openai-accounts', { params }),
|
||||
apiClient.get('/admin/azure-openai-accounts', { params })
|
||||
)
|
||||
} else {
|
||||
// 只请求指定平台,其他平台设为null占位
|
||||
@@ -958,7 +981,7 @@ const loadAccounts = async (forceReload = false) => {
|
||||
// 加载分组成员关系(需要在分组数据加载完成后)
|
||||
await loadGroupMembers(forceReload)
|
||||
|
||||
const [claudeData, claudeConsoleData, bedrockData, geminiData, openaiData] =
|
||||
const [claudeData, claudeConsoleData, bedrockData, geminiData, openaiData, azureOpenaiData] =
|
||||
await Promise.all(requests)
|
||||
|
||||
const allAccounts = []
|
||||
@@ -1016,6 +1039,17 @@ const loadAccounts = async (forceReload = false) => {
|
||||
})
|
||||
allAccounts.push(...openaiAccounts)
|
||||
}
|
||||
if (azureOpenaiData && azureOpenaiData.success) {
|
||||
const azureOpenaiAccounts = (azureOpenaiData.data || []).map((acc) => {
|
||||
// 计算每个Azure OpenAI账户绑定的API Key数量
|
||||
const boundApiKeysCount = apiKeys.value.filter(
|
||||
(key) => key.azureOpenaiAccountId === acc.id
|
||||
).length
|
||||
const groupInfo = accountGroupMap.value.get(acc.id) || null
|
||||
return { ...acc, platform: 'azure_openai', boundApiKeysCount, groupInfo }
|
||||
})
|
||||
allAccounts.push(...azureOpenaiAccounts)
|
||||
}
|
||||
|
||||
accounts.value = allAccounts
|
||||
} catch (error) {
|
||||
@@ -1244,6 +1278,8 @@ const deleteAccount = async (account) => {
|
||||
endpoint = `/admin/bedrock-accounts/${account.id}`
|
||||
} else if (account.platform === 'openai') {
|
||||
endpoint = `/admin/openai-accounts/${account.id}`
|
||||
} else if (account.platform === 'azure_openai') {
|
||||
endpoint = `/admin/azure-openai-accounts/${account.id}`
|
||||
} else {
|
||||
endpoint = `/admin/gemini-accounts/${account.id}`
|
||||
}
|
||||
@@ -1316,6 +1352,8 @@ const toggleSchedulable = async (account) => {
|
||||
endpoint = `/admin/gemini-accounts/${account.id}/toggle-schedulable`
|
||||
} else if (account.platform === 'openai') {
|
||||
endpoint = `/admin/openai-accounts/${account.id}/toggle-schedulable`
|
||||
} else if (account.platform === 'azure_openai') {
|
||||
endpoint = `/admin/azure-openai-accounts/${account.id}/toggle-schedulable`
|
||||
} else {
|
||||
showToast('该账户类型暂不支持调度控制', 'warning')
|
||||
return
|
||||
|
||||
@@ -105,6 +105,20 @@
|
||||
dashboardData.accountsByPlatform.openai.total
|
||||
}}</span>
|
||||
</div>
|
||||
<!-- Azure OpenAI账户 -->
|
||||
<div
|
||||
v-if="
|
||||
dashboardData.accountsByPlatform.azure_openai &&
|
||||
dashboardData.accountsByPlatform.azure_openai.total > 0
|
||||
"
|
||||
class="inline-flex items-center gap-0.5"
|
||||
:title="`Azure OpenAI: ${dashboardData.accountsByPlatform.azure_openai.total} 个 (正常: ${dashboardData.accountsByPlatform.azure_openai.normal})`"
|
||||
>
|
||||
<i class="fab fa-microsoft text-xs text-blue-600" />
|
||||
<span class="text-xs font-medium text-gray-700 dark:text-gray-300">{{
|
||||
dashboardData.accountsByPlatform.azure_openai.total
|
||||
}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p class="mt-1 text-xs text-gray-500 dark:text-gray-400">
|
||||
|
||||
Reference in New Issue
Block a user