mirror of
https://github.com/Wei-Shaw/claude-relay-service.git
synced 2026-01-23 09:38:02 +00:00
feat: 完全移除 API Key 图标功能
彻底删除 API Key 图标功能的所有相关代码: 前端改动: - 删除 IconPicker.vue 组件文件 - 移除 ApiKeysView.vue 中的图标显示和 updateApiKeyIcon 方法 - 清理 CreateApiKeyModal.vue 中的图标选择器 - 清理 EditApiKeyModal.vue 中的图标选择器 - 移除所有 IconPicker 组件的引用 后端改动: - 从 apiKeyService.js 中移除 icon 字段更新支持 - 从 admin.js 路由中移除 icon 参数处理和验证逻辑 - 清理创建和更新 API Key 时的 icon 参数 此改动简化了 API Key 管理界面,移除了不必要的图标功能。 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -534,8 +534,7 @@ router.post('/api-keys', authenticateAdmin, async (req, res) => {
|
|||||||
weeklyOpusCostLimit,
|
weeklyOpusCostLimit,
|
||||||
tags,
|
tags,
|
||||||
activationDays, // 新增:激活后有效天数
|
activationDays, // 新增:激活后有效天数
|
||||||
expirationMode, // 新增:过期模式
|
expirationMode // 新增:过期模式
|
||||||
icon // 新增:图标(base64编码)
|
|
||||||
} = req.body
|
} = req.body
|
||||||
|
|
||||||
// 输入验证
|
// 输入验证
|
||||||
@@ -989,8 +988,7 @@ router.put('/api-keys/:keyId', authenticateAdmin, async (req, res) => {
|
|||||||
dailyCostLimit,
|
dailyCostLimit,
|
||||||
weeklyOpusCostLimit,
|
weeklyOpusCostLimit,
|
||||||
tags,
|
tags,
|
||||||
ownerId, // 新增:所有者ID字段
|
ownerId // 新增:所有者ID字段
|
||||||
icon // 新增:图标(base64编码)
|
|
||||||
} = req.body
|
} = req.body
|
||||||
|
|
||||||
// 只允许更新指定字段
|
// 只允许更新指定字段
|
||||||
@@ -1164,19 +1162,6 @@ router.put('/api-keys/:keyId', authenticateAdmin, async (req, res) => {
|
|||||||
updates.tags = tags
|
updates.tags = tags
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理图标
|
|
||||||
if (icon !== undefined) {
|
|
||||||
// icon 可以是空字符串(清除图标)或 base64 编码的字符串
|
|
||||||
if (icon !== '' && typeof icon !== 'string') {
|
|
||||||
return res.status(400).json({ error: 'Icon must be a string' })
|
|
||||||
}
|
|
||||||
// 简单验证 base64 格式(如果不为空)
|
|
||||||
if (icon && !icon.startsWith('data:image/')) {
|
|
||||||
return res.status(400).json({ error: 'Icon must be a valid base64 image' })
|
|
||||||
}
|
|
||||||
updates.icon = icon
|
|
||||||
}
|
|
||||||
|
|
||||||
// 处理活跃/禁用状态状态, 放在过期处理后,以确保后续增加禁用key功能
|
// 处理活跃/禁用状态状态, 放在过期处理后,以确保后续增加禁用key功能
|
||||||
if (isActive !== undefined) {
|
if (isActive !== undefined) {
|
||||||
if (typeof isActive !== 'boolean') {
|
if (typeof isActive !== 'boolean') {
|
||||||
|
|||||||
@@ -412,8 +412,7 @@ class ApiKeyService {
|
|||||||
'tags',
|
'tags',
|
||||||
'userId', // 新增:用户ID(所有者变更)
|
'userId', // 新增:用户ID(所有者变更)
|
||||||
'userUsername', // 新增:用户名(所有者变更)
|
'userUsername', // 新增:用户名(所有者变更)
|
||||||
'createdBy', // 新增:创建者(所有者变更)
|
'createdBy' // 新增:创建者(所有者变更)
|
||||||
'icon' // 新增:图标(base64编码)
|
|
||||||
]
|
]
|
||||||
const updatedData = { ...keyData }
|
const updatedData = { ...keyData }
|
||||||
|
|
||||||
|
|||||||
@@ -110,9 +110,7 @@
|
|||||||
class="mb-1.5 block text-xs font-semibold text-gray-700 dark:text-gray-300 sm:mb-2 sm:text-sm"
|
class="mb-1.5 block text-xs font-semibold text-gray-700 dark:text-gray-300 sm:mb-2 sm:text-sm"
|
||||||
>名称 <span class="text-red-500">*</span></label
|
>名称 <span class="text-red-500">*</span></label
|
||||||
>
|
>
|
||||||
<div class="flex items-center gap-2">
|
<div>
|
||||||
<!-- 图标选择器 -->
|
|
||||||
<IconPicker v-model="form.icon" size="medium" />
|
|
||||||
<input
|
<input
|
||||||
v-model="form.name"
|
v-model="form.name"
|
||||||
class="form-input flex-1 border-gray-300 text-sm dark:border-gray-600 dark:bg-gray-700 dark:text-gray-200 dark:placeholder-gray-400"
|
class="form-input flex-1 border-gray-300 text-sm dark:border-gray-600 dark:bg-gray-700 dark:text-gray-200 dark:placeholder-gray-400"
|
||||||
@@ -811,7 +809,6 @@ import { useClientsStore } from '@/stores/clients'
|
|||||||
import { useApiKeysStore } from '@/stores/apiKeys'
|
import { useApiKeysStore } from '@/stores/apiKeys'
|
||||||
import { apiClient } from '@/config/api'
|
import { apiClient } from '@/config/api'
|
||||||
import AccountSelector from '@/components/common/AccountSelector.vue'
|
import AccountSelector from '@/components/common/AccountSelector.vue'
|
||||||
import IconPicker from '@/components/common/IconPicker.vue'
|
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
accounts: {
|
accounts: {
|
||||||
@@ -858,7 +855,6 @@ const form = reactive({
|
|||||||
createType: 'single',
|
createType: 'single',
|
||||||
batchCount: 10,
|
batchCount: 10,
|
||||||
name: '',
|
name: '',
|
||||||
icon: '',
|
|
||||||
description: '',
|
description: '',
|
||||||
rateLimitWindow: '',
|
rateLimitWindow: '',
|
||||||
rateLimitRequests: '',
|
rateLimitRequests: '',
|
||||||
@@ -1204,8 +1200,7 @@ const createApiKey = async () => {
|
|||||||
// 单个创建
|
// 单个创建
|
||||||
const data = {
|
const data = {
|
||||||
...baseData,
|
...baseData,
|
||||||
name: form.name,
|
name: form.name
|
||||||
icon: form.icon || ''
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await apiClient.post('/admin/api-keys', data)
|
const result = await apiClient.post('/admin/api-keys', data)
|
||||||
@@ -1223,8 +1218,7 @@ const createApiKey = async () => {
|
|||||||
...baseData,
|
...baseData,
|
||||||
createType: 'batch',
|
createType: 'batch',
|
||||||
baseName: form.name,
|
baseName: form.name,
|
||||||
count: form.batchCount,
|
count: form.batchCount
|
||||||
icon: form.icon || ''
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await apiClient.post('/admin/api-keys/batch', data)
|
const result = await apiClient.post('/admin/api-keys/batch', data)
|
||||||
|
|||||||
@@ -32,9 +32,7 @@
|
|||||||
class="mb-1.5 block text-xs font-semibold text-gray-700 dark:text-gray-300 sm:mb-3 sm:text-sm"
|
class="mb-1.5 block text-xs font-semibold text-gray-700 dark:text-gray-300 sm:mb-3 sm:text-sm"
|
||||||
>名称</label
|
>名称</label
|
||||||
>
|
>
|
||||||
<div class="flex items-center gap-2">
|
<div>
|
||||||
<!-- 图标选择器 -->
|
|
||||||
<IconPicker v-model="form.icon" size="medium" />
|
|
||||||
<input
|
<input
|
||||||
v-model="form.name"
|
v-model="form.name"
|
||||||
class="form-input flex-1 border-gray-300 text-sm dark:border-gray-600 dark:bg-gray-700 dark:text-gray-200 dark:placeholder-gray-400"
|
class="form-input flex-1 border-gray-300 text-sm dark:border-gray-600 dark:bg-gray-700 dark:text-gray-200 dark:placeholder-gray-400"
|
||||||
@@ -662,7 +660,6 @@ import { useClientsStore } from '@/stores/clients'
|
|||||||
import { useApiKeysStore } from '@/stores/apiKeys'
|
import { useApiKeysStore } from '@/stores/apiKeys'
|
||||||
import { apiClient } from '@/config/api'
|
import { apiClient } from '@/config/api'
|
||||||
import AccountSelector from '@/components/common/AccountSelector.vue'
|
import AccountSelector from '@/components/common/AccountSelector.vue'
|
||||||
import IconPicker from '@/components/common/IconPicker.vue'
|
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
apiKey: {
|
apiKey: {
|
||||||
@@ -710,7 +707,6 @@ const unselectedTags = computed(() => {
|
|||||||
// 表单数据
|
// 表单数据
|
||||||
const form = reactive({
|
const form = reactive({
|
||||||
name: '',
|
name: '',
|
||||||
icon: '',
|
|
||||||
tokenLimit: '', // 保留用于检测历史数据
|
tokenLimit: '', // 保留用于检测历史数据
|
||||||
rateLimitWindow: '',
|
rateLimitWindow: '',
|
||||||
rateLimitRequests: '',
|
rateLimitRequests: '',
|
||||||
@@ -809,7 +805,6 @@ const updateApiKey = async () => {
|
|||||||
// 准备提交的数据
|
// 准备提交的数据
|
||||||
const data = {
|
const data = {
|
||||||
name: form.name, // 添加名称字段
|
name: form.name, // 添加名称字段
|
||||||
icon: form.icon || '', // 添加图标字段
|
|
||||||
tokenLimit: 0, // 清除历史token限制
|
tokenLimit: 0, // 清除历史token限制
|
||||||
rateLimitWindow:
|
rateLimitWindow:
|
||||||
form.rateLimitWindow !== '' && form.rateLimitWindow !== null
|
form.rateLimitWindow !== '' && form.rateLimitWindow !== null
|
||||||
@@ -1042,7 +1037,6 @@ onMounted(async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
form.name = props.apiKey.name
|
form.name = props.apiKey.name
|
||||||
form.icon = props.apiKey.icon || ''
|
|
||||||
|
|
||||||
// 处理速率限制迁移:如果有tokenLimit且没有rateLimitCost,提示用户
|
// 处理速率限制迁移:如果有tokenLimit且没有rateLimitCost,提示用户
|
||||||
form.tokenLimit = props.apiKey.tokenLimit || ''
|
form.tokenLimit = props.apiKey.tokenLimit || ''
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1046,12 +1046,6 @@
|
|||||||
:value="key.id"
|
:value="key.id"
|
||||||
@change="updateSelectAllState"
|
@change="updateSelectAllState"
|
||||||
/>
|
/>
|
||||||
<!-- API Key 图标 -->
|
|
||||||
<IconPicker
|
|
||||||
v-model="key.icon"
|
|
||||||
size="medium"
|
|
||||||
@update:model-value="(val) => updateApiKeyIcon(key.id, val)"
|
|
||||||
/>
|
|
||||||
<div>
|
<div>
|
||||||
<h4 class="text-sm font-semibold text-gray-900 dark:text-gray-100">
|
<h4 class="text-sm font-semibold text-gray-900 dark:text-gray-100">
|
||||||
{{ key.name }}
|
{{ key.name }}
|
||||||
@@ -1758,7 +1752,6 @@ import { apiClient } from '@/config/api'
|
|||||||
import { useClientsStore } from '@/stores/clients'
|
import { useClientsStore } from '@/stores/clients'
|
||||||
import { useAuthStore } from '@/stores/auth'
|
import { useAuthStore } from '@/stores/auth'
|
||||||
import * as XLSX from 'xlsx-js-style'
|
import * as XLSX from 'xlsx-js-style'
|
||||||
import IconPicker from '@/components/common/IconPicker.vue'
|
|
||||||
import CreateApiKeyModal from '@/components/apikeys/CreateApiKeyModal.vue'
|
import CreateApiKeyModal from '@/components/apikeys/CreateApiKeyModal.vue'
|
||||||
import EditApiKeyModal from '@/components/apikeys/EditApiKeyModal.vue'
|
import EditApiKeyModal from '@/components/apikeys/EditApiKeyModal.vue'
|
||||||
import RenewApiKeyModal from '@/components/apikeys/RenewApiKeyModal.vue'
|
import RenewApiKeyModal from '@/components/apikeys/RenewApiKeyModal.vue'
|
||||||
@@ -2977,28 +2970,6 @@ const toggleApiKeyStatus = async (key) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 更新API Key图标
|
// 更新API Key图标
|
||||||
const updateApiKeyIcon = async (keyId, icon) => {
|
|
||||||
try {
|
|
||||||
const data = await apiClient.put(`/admin/api-keys/${keyId}`, {
|
|
||||||
icon: icon
|
|
||||||
})
|
|
||||||
|
|
||||||
if (data.success) {
|
|
||||||
// 更新本地数据
|
|
||||||
const localKey = apiKeys.value.find((k) => k.id === keyId)
|
|
||||||
if (localKey) {
|
|
||||||
localKey.icon = icon
|
|
||||||
}
|
|
||||||
showToast('图标已更新', 'success')
|
|
||||||
} else {
|
|
||||||
showToast(data.message || '更新图标失败', 'error')
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('更新图标失败:', error)
|
|
||||||
showToast('更新图标失败,请重试', 'error')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 删除API Key
|
// 删除API Key
|
||||||
const deleteApiKey = async (keyId) => {
|
const deleteApiKey = async (keyId) => {
|
||||||
let confirmed = false
|
let confirmed = false
|
||||||
|
|||||||
Reference in New Issue
Block a user