admin now is able to reassign apikey to admin/user

This commit is contained in:
Feng Yue
2025-09-02 17:17:06 +08:00
parent e973158472
commit 7a9e4abdd5
2 changed files with 144 additions and 5 deletions

View File

@@ -24,6 +24,50 @@ const ProxyHelper = require('../utils/proxyHelper')
const router = express.Router() const router = express.Router()
// 👥 用户管理
// 获取所有用户列表用于API Key分配
router.get('/users', authenticateAdmin, async (req, res) => {
try {
const userService = require('../services/userService')
const allUsers = await userService.getAllUsers()
// 只返回活跃用户,并包含管理员选项
const activeUsers = allUsers
.filter((user) => user.isActive)
.map((user) => ({
id: user.id,
username: user.username,
displayName: user.displayName || user.username,
email: user.email,
role: user.role
}))
// 添加Admin选项作为第一个
const usersWithAdmin = [
{
id: 'admin',
username: 'admin',
displayName: 'Admin',
email: '',
role: 'admin'
},
...activeUsers
]
return res.json({
success: true,
data: usersWithAdmin
})
} catch (error) {
logger.error('❌ Failed to get users list:', error)
return res.status(500).json({
error: 'Failed to get users list',
message: error.message
})
}
})
// 🔑 API Keys 管理 // 🔑 API Keys 管理
// 调试获取API Key费用详情 // 调试获取API Key费用详情
@@ -844,7 +888,8 @@ router.put('/api-keys/:keyId', authenticateAdmin, async (req, res) => {
expiresAt, expiresAt,
dailyCostLimit, dailyCostLimit,
weeklyOpusCostLimit, weeklyOpusCostLimit,
tags tags,
ownerId // 新增所有者ID字段
} = req.body } = req.body
// 只允许更新指定字段 // 只允许更新指定字段
@@ -1014,6 +1059,45 @@ router.put('/api-keys/:keyId', authenticateAdmin, async (req, res) => {
updates.isActive = isActive updates.isActive = isActive
} }
// 处理所有者变更
if (ownerId !== undefined) {
const userService = require('../services/userService')
if (ownerId === 'admin') {
// 分配给Admin
updates.userId = ''
updates.userUsername = ''
updates.createdBy = 'admin'
} else if (ownerId) {
// 分配给用户
try {
const user = await userService.getUserById(ownerId, false)
if (!user) {
return res.status(400).json({ error: 'Invalid owner: User not found' })
}
if (!user.isActive) {
return res.status(400).json({ error: 'Cannot assign to inactive user' })
}
// 设置新的所有者信息
updates.userId = ownerId
updates.userUsername = user.username
updates.createdBy = user.username
// 管理员重新分配时不检查用户的API Key数量限制
logger.info(`🔄 Admin reassigning API key ${keyId} to user ${user.username}`)
} catch (error) {
logger.error('Error fetching user for owner reassignment:', error)
return res.status(400).json({ error: 'Invalid owner ID' })
}
} else {
// 清空所有者分配给Admin
updates.userId = ''
updates.userUsername = ''
updates.createdBy = 'admin'
}
}
await apiKeyService.updateApiKey(keyId, updates) await apiKeyService.updateApiKey(keyId, updates)
logger.success(`📝 Admin updated API key: ${keyId}`) logger.success(`📝 Admin updated API key: ${keyId}`)

View File

@@ -41,6 +41,26 @@
<p class="mt-1 text-xs text-gray-500 dark:text-gray-400 sm:mt-2">名称不可修改</p> <p class="mt-1 text-xs text-gray-500 dark:text-gray-400 sm:mt-2">名称不可修改</p>
</div> </div>
<!-- 所有者选择 -->
<div>
<label
class="mb-1.5 block text-xs font-semibold text-gray-700 dark:text-gray-300 sm:mb-3 sm:text-sm"
>所有者</label
>
<select
v-model="form.ownerId"
class="form-input w-full dark:border-gray-600 dark:bg-gray-700 dark:text-gray-200"
>
<option v-for="user in availableUsers" :key="user.id" :value="user.id">
{{ user.displayName }} ({{ user.username }})
<span v-if="user.role === 'admin'" class="text-gray-500">- 管理员</span>
</option>
</select>
<p class="mt-1 text-xs text-gray-500 dark:text-gray-400 sm:mt-2">
分配此 API Key 给指定用户或管理员管理员分配时不受用户 API Key 数量限制
</p>
</div>
<!-- 标签 --> <!-- 标签 -->
<div> <div>
<label <label
@@ -666,6 +686,9 @@ const localAccounts = ref({
// 支持的客户端列表 // 支持的客户端列表
const supportedClients = ref([]) const supportedClients = ref([])
// 可用用户列表
const availableUsers = ref([])
// 标签相关 // 标签相关
const newTag = ref('') const newTag = ref('')
const availableTags = ref([]) const availableTags = ref([])
@@ -696,7 +719,8 @@ const form = reactive({
enableClientRestriction: false, enableClientRestriction: false,
allowedClients: [], allowedClients: [],
tags: [], tags: [],
isActive: true isActive: true,
ownerId: '' // 新增所有者ID
}) })
// 添加限制的模型 // 添加限制的模型
@@ -856,6 +880,11 @@ const updateApiKey = async () => {
// 活跃状态 // 活跃状态
data.isActive = form.isActive data.isActive = form.isActive
// 所有者
if (form.ownerId !== undefined) {
data.ownerId = form.ownerId
}
const result = await apiClient.put(`/admin/api-keys/${props.apiKey.id}`, data) const result = await apiClient.put(`/admin/api-keys/${props.apiKey.id}`, data)
if (result.success) { if (result.success) {
@@ -947,11 +976,34 @@ const refreshAccounts = async () => {
} }
} }
// 加载用户列表
const loadUsers = async () => {
try {
const response = await apiClient.get('/admin/users')
if (response.success) {
availableUsers.value = response.data || []
}
} catch (error) {
console.error('Failed to load users:', error)
availableUsers.value = [
{
id: 'admin',
username: 'admin',
displayName: 'Admin',
email: '',
role: 'admin'
}
]
}
}
// 初始化表单数据 // 初始化表单数据
onMounted(async () => { onMounted(async () => {
// 加载支持的客户端和已存在的标签 // 并行加载所有需要的数据
supportedClients.value = await clientsStore.loadSupportedClients() await Promise.all([clientsStore.loadSupportedClients(), apiKeysStore.fetchTags(), loadUsers()])
availableTags.value = await apiKeysStore.fetchTags()
supportedClients.value = clientsStore.supportedClients
availableTags.value = apiKeysStore.availableTags
// 初始化账号数据 // 初始化账号数据
if (props.accounts) { if (props.accounts) {
@@ -1001,6 +1053,9 @@ onMounted(async () => {
form.enableClientRestriction = props.apiKey.enableClientRestriction || false form.enableClientRestriction = props.apiKey.enableClientRestriction || false
// 初始化活跃状态,默认为 true // 初始化活跃状态,默认为 true
form.isActive = props.apiKey.isActive !== undefined ? props.apiKey.isActive : true form.isActive = props.apiKey.isActive !== undefined ? props.apiKey.isActive : true
// 初始化所有者
form.ownerId = props.apiKey.userId || 'admin'
}) })
</script> </script>