feat: 为API Key添加模型限制功能

- 前端:在API Key创建和编辑表单中添加模型限制开关和标签输入
- 前端:支持动态添加/删除限制的模型列表
- 后端:更新API Key数据结构,新增enableModelRestriction和restrictedModels字段
- 后端:在中转请求时检查模型访问权限
- 修复:Enter键提交表单问题,使用@keydown.enter.prevent
- 优化:限制模型数据持久化,关闭开关时不清空数据

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
shaw
2025-07-19 20:54:26 +08:00
parent f9933f7061
commit f962083752
9 changed files with 278 additions and 22 deletions

View File

@@ -32,7 +32,9 @@ router.post('/api-keys', authenticateAdmin, async (req, res) => {
tokenLimit,
expiresAt,
claudeAccountId,
concurrencyLimit
concurrencyLimit,
enableModelRestriction,
restrictedModels
} = req.body;
// 输入验证
@@ -57,13 +59,24 @@ router.post('/api-keys', authenticateAdmin, async (req, res) => {
return res.status(400).json({ error: 'Concurrency limit must be a non-negative integer' });
}
// 验证模型限制字段
if (enableModelRestriction !== undefined && typeof enableModelRestriction !== 'boolean') {
return res.status(400).json({ error: 'Enable model restriction must be a boolean' });
}
if (restrictedModels !== undefined && !Array.isArray(restrictedModels)) {
return res.status(400).json({ error: 'Restricted models must be an array' });
}
const newKey = await apiKeyService.generateApiKey({
name,
description,
tokenLimit,
expiresAt,
claudeAccountId,
concurrencyLimit
concurrencyLimit,
enableModelRestriction,
restrictedModels
});
logger.success(`🔑 Admin created new API key: ${name}`);
@@ -78,9 +91,9 @@ router.post('/api-keys', authenticateAdmin, async (req, res) => {
router.put('/api-keys/:keyId', authenticateAdmin, async (req, res) => {
try {
const { keyId } = req.params;
const { tokenLimit, concurrencyLimit, claudeAccountId } = req.body;
const { tokenLimit, concurrencyLimit, claudeAccountId, enableModelRestriction, restrictedModels } = req.body;
// 只允许更新tokenLimit、concurrencyLimit和claudeAccountId
// 只允许更新指定字段
const updates = {};
if (tokenLimit !== undefined && tokenLimit !== null && tokenLimit !== '') {
@@ -102,6 +115,21 @@ router.put('/api-keys/:keyId', authenticateAdmin, async (req, res) => {
updates.claudeAccountId = claudeAccountId || '';
}
// 处理模型限制字段
if (enableModelRestriction !== undefined) {
if (typeof enableModelRestriction !== 'boolean') {
return res.status(400).json({ error: 'Enable model restriction must be a boolean' });
}
updates.enableModelRestriction = enableModelRestriction;
}
if (restrictedModels !== undefined) {
if (!Array.isArray(restrictedModels)) {
return res.status(400).json({ error: 'Restricted models must be an array' });
}
updates.restrictedModels = restrictedModels;
}
await apiKeyService.updateApiKey(keyId, updates);
logger.success(`📝 Admin updated API key: ${keyId}`);