mirror of
https://github.com/Wei-Shaw/claude-relay-service.git
synced 2026-01-23 00:53:33 +00:00
fix: 修复apikeys页面的一些bug
This commit is contained in:
@@ -679,6 +679,9 @@ router.put('/api-keys/batch', authenticateAdmin, async (req, res) => {
|
||||
if (updates.tokenLimit !== undefined) {
|
||||
finalUpdates.tokenLimit = updates.tokenLimit
|
||||
}
|
||||
if (updates.rateLimitCost !== undefined) {
|
||||
finalUpdates.rateLimitCost = updates.rateLimitCost
|
||||
}
|
||||
if (updates.concurrencyLimit !== undefined) {
|
||||
finalUpdates.concurrencyLimit = updates.concurrencyLimit
|
||||
}
|
||||
@@ -1125,7 +1128,7 @@ router.get('/api-keys/deleted', authenticateAdmin, async (req, res) => {
|
||||
deletedAt: key.deletedAt,
|
||||
deletedBy: key.deletedBy,
|
||||
deletedByType: key.deletedByType,
|
||||
canRestore: false // Deleted keys cannot be restored per requirement
|
||||
canRestore: true // 已删除的API Key可以恢复
|
||||
}))
|
||||
|
||||
logger.success(`📋 Admin retrieved ${enrichedKeys.length} deleted API keys`)
|
||||
@@ -1138,6 +1141,123 @@ router.get('/api-keys/deleted', authenticateAdmin, async (req, res) => {
|
||||
}
|
||||
})
|
||||
|
||||
// 🔄 恢复已删除的API Key
|
||||
router.post('/api-keys/:keyId/restore', authenticateAdmin, async (req, res) => {
|
||||
try {
|
||||
const { keyId } = req.params
|
||||
const adminUsername = req.session?.admin?.username || 'unknown'
|
||||
|
||||
// 调用服务层的恢复方法
|
||||
const result = await apiKeyService.restoreApiKey(keyId, adminUsername, 'admin')
|
||||
|
||||
if (result.success) {
|
||||
logger.success(`✅ Admin ${adminUsername} restored API key: ${keyId}`)
|
||||
return res.json({
|
||||
success: true,
|
||||
message: 'API Key 已成功恢复',
|
||||
apiKey: result.apiKey
|
||||
})
|
||||
} else {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
error: 'Failed to restore API key'
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('❌ Failed to restore API key:', error)
|
||||
|
||||
// 根据错误类型返回适当的响应
|
||||
if (error.message === 'API key not found') {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
error: 'API Key 不存在'
|
||||
})
|
||||
} else if (error.message === 'API key is not deleted') {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
error: '该 API Key 未被删除,无需恢复'
|
||||
})
|
||||
}
|
||||
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
error: '恢复 API Key 失败',
|
||||
message: error.message
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
// 🗑️ 彻底删除API Key(物理删除)
|
||||
router.delete('/api-keys/:keyId/permanent', authenticateAdmin, async (req, res) => {
|
||||
try {
|
||||
const { keyId } = req.params
|
||||
const adminUsername = req.session?.admin?.username || 'unknown'
|
||||
|
||||
// 调用服务层的彻底删除方法
|
||||
const result = await apiKeyService.permanentDeleteApiKey(keyId)
|
||||
|
||||
if (result.success) {
|
||||
logger.success(`🗑️ Admin ${adminUsername} permanently deleted API key: ${keyId}`)
|
||||
return res.json({
|
||||
success: true,
|
||||
message: 'API Key 已彻底删除'
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('❌ Failed to permanently delete API key:', error)
|
||||
|
||||
if (error.message === 'API key not found') {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
error: 'API Key 不存在'
|
||||
})
|
||||
} else if (error.message === '只能彻底删除已经删除的API Key') {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
error: '只能彻底删除已经删除的API Key'
|
||||
})
|
||||
}
|
||||
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
error: '彻底删除 API Key 失败',
|
||||
message: error.message
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
// 🧹 清空所有已删除的API Keys
|
||||
router.delete('/api-keys/deleted/clear-all', authenticateAdmin, async (req, res) => {
|
||||
try {
|
||||
const adminUsername = req.session?.admin?.username || 'unknown'
|
||||
|
||||
// 调用服务层的清空方法
|
||||
const result = await apiKeyService.clearAllDeletedApiKeys()
|
||||
|
||||
logger.success(
|
||||
`🧹 Admin ${adminUsername} cleared deleted API keys: ${result.successCount}/${result.total}`
|
||||
)
|
||||
|
||||
return res.json({
|
||||
success: true,
|
||||
message: `成功清空 ${result.successCount} 个已删除的 API Keys`,
|
||||
details: {
|
||||
total: result.total,
|
||||
successCount: result.successCount,
|
||||
failedCount: result.failedCount,
|
||||
errors: result.errors
|
||||
}
|
||||
})
|
||||
} catch (error) {
|
||||
logger.error('❌ Failed to clear all deleted API keys:', error)
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
error: '清空已删除的 API Keys 失败',
|
||||
message: error.message
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
// 👥 账户分组管理
|
||||
|
||||
// 创建账户分组
|
||||
|
||||
@@ -434,6 +434,139 @@ class ApiKeyService {
|
||||
}
|
||||
}
|
||||
|
||||
// 🔄 恢复已删除的API Key
|
||||
async restoreApiKey(keyId, restoredBy = 'system', restoredByType = 'system') {
|
||||
try {
|
||||
const keyData = await redis.getApiKey(keyId)
|
||||
if (!keyData || Object.keys(keyData).length === 0) {
|
||||
throw new Error('API key not found')
|
||||
}
|
||||
|
||||
// 检查是否确实是已删除的key
|
||||
if (keyData.isDeleted !== 'true') {
|
||||
throw new Error('API key is not deleted')
|
||||
}
|
||||
|
||||
// 准备更新的数据
|
||||
const updatedData = { ...keyData }
|
||||
updatedData.isActive = 'true'
|
||||
updatedData.restoredAt = new Date().toISOString()
|
||||
updatedData.restoredBy = restoredBy
|
||||
updatedData.restoredByType = restoredByType
|
||||
|
||||
// 从更新的数据中移除删除相关的字段
|
||||
delete updatedData.isDeleted
|
||||
delete updatedData.deletedAt
|
||||
delete updatedData.deletedBy
|
||||
delete updatedData.deletedByType
|
||||
|
||||
// 保存更新后的数据
|
||||
await redis.setApiKey(keyId, updatedData)
|
||||
|
||||
// 使用Redis的hdel命令删除不需要的字段
|
||||
const keyName = `apikey:${keyId}`
|
||||
await redis.client.hdel(keyName, 'isDeleted', 'deletedAt', 'deletedBy', 'deletedByType')
|
||||
|
||||
// 重新建立哈希映射(恢复API Key的使用能力)
|
||||
if (keyData.apiKey) {
|
||||
await redis.setApiKeyHash(keyData.apiKey, {
|
||||
id: keyId,
|
||||
name: keyData.name,
|
||||
isActive: 'true'
|
||||
})
|
||||
}
|
||||
|
||||
logger.success(`✅ Restored API key: ${keyId} by ${restoredBy} (${restoredByType})`)
|
||||
|
||||
return { success: true, apiKey: updatedData }
|
||||
} catch (error) {
|
||||
logger.error('❌ Failed to restore API key:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
// 🗑️ 彻底删除API Key(物理删除)
|
||||
async permanentDeleteApiKey(keyId) {
|
||||
try {
|
||||
const keyData = await redis.getApiKey(keyId)
|
||||
if (!keyData || Object.keys(keyData).length === 0) {
|
||||
throw new Error('API key not found')
|
||||
}
|
||||
|
||||
// 确保只能彻底删除已经软删除的key
|
||||
if (keyData.isDeleted !== 'true') {
|
||||
throw new Error('只能彻底删除已经删除的API Key')
|
||||
}
|
||||
|
||||
// 删除所有相关的使用统计数据
|
||||
const today = new Date().toISOString().split('T')[0]
|
||||
const yesterday = new Date(Date.now() - 86400000).toISOString().split('T')[0]
|
||||
|
||||
// 删除每日统计
|
||||
await redis.client.del(`usage:daily:${today}:${keyId}`)
|
||||
await redis.client.del(`usage:daily:${yesterday}:${keyId}`)
|
||||
|
||||
// 删除月度统计
|
||||
const currentMonth = today.substring(0, 7)
|
||||
await redis.client.del(`usage:monthly:${currentMonth}:${keyId}`)
|
||||
|
||||
// 删除所有相关的统计键(通过模式匹配)
|
||||
const usageKeys = await redis.client.keys(`usage:*:${keyId}*`)
|
||||
if (usageKeys.length > 0) {
|
||||
await redis.client.del(...usageKeys)
|
||||
}
|
||||
|
||||
// 删除API Key本身
|
||||
await redis.deleteApiKey(keyId)
|
||||
|
||||
logger.success(`🗑️ Permanently deleted API key: ${keyId}`)
|
||||
|
||||
return { success: true }
|
||||
} catch (error) {
|
||||
logger.error('❌ Failed to permanently delete API key:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
// 🧹 清空所有已删除的API Keys
|
||||
async clearAllDeletedApiKeys() {
|
||||
try {
|
||||
const allKeys = await this.getAllApiKeys(true)
|
||||
const deletedKeys = allKeys.filter((key) => key.isDeleted === 'true')
|
||||
|
||||
let successCount = 0
|
||||
let failedCount = 0
|
||||
const errors = []
|
||||
|
||||
for (const key of deletedKeys) {
|
||||
try {
|
||||
await this.permanentDeleteApiKey(key.id)
|
||||
successCount++
|
||||
} catch (error) {
|
||||
failedCount++
|
||||
errors.push({
|
||||
keyId: key.id,
|
||||
keyName: key.name,
|
||||
error: error.message
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
logger.success(`🧹 Cleared deleted API keys: ${successCount} success, ${failedCount} failed`)
|
||||
|
||||
return {
|
||||
success: true,
|
||||
total: deletedKeys.length,
|
||||
successCount,
|
||||
failedCount,
|
||||
errors
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('❌ Failed to clear all deleted API keys:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
// 📊 记录使用情况(支持缓存token和账户级别统计)
|
||||
async recordUsage(
|
||||
keyId,
|
||||
|
||||
Reference in New Issue
Block a user