Merge pull request #814 from Guccbai/feature/multi-select-permissions [skip ci]

feat(permissions): 服务权限从单选改为多选
This commit is contained in:
Wesley Liddick
2025-12-26 00:52:42 -05:00
committed by GitHub
10 changed files with 171 additions and 113 deletions

View File

@@ -37,6 +37,51 @@ const ACCOUNT_CATEGORY_MAP = {
droid: 'droid'
}
/**
* 规范化权限数据,兼容旧格式(字符串)和新格式(数组)
* @param {string|array} permissions - 权限数据
* @returns {array} - 权限数组,空数组表示全部服务
*/
function normalizePermissions(permissions) {
if (!permissions) {
return [] // 空 = 全部服务
}
if (Array.isArray(permissions)) {
return permissions
}
// 尝试解析 JSON 字符串(新格式存储)
if (typeof permissions === 'string') {
if (permissions.startsWith('[')) {
try {
const parsed = JSON.parse(permissions)
if (Array.isArray(parsed)) {
return parsed
}
} catch (e) {
// 解析失败,继续处理为普通字符串
}
}
// 旧格式 'all' 转为空数组
if (permissions === 'all') {
return []
}
// 旧单个字符串转为数组
return [permissions]
}
return []
}
/**
* 检查是否有访问特定服务的权限
* @param {string|array} permissions - 权限数据
* @param {string} service - 服务名称claude/gemini/openai/droid
* @returns {boolean} - 是否有权限
*/
function hasPermission(permissions, service) {
const perms = normalizePermissions(permissions)
return perms.length === 0 || perms.includes(service) // 空数组 = 全部服务
}
function normalizeAccountTypeKey(type) {
if (!type) {
return null
@@ -89,7 +134,7 @@ class ApiKeyService {
azureOpenaiAccountId = null,
bedrockAccountId = null, // 添加 Bedrock 账号ID支持
droidAccountId = null,
permissions = 'all', // 可选值:'claude''gemini'、'openai'、'droid' 或 'all'
permissions = [], // 数组格式,空数组表示全部服务,如 ['claude', 'gemini']
isActive = true,
concurrencyLimit = 0,
rateLimitWindow = null,
@@ -132,7 +177,7 @@ class ApiKeyService {
azureOpenaiAccountId: azureOpenaiAccountId || '',
bedrockAccountId: bedrockAccountId || '', // 添加 Bedrock 账号ID
droidAccountId: droidAccountId || '',
permissions: permissions || 'all',
permissions: JSON.stringify(normalizePermissions(permissions)),
enableModelRestriction: String(enableModelRestriction),
restrictedModels: JSON.stringify(restrictedModels || []),
enableClientRestriction: String(enableClientRestriction || false),
@@ -186,7 +231,7 @@ class ApiKeyService {
azureOpenaiAccountId: keyData.azureOpenaiAccountId,
bedrockAccountId: keyData.bedrockAccountId, // 添加 Bedrock 账号ID
droidAccountId: keyData.droidAccountId,
permissions: keyData.permissions,
permissions: normalizePermissions(keyData.permissions),
enableModelRestriction: keyData.enableModelRestriction === 'true',
restrictedModels: JSON.parse(keyData.restrictedModels),
enableClientRestriction: keyData.enableClientRestriction === 'true',
@@ -338,7 +383,7 @@ class ApiKeyService {
azureOpenaiAccountId: keyData.azureOpenaiAccountId,
bedrockAccountId: keyData.bedrockAccountId, // 添加 Bedrock 账号ID
droidAccountId: keyData.droidAccountId,
permissions: keyData.permissions || 'all',
permissions: normalizePermissions(keyData.permissions),
tokenLimit: parseInt(keyData.tokenLimit),
concurrencyLimit: parseInt(keyData.concurrencyLimit || 0),
rateLimitWindow: parseInt(keyData.rateLimitWindow || 0),
@@ -467,7 +512,7 @@ class ApiKeyService {
azureOpenaiAccountId: keyData.azureOpenaiAccountId,
bedrockAccountId: keyData.bedrockAccountId,
droidAccountId: keyData.droidAccountId,
permissions: keyData.permissions || 'all',
permissions: normalizePermissions(keyData.permissions),
tokenLimit: parseInt(keyData.tokenLimit),
concurrencyLimit: parseInt(keyData.concurrencyLimit || 0),
rateLimitWindow: parseInt(keyData.rateLimitWindow || 0),
@@ -525,7 +570,7 @@ class ApiKeyService {
key.isActive = key.isActive === 'true'
key.enableModelRestriction = key.enableModelRestriction === 'true'
key.enableClientRestriction = key.enableClientRestriction === 'true'
key.permissions = key.permissions || 'all' // 兼容旧数据
key.permissions = normalizePermissions(key.permissions)
key.dailyCostLimit = parseFloat(key.dailyCostLimit || 0)
key.totalCostLimit = parseFloat(key.totalCostLimit || 0)
key.weeklyOpusCostLimit = parseFloat(key.weeklyOpusCostLimit || 0)
@@ -1568,7 +1613,7 @@ class ApiKeyService {
userId: keyData.userId,
userUsername: keyData.userUsername,
createdBy: keyData.createdBy,
permissions: keyData.permissions,
permissions: normalizePermissions(keyData.permissions),
dailyCostLimit: parseFloat(keyData.dailyCostLimit || 0),
totalCostLimit: parseFloat(keyData.totalCostLimit || 0),
// 所有平台账户绑定字段
@@ -1820,4 +1865,8 @@ const apiKeyService = new ApiKeyService()
// 为了方便其他服务调用,导出 recordUsage 方法
apiKeyService.recordUsageMetrics = apiKeyService.recordUsage.bind(apiKeyService)
// 导出权限辅助函数供路由使用
apiKeyService.hasPermission = hasPermission
apiKeyService.normalizePermissions = normalizePermissions
module.exports = apiKeyService