fix: 修复apikeys页面的一些bug

This commit is contained in:
shaw
2025-09-02 21:36:36 +08:00
parent 59d7705697
commit 81ad098678
4 changed files with 584 additions and 149 deletions

View File

@@ -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
})
}
})
// 👥 账户分组管理
// 创建账户分组

View File

@@ -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,