From f642fa7095f6b5828f56a5942c2ac500e1e163be Mon Sep 17 00:00:00 2001 From: iRubbish Date: Mon, 18 Aug 2025 17:00:26 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AE=9E=E7=8E=B0=20API=20Keys=20?= =?UTF-8?q?=E6=89=B9=E9=87=8F=E5=88=A0=E9=99=A4=E5=90=8E=E7=AB=AF=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 DELETE /admin/api-keys/batch 端点 - 支持批量删除最多100个API Keys - 完整的请求参数验证(数组格式、数量限制、ID有效性) - 逐个删除并记录成功/失败状态 - 详细的错误信息和日志记录 - 返回成功/失败统计结果 请求格式: { "keyIds": ["key1", "key2", ...] } 响应格式: { "success": true, "data": { "successCount": 2, "failedCount": 0, "errors": [] } } 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- .mcp.json | 14 +++++++ src/routes/admin.js | 89 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+) create mode 100644 .mcp.json diff --git a/.mcp.json b/.mcp.json new file mode 100644 index 00000000..c64c0718 --- /dev/null +++ b/.mcp.json @@ -0,0 +1,14 @@ +{ + "mcpServers": { + "spec-workflow": { + "type": "stdio", + "command": "npx", + "args": [ + "-y", + "@pimzino/spec-workflow-mcp@latest", + "/Users/weidian/project/claude-relay-service" + ], + "env": {} + } + } +} \ No newline at end of file diff --git a/src/routes/admin.js b/src/routes/admin.js index f6e9cf2d..719db889 100644 --- a/src/routes/admin.js +++ b/src/routes/admin.js @@ -814,6 +814,95 @@ router.delete('/api-keys/:keyId', authenticateAdmin, async (req, res) => { } }) +// 批量删除API Keys +router.delete('/api-keys/batch', authenticateAdmin, async (req, res) => { + try { + const { keyIds } = req.body + + // 参数验证 + if (!keyIds || !Array.isArray(keyIds) || keyIds.length === 0) { + return res.status(400).json({ + error: 'Invalid request', + message: 'keyIds 必须是一个非空数组' + }) + } + + if (keyIds.length > 100) { + return res.status(400).json({ + error: 'Too many keys', + message: '每次最多只能删除100个API Keys' + }) + } + + // 验证keyIds格式 + const invalidKeys = keyIds.filter((id) => !id || typeof id !== 'string') + if (invalidKeys.length > 0) { + return res.status(400).json({ + error: 'Invalid key IDs', + message: '包含无效的API Key ID' + }) + } + + logger.info(`🗑️ Admin attempting batch delete of ${keyIds.length} API keys`) + + const results = { + successCount: 0, + failedCount: 0, + errors: [] + } + + // 逐个删除,记录成功和失败情况 + for (const keyId of keyIds) { + try { + // 检查API Key是否存在 + const apiKey = await apiKeyService.getApiKey(keyId) + if (!apiKey) { + results.failedCount++ + results.errors.push({ keyId, error: 'API Key 不存在' }) + continue + } + + // 执行删除 + await apiKeyService.deleteApiKey(keyId) + results.successCount++ + + logger.success(`✅ Batch delete: API key ${keyId} deleted successfully`) + } catch (error) { + results.failedCount++ + results.errors.push({ + keyId, + error: error.message || '删除失败' + }) + + logger.error(`❌ Batch delete failed for key ${keyId}:`, error) + } + } + + // 记录批量删除结果 + if (results.successCount > 0) { + logger.success( + `🎉 Batch delete completed: ${results.successCount} successful, ${results.failedCount} failed` + ) + } else { + logger.warn( + `⚠️ Batch delete completed with no successful deletions: ${results.failedCount} failed` + ) + } + + return res.json({ + success: true, + message: `批量删除完成`, + data: results + }) + } catch (error) { + logger.error('❌ Failed to batch delete API keys:', error) + return res.status(500).json({ + error: 'Batch delete failed', + message: error.message + }) + } +}) + // 👥 账户分组管理 // 创建账户分组