Merge branch 'pr-485' into dev

This commit is contained in:
shaw
2025-09-28 09:45:03 +08:00
7 changed files with 429 additions and 28 deletions

View File

@@ -547,6 +547,7 @@ router.post('/api-keys', authenticateAdmin, async (req, res) => {
weeklyOpusCostLimit,
tags,
activationDays, // 新增:激活后有效天数
activationUnit, // 新增:激活时间单位 (hours/days)
expirationMode, // 新增:过期模式
icon // 新增:图标
} = req.body
@@ -643,14 +644,23 @@ router.post('/api-keys', authenticateAdmin, async (req, res) => {
}
if (expirationMode === 'activation') {
// 验证激活时间单位
if (!activationUnit || !['hours', 'days'].includes(activationUnit)) {
return res.status(400).json({
error: 'Activation unit must be either "hours" or "days" when using activation mode'
})
}
// 验证激活时间数值
if (
!activationDays ||
!Number.isInteger(Number(activationDays)) ||
Number(activationDays) < 1
) {
return res
.status(400)
.json({ error: 'Activation days must be a positive integer when using activation mode' })
const unitText = activationUnit === 'hours' ? 'hours' : 'days'
return res.status(400).json({
error: `Activation ${unitText} must be a positive integer when using activation mode`
})
}
// 激活模式下不应该设置固定过期时间
if (expiresAt) {
@@ -684,6 +694,7 @@ router.post('/api-keys', authenticateAdmin, async (req, res) => {
weeklyOpusCostLimit,
tags,
activationDays,
activationUnit,
expirationMode,
icon
})
@@ -724,6 +735,7 @@ router.post('/api-keys/batch', authenticateAdmin, async (req, res) => {
weeklyOpusCostLimit,
tags,
activationDays,
activationUnit,
expirationMode,
icon
} = req.body
@@ -774,6 +786,7 @@ router.post('/api-keys/batch', authenticateAdmin, async (req, res) => {
weeklyOpusCostLimit,
tags,
activationDays,
activationUnit,
expirationMode,
icon
})

View File

@@ -37,6 +37,7 @@ class ApiKeyService {
weeklyOpusCostLimit = 0,
tags = [],
activationDays = 0, // 新增激活后有效天数0表示不使用此功能
activationUnit = 'days', // 新增:激活时间单位 'hours' 或 'days'
expirationMode = 'fixed', // 新增:过期模式 'fixed'(固定时间) 或 'activation'(首次使用后激活)
icon = '' // 新增图标base64编码
} = options
@@ -73,6 +74,7 @@ class ApiKeyService {
weeklyOpusCostLimit: String(weeklyOpusCostLimit || 0),
tags: JSON.stringify(tags || []),
activationDays: String(activationDays || 0), // 新增:激活后有效天数
activationUnit: activationUnit || 'days', // 新增:激活时间单位
expirationMode: expirationMode || 'fixed', // 新增:过期模式
isActivated: expirationMode === 'fixed' ? 'true' : 'false', // 根据模式决定激活状态
activatedAt: expirationMode === 'fixed' ? new Date().toISOString() : '', // 激活时间
@@ -117,6 +119,7 @@ class ApiKeyService {
weeklyOpusCostLimit: parseFloat(keyData.weeklyOpusCostLimit || 0),
tags: JSON.parse(keyData.tags || '[]'),
activationDays: parseInt(keyData.activationDays || 0),
activationUnit: keyData.activationUnit || 'days',
expirationMode: keyData.expirationMode || 'fixed',
isActivated: keyData.isActivated === 'true',
activatedAt: keyData.activatedAt,
@@ -152,8 +155,18 @@ class ApiKeyService {
if (keyData.expirationMode === 'activation' && keyData.isActivated !== 'true') {
// 首次使用,需要激活
const now = new Date()
const activationDays = parseInt(keyData.activationDays || 30) // 默认30
const expiresAt = new Date(now.getTime() + activationDays * 24 * 60 * 60 * 1000)
const activationPeriod = parseInt(keyData.activationDays || 30) // 默认30
const activationUnit = keyData.activationUnit || 'days' // 默认天
// 根据单位计算过期时间
let milliseconds
if (activationUnit === 'hours') {
milliseconds = activationPeriod * 60 * 60 * 1000 // 小时转毫秒
} else {
milliseconds = activationPeriod * 24 * 60 * 60 * 1000 // 天转毫秒
}
const expiresAt = new Date(now.getTime() + milliseconds)
// 更新激活状态和过期时间
keyData.isActivated = 'true'
@@ -167,7 +180,7 @@ class ApiKeyService {
logger.success(
`🔓 API key activated: ${keyData.id} (${
keyData.name
}), will expire in ${activationDays} days at ${expiresAt.toISOString()}`
}), will expire in ${activationPeriod} ${activationUnit} at ${expiresAt.toISOString()}`
)
}
@@ -361,6 +374,7 @@ class ApiKeyService {
expirationMode: keyData.expirationMode || 'fixed',
isActivated: keyData.isActivated === 'true',
activationDays: parseInt(keyData.activationDays || 0),
activationUnit: keyData.activationUnit || 'days',
activatedAt: keyData.activatedAt || null,
claudeAccountId: keyData.claudeAccountId,
claudeConsoleAccountId: keyData.claudeConsoleAccountId,
@@ -432,6 +446,7 @@ class ApiKeyService {
key.dailyCost = (await redis.getDailyCost(key.id)) || 0
key.weeklyOpusCost = (await redis.getWeeklyOpusCost(key.id)) || 0
key.activationDays = parseInt(key.activationDays || 0)
key.activationUnit = key.activationUnit || 'days'
key.expirationMode = key.expirationMode || 'fixed'
key.isActivated = key.isActivated === 'true'
key.activatedAt = key.activatedAt || null
@@ -541,6 +556,7 @@ class ApiKeyService {
'permissions',
'expiresAt',
'activationDays', // 新增:激活后有效天数
'activationUnit', // 新增:激活时间单位
'expirationMode', // 新增:过期模式
'isActivated', // 新增:是否已激活
'activatedAt', // 新增:激活时间