fix: 继续修复PR-541引发的系列bug

This commit is contained in:
shaw
2025-10-13 08:41:24 +08:00
parent a67c34bee1
commit 508d9aad1b

View File

@@ -32,6 +32,36 @@ const ProxyHelper = require('../utils/proxyHelper')
const router = express.Router() const router = express.Router()
function normalizeNullableDate(value) {
if (value === undefined || value === null) {
return null
}
if (typeof value === 'string') {
const trimmed = value.trim()
return trimmed === '' ? null : trimmed
}
return value
}
function formatSubscriptionExpiry(account) {
if (!account || typeof account !== 'object') {
return account
}
const rawSubscription = account.subscriptionExpiresAt
const rawToken = account.tokenExpiresAt !== undefined ? account.tokenExpiresAt : account.expiresAt
const subscriptionExpiresAt = normalizeNullableDate(rawSubscription)
const tokenExpiresAt = normalizeNullableDate(rawToken)
return {
...account,
subscriptionExpiresAt,
tokenExpiresAt,
expiresAt: subscriptionExpiresAt
}
}
// 👥 用户管理 // 👥 用户管理
// 获取所有用户列表用于API Key分配 // 获取所有用户列表用于API Key分配
@@ -2082,6 +2112,7 @@ router.get('/claude-accounts', authenticateAdmin, async (req, res) => {
try { try {
const usageStats = await redis.getAccountUsageStats(account.id, 'openai') const usageStats = await redis.getAccountUsageStats(account.id, 'openai')
const groupInfos = await accountGroupService.getAccountGroups(account.id) const groupInfos = await accountGroupService.getAccountGroups(account.id)
const formattedAccount = formatSubscriptionExpiry(account)
// 获取会话窗口使用统计(仅对有活跃窗口的账户) // 获取会话窗口使用统计(仅对有活跃窗口的账户)
let sessionWindowUsage = null let sessionWindowUsage = null
@@ -2124,7 +2155,7 @@ router.get('/claude-accounts', authenticateAdmin, async (req, res) => {
} }
return { return {
...account, ...formattedAccount,
// 转换schedulable为布尔值 // 转换schedulable为布尔值
schedulable: account.schedulable === 'true' || account.schedulable === true, schedulable: account.schedulable === 'true' || account.schedulable === true,
groupInfos, groupInfos,
@@ -2140,8 +2171,9 @@ router.get('/claude-accounts', authenticateAdmin, async (req, res) => {
// 如果获取统计失败,返回空统计 // 如果获取统计失败,返回空统计
try { try {
const groupInfos = await accountGroupService.getAccountGroups(account.id) const groupInfos = await accountGroupService.getAccountGroups(account.id)
const formattedAccount = formatSubscriptionExpiry(account)
return { return {
...account, ...formattedAccount,
groupInfos, groupInfos,
usage: { usage: {
daily: { tokens: 0, requests: 0, allTokens: 0 }, daily: { tokens: 0, requests: 0, allTokens: 0 },
@@ -2155,8 +2187,9 @@ router.get('/claude-accounts', authenticateAdmin, async (req, res) => {
`⚠️ Failed to get group info for account ${account.id}:`, `⚠️ Failed to get group info for account ${account.id}:`,
groupError.message groupError.message
) )
const formattedAccount = formatSubscriptionExpiry(account)
return { return {
...account, ...formattedAccount,
groupInfos: [], groupInfos: [],
usage: { usage: {
daily: { tokens: 0, requests: 0, allTokens: 0 }, daily: { tokens: 0, requests: 0, allTokens: 0 },
@@ -2170,7 +2203,8 @@ router.get('/claude-accounts', authenticateAdmin, async (req, res) => {
}) })
) )
return res.json({ success: true, data: accountsWithStats }) const formattedAccounts = accountsWithStats.map(formatSubscriptionExpiry)
return res.json({ success: true, data: formattedAccounts })
} catch (error) { } catch (error) {
logger.error('❌ Failed to get Claude accounts:', error) logger.error('❌ Failed to get Claude accounts:', error)
return res.status(500).json({ error: 'Failed to get Claude accounts', message: error.message }) return res.status(500).json({ error: 'Failed to get Claude accounts', message: error.message })
@@ -2327,7 +2361,8 @@ router.post('/claude-accounts', authenticateAdmin, async (req, res) => {
} }
logger.success(`🏢 Admin created new Claude account: ${name} (${accountType || 'shared'})`) logger.success(`🏢 Admin created new Claude account: ${name} (${accountType || 'shared'})`)
return res.json({ success: true, data: newAccount }) const responseAccount = formatSubscriptionExpiry(newAccount)
return res.json({ success: true, data: responseAccount })
} catch (error) { } catch (error) {
logger.error('❌ Failed to create Claude account:', error) logger.error('❌ Failed to create Claude account:', error)
return res return res
@@ -2610,14 +2645,16 @@ router.get('/claude-console-accounts', authenticateAdmin, async (req, res) => {
} }
// 为每个账户添加使用统计信息 // 为每个账户添加使用统计信息
const accountsWithStats = await Promise.all( const accountsWithStats = await Promise.all(
accounts.map(async (account) => { accounts.map(async (account) => {
const formattedAccount = formatSubscriptionExpiry(account)
try { try {
const usageStats = await redis.getAccountUsageStats(account.id, 'openai') const usageStats = await redis.getAccountUsageStats(account.id, 'openai')
const groupInfos = await accountGroupService.getAccountGroups(account.id) const groupInfos = await accountGroupService.getAccountGroups(account.id)
return { return {
...account, ...formattedAccount,
// 转换schedulable为布尔值 // 转换schedulable为布尔值
schedulable: account.schedulable === 'true' || account.schedulable === true, schedulable: account.schedulable === 'true' || account.schedulable === true,
groupInfos, groupInfos,
@@ -2635,7 +2672,7 @@ router.get('/claude-console-accounts', authenticateAdmin, async (req, res) => {
try { try {
const groupInfos = await accountGroupService.getAccountGroups(account.id) const groupInfos = await accountGroupService.getAccountGroups(account.id)
return { return {
...account, ...formattedAccount,
// 转换schedulable为布尔值 // 转换schedulable为布尔值
schedulable: account.schedulable === 'true' || account.schedulable === true, schedulable: account.schedulable === 'true' || account.schedulable === true,
groupInfos, groupInfos,
@@ -2651,7 +2688,7 @@ router.get('/claude-console-accounts', authenticateAdmin, async (req, res) => {
groupError.message groupError.message
) )
return { return {
...account, ...formattedAccount,
groupInfos: [], groupInfos: [],
usage: { usage: {
daily: { tokens: 0, requests: 0, allTokens: 0 }, daily: { tokens: 0, requests: 0, allTokens: 0 },
@@ -2664,7 +2701,8 @@ router.get('/claude-console-accounts', authenticateAdmin, async (req, res) => {
}) })
) )
return res.json({ success: true, data: accountsWithStats }) const formattedAccounts = accountsWithStats.map(formatSubscriptionExpiry)
return res.json({ success: true, data: formattedAccounts })
} catch (error) { } catch (error) {
logger.error('❌ Failed to get Claude Console accounts:', error) logger.error('❌ Failed to get Claude Console accounts:', error)
return res return res
@@ -2735,7 +2773,8 @@ router.post('/claude-console-accounts', authenticateAdmin, async (req, res) => {
} }
logger.success(`🎮 Admin created Claude Console account: ${name}`) logger.success(`🎮 Admin created Claude Console account: ${name}`)
return res.json({ success: true, data: newAccount }) const responseAccount = formatSubscriptionExpiry(newAccount)
return res.json({ success: true, data: responseAccount })
} catch (error) { } catch (error) {
logger.error('❌ Failed to create Claude Console account:', error) logger.error('❌ Failed to create Claude Console account:', error)
return res return res
@@ -3037,12 +3076,13 @@ router.get('/ccr-accounts', authenticateAdmin, async (req, res) => {
// 为每个账户添加使用统计信息 // 为每个账户添加使用统计信息
const accountsWithStats = await Promise.all( const accountsWithStats = await Promise.all(
accounts.map(async (account) => { accounts.map(async (account) => {
const formattedAccount = formatSubscriptionExpiry(account)
try { try {
const usageStats = await redis.getAccountUsageStats(account.id) const usageStats = await redis.getAccountUsageStats(account.id)
const groupInfos = await accountGroupService.getAccountGroups(account.id) const groupInfos = await accountGroupService.getAccountGroups(account.id)
return { return {
...account, ...formattedAccount,
// 转换schedulable为布尔值 // 转换schedulable为布尔值
schedulable: account.schedulable === 'true' || account.schedulable === true, schedulable: account.schedulable === 'true' || account.schedulable === true,
groupInfos, groupInfos,
@@ -3060,7 +3100,7 @@ router.get('/ccr-accounts', authenticateAdmin, async (req, res) => {
try { try {
const groupInfos = await accountGroupService.getAccountGroups(account.id) const groupInfos = await accountGroupService.getAccountGroups(account.id)
return { return {
...account, ...formattedAccount,
// 转换schedulable为布尔值 // 转换schedulable为布尔值
schedulable: account.schedulable === 'true' || account.schedulable === true, schedulable: account.schedulable === 'true' || account.schedulable === true,
groupInfos, groupInfos,
@@ -3076,7 +3116,7 @@ router.get('/ccr-accounts', authenticateAdmin, async (req, res) => {
groupError.message groupError.message
) )
return { return {
...account, ...formattedAccount,
groupInfos: [], groupInfos: [],
usage: { usage: {
daily: { tokens: 0, requests: 0, allTokens: 0 }, daily: { tokens: 0, requests: 0, allTokens: 0 },
@@ -3089,7 +3129,8 @@ router.get('/ccr-accounts', authenticateAdmin, async (req, res) => {
}) })
) )
return res.json({ success: true, data: accountsWithStats }) const formattedAccounts = accountsWithStats.map(formatSubscriptionExpiry)
return res.json({ success: true, data: formattedAccounts })
} catch (error) { } catch (error) {
logger.error('❌ Failed to get CCR accounts:', error) logger.error('❌ Failed to get CCR accounts:', error)
return res.status(500).json({ error: 'Failed to get CCR accounts', message: error.message }) return res.status(500).json({ error: 'Failed to get CCR accounts', message: error.message })
@@ -3158,7 +3199,8 @@ router.post('/ccr-accounts', authenticateAdmin, async (req, res) => {
} }
logger.success(`🔧 Admin created CCR account: ${name}`) logger.success(`🔧 Admin created CCR account: ${name}`)
return res.json({ success: true, data: newAccount }) const responseAccount = formatSubscriptionExpiry(newAccount)
return res.json({ success: true, data: responseAccount })
} catch (error) { } catch (error) {
logger.error('❌ Failed to create CCR account:', error) logger.error('❌ Failed to create CCR account:', error)
return res.status(500).json({ error: 'Failed to create CCR account', message: error.message }) return res.status(500).json({ error: 'Failed to create CCR account', message: error.message })
@@ -3446,12 +3488,13 @@ router.get('/bedrock-accounts', authenticateAdmin, async (req, res) => {
// 为每个账户添加使用统计信息 // 为每个账户添加使用统计信息
const accountsWithStats = await Promise.all( const accountsWithStats = await Promise.all(
accounts.map(async (account) => { accounts.map(async (account) => {
const formattedAccount = formatSubscriptionExpiry(account)
try { try {
const usageStats = await redis.getAccountUsageStats(account.id, 'openai') const usageStats = await redis.getAccountUsageStats(account.id, 'openai')
const groupInfos = await accountGroupService.getAccountGroups(account.id) const groupInfos = await accountGroupService.getAccountGroups(account.id)
return { return {
...account, ...formattedAccount,
groupInfos, groupInfos,
usage: { usage: {
daily: usageStats.daily, daily: usageStats.daily,
@@ -3467,7 +3510,7 @@ router.get('/bedrock-accounts', authenticateAdmin, async (req, res) => {
try { try {
const groupInfos = await accountGroupService.getAccountGroups(account.id) const groupInfos = await accountGroupService.getAccountGroups(account.id)
return { return {
...account, ...formattedAccount,
groupInfos, groupInfos,
usage: { usage: {
daily: { tokens: 0, requests: 0, allTokens: 0 }, daily: { tokens: 0, requests: 0, allTokens: 0 },
@@ -3481,7 +3524,7 @@ router.get('/bedrock-accounts', authenticateAdmin, async (req, res) => {
groupError.message groupError.message
) )
return { return {
...account, ...formattedAccount,
groupInfos: [], groupInfos: [],
usage: { usage: {
daily: { tokens: 0, requests: 0, allTokens: 0 }, daily: { tokens: 0, requests: 0, allTokens: 0 },
@@ -3494,7 +3537,8 @@ router.get('/bedrock-accounts', authenticateAdmin, async (req, res) => {
}) })
) )
return res.json({ success: true, data: accountsWithStats }) const formattedAccounts = accountsWithStats.map(formatSubscriptionExpiry)
return res.json({ success: true, data: formattedAccounts })
} catch (error) { } catch (error) {
logger.error('❌ Failed to get Bedrock accounts:', error) logger.error('❌ Failed to get Bedrock accounts:', error)
return res.status(500).json({ error: 'Failed to get Bedrock accounts', message: error.message }) return res.status(500).json({ error: 'Failed to get Bedrock accounts', message: error.message })
@@ -3556,7 +3600,8 @@ router.post('/bedrock-accounts', authenticateAdmin, async (req, res) => {
} }
logger.success(`☁️ Admin created Bedrock account: ${name}`) logger.success(`☁️ Admin created Bedrock account: ${name}`)
return res.json({ success: true, data: result.data }) const responseAccount = formatSubscriptionExpiry(result.data)
return res.json({ success: true, data: responseAccount })
} catch (error) { } catch (error) {
logger.error('❌ Failed to create Bedrock account:', error) logger.error('❌ Failed to create Bedrock account:', error)
return res return res
@@ -3923,17 +3968,13 @@ router.get('/gemini-accounts', authenticateAdmin, async (req, res) => {
// 为每个账户添加使用统计信息与Claude账户相同的逻辑 // 为每个账户添加使用统计信息与Claude账户相同的逻辑
const accountsWithStats = await Promise.all( const accountsWithStats = await Promise.all(
accounts.map(async (account) => { accounts.map(async (account) => {
const formattedAccount = formatSubscriptionExpiry(account)
try { try {
const usageStats = await redis.getAccountUsageStats(account.id, 'openai') const usageStats = await redis.getAccountUsageStats(account.id, 'openai')
const groupInfos = await accountGroupService.getAccountGroups(account.id) const groupInfos = await accountGroupService.getAccountGroups(account.id)
return { return {
...account, ...formattedAccount,
expiresAt: account.expiresAt || null,
subscriptionExpiresAt:
account.subscriptionExpiresAt && account.subscriptionExpiresAt !== ''
? account.subscriptionExpiresAt
: null,
groupInfos, groupInfos,
usage: { usage: {
daily: usageStats.daily, daily: usageStats.daily,
@@ -3950,12 +3991,7 @@ router.get('/gemini-accounts', authenticateAdmin, async (req, res) => {
try { try {
const groupInfos = await accountGroupService.getAccountGroups(account.id) const groupInfos = await accountGroupService.getAccountGroups(account.id)
return { return {
...account, ...formattedAccount,
expiresAt: account.expiresAt || null,
subscriptionExpiresAt:
account.subscriptionExpiresAt && account.subscriptionExpiresAt !== ''
? account.subscriptionExpiresAt
: null,
groupInfos, groupInfos,
usage: { usage: {
daily: { tokens: 0, requests: 0, allTokens: 0 }, daily: { tokens: 0, requests: 0, allTokens: 0 },
@@ -3969,12 +4005,7 @@ router.get('/gemini-accounts', authenticateAdmin, async (req, res) => {
groupError.message groupError.message
) )
return { return {
...account, ...formattedAccount,
expiresAt: account.expiresAt || null,
subscriptionExpiresAt:
account.subscriptionExpiresAt && account.subscriptionExpiresAt !== ''
? account.subscriptionExpiresAt
: null,
groupInfos: [], groupInfos: [],
usage: { usage: {
daily: { tokens: 0, requests: 0, allTokens: 0 }, daily: { tokens: 0, requests: 0, allTokens: 0 },
@@ -3987,7 +4018,8 @@ router.get('/gemini-accounts', authenticateAdmin, async (req, res) => {
}) })
) )
return res.json({ success: true, data: accountsWithStats }) const formattedAccounts = accountsWithStats.map(formatSubscriptionExpiry)
return res.json({ success: true, data: formattedAccounts })
} catch (error) { } catch (error) {
logger.error('❌ Failed to get Gemini accounts:', error) logger.error('❌ Failed to get Gemini accounts:', error)
return res.status(500).json({ error: 'Failed to get accounts', message: error.message }) return res.status(500).json({ error: 'Failed to get accounts', message: error.message })
@@ -4027,7 +4059,8 @@ router.post('/gemini-accounts', authenticateAdmin, async (req, res) => {
} }
logger.success(`🏢 Admin created new Gemini account: ${accountData.name}`) logger.success(`🏢 Admin created new Gemini account: ${accountData.name}`)
return res.json({ success: true, data: newAccount }) const responseAccount = formatSubscriptionExpiry(newAccount)
return res.json({ success: true, data: responseAccount })
} catch (error) { } catch (error) {
logger.error('❌ Failed to create Gemini account:', error) logger.error('❌ Failed to create Gemini account:', error)
return res.status(500).json({ error: 'Failed to create account', message: error.message }) return res.status(500).json({ error: 'Failed to create account', message: error.message })
@@ -4099,7 +4132,8 @@ router.put('/gemini-accounts/:accountId', authenticateAdmin, async (req, res) =>
const updatedAccount = await geminiAccountService.updateAccount(accountId, mappedUpdates) const updatedAccount = await geminiAccountService.updateAccount(accountId, mappedUpdates)
logger.success(`📝 Admin updated Gemini account: ${accountId}`) logger.success(`📝 Admin updated Gemini account: ${accountId}`)
return res.json({ success: true, data: updatedAccount }) const responseAccount = formatSubscriptionExpiry(updatedAccount)
return res.json({ success: true, data: responseAccount })
} catch (error) { } catch (error) {
logger.error('❌ Failed to update Gemini account:', error) logger.error('❌ Failed to update Gemini account:', error)
return res.status(500).json({ error: 'Failed to update account', message: error.message }) return res.status(500).json({ error: 'Failed to update account', message: error.message })
@@ -7247,8 +7281,9 @@ router.get('/openai-accounts', authenticateAdmin, async (req, res) => {
try { try {
const usageStats = await redis.getAccountUsageStats(account.id, 'openai') const usageStats = await redis.getAccountUsageStats(account.id, 'openai')
const groupInfos = await fetchAccountGroups(account.id) const groupInfos = await fetchAccountGroups(account.id)
const formattedAccount = formatSubscriptionExpiry(account)
return { return {
...account, ...formattedAccount,
groupInfos, groupInfos,
usage: { usage: {
daily: usageStats.daily, daily: usageStats.daily,
@@ -7259,8 +7294,9 @@ router.get('/openai-accounts', authenticateAdmin, async (req, res) => {
} catch (error) { } catch (error) {
logger.debug(`Failed to get usage stats for OpenAI account ${account.id}:`, error) logger.debug(`Failed to get usage stats for OpenAI account ${account.id}:`, error)
const groupInfos = await fetchAccountGroups(account.id) const groupInfos = await fetchAccountGroups(account.id)
const formattedAccount = formatSubscriptionExpiry(account)
return { return {
...account, ...formattedAccount,
groupInfos, groupInfos,
usage: { usage: {
daily: { requests: 0, tokens: 0, allTokens: 0 }, daily: { requests: 0, tokens: 0, allTokens: 0 },
@@ -7274,9 +7310,11 @@ router.get('/openai-accounts', authenticateAdmin, async (req, res) => {
logger.info(`获取 OpenAI 账户列表: ${accountsWithStats.length} 个账户`) logger.info(`获取 OpenAI 账户列表: ${accountsWithStats.length} 个账户`)
const formattedAccounts = accountsWithStats.map(formatSubscriptionExpiry)
return res.json({ return res.json({
success: true, success: true,
data: accountsWithStats data: formattedAccounts
}) })
} catch (error) { } catch (error) {
logger.error('获取 OpenAI 账户列表失败:', error) logger.error('获取 OpenAI 账户列表失败:', error)
@@ -7362,9 +7400,11 @@ router.post('/openai-accounts', authenticateAdmin, async (req, res) => {
logger.success(`✅ 创建并验证 OpenAI 账户成功: ${name} (ID: ${tempAccount.id})`) logger.success(`✅ 创建并验证 OpenAI 账户成功: ${name} (ID: ${tempAccount.id})`)
const responseAccount = formatSubscriptionExpiry(refreshedAccount)
return res.json({ return res.json({
success: true, success: true,
data: refreshedAccount, data: responseAccount,
message: '账户创建成功,并已获取完整 token 信息' message: '账户创建成功,并已获取完整 token 信息'
}) })
} catch (refreshError) { } catch (refreshError) {
@@ -7426,9 +7466,11 @@ router.post('/openai-accounts', authenticateAdmin, async (req, res) => {
logger.success(`✅ 创建 OpenAI 账户成功: ${name} (ID: ${createdAccount.id})`) logger.success(`✅ 创建 OpenAI 账户成功: ${name} (ID: ${createdAccount.id})`)
const responseAccount = formatSubscriptionExpiry(createdAccount)
return res.json({ return res.json({
success: true, success: true,
data: createdAccount data: responseAccount
}) })
} catch (error) { } catch (error) {
logger.error('创建 OpenAI 账户失败:', error) logger.error('创建 OpenAI 账户失败:', error)
@@ -7635,7 +7677,8 @@ router.put('/openai-accounts/:id', authenticateAdmin, async (req, res) => {
} }
logger.success(`📝 Admin updated OpenAI account: ${id}`) logger.success(`📝 Admin updated OpenAI account: ${id}`)
return res.json({ success: true, data: updatedAccount }) const responseAccount = formatSubscriptionExpiry(updatedAccount)
return res.json({ success: true, data: responseAccount })
} catch (error) { } catch (error) {
logger.error('❌ Failed to update OpenAI account:', error) logger.error('❌ Failed to update OpenAI account:', error)
return res.status(500).json({ error: 'Failed to update account', message: error.message }) return res.status(500).json({ error: 'Failed to update account', message: error.message })
@@ -7716,9 +7759,11 @@ router.put('/openai-accounts/:id/toggle', authenticateAdmin, async (req, res) =>
`${account.enabled ? '启用' : '禁用'} OpenAI 账户: ${account.name} (ID: ${id})` `${account.enabled ? '启用' : '禁用'} OpenAI 账户: ${account.name} (ID: ${id})`
) )
const responseAccount = formatSubscriptionExpiry(account)
return res.json({ return res.json({
success: true, success: true,
data: account data: responseAccount
}) })
} catch (error) { } catch (error) {
logger.error('切换 OpenAI 账户状态失败:', error) logger.error('切换 OpenAI 账户状态失败:', error)
@@ -7824,11 +7869,12 @@ router.get('/azure-openai-accounts', authenticateAdmin, async (req, res) => {
// 为每个账户添加使用统计信息和分组信息 // 为每个账户添加使用统计信息和分组信息
const accountsWithStats = await Promise.all( const accountsWithStats = await Promise.all(
accounts.map(async (account) => { accounts.map(async (account) => {
const formattedAccount = formatSubscriptionExpiry(account)
try { try {
const usageStats = await redis.getAccountUsageStats(account.id, 'openai') const usageStats = await redis.getAccountUsageStats(account.id, 'openai')
const groupInfos = await accountGroupService.getAccountGroups(account.id) const groupInfos = await accountGroupService.getAccountGroups(account.id)
return { return {
...account, ...formattedAccount,
groupInfos, groupInfos,
usage: { usage: {
daily: usageStats.daily, daily: usageStats.daily,
@@ -7841,7 +7887,7 @@ router.get('/azure-openai-accounts', authenticateAdmin, async (req, res) => {
try { try {
const groupInfos = await accountGroupService.getAccountGroups(account.id) const groupInfos = await accountGroupService.getAccountGroups(account.id)
return { return {
...account, ...formattedAccount,
groupInfos, groupInfos,
usage: { usage: {
daily: { requests: 0, tokens: 0, allTokens: 0 }, daily: { requests: 0, tokens: 0, allTokens: 0 },
@@ -7852,7 +7898,7 @@ router.get('/azure-openai-accounts', authenticateAdmin, async (req, res) => {
} catch (groupError) { } catch (groupError) {
logger.debug(`Failed to get group info for account ${account.id}:`, groupError) logger.debug(`Failed to get group info for account ${account.id}:`, groupError)
return { return {
...account, ...formattedAccount,
groupInfos: [], groupInfos: [],
usage: { usage: {
daily: { requests: 0, tokens: 0, allTokens: 0 }, daily: { requests: 0, tokens: 0, allTokens: 0 },
@@ -7865,9 +7911,11 @@ router.get('/azure-openai-accounts', authenticateAdmin, async (req, res) => {
}) })
) )
const formattedAccounts = accountsWithStats.map(formatSubscriptionExpiry)
res.json({ res.json({
success: true, success: true,
data: accountsWithStats data: formattedAccounts
}) })
} catch (error) { } catch (error) {
logger.error('Failed to fetch Azure OpenAI accounts:', error) logger.error('Failed to fetch Azure OpenAI accounts:', error)
@@ -7986,9 +8034,11 @@ router.post('/azure-openai-accounts', authenticateAdmin, async (req, res) => {
} }
} }
const responseAccount = formatSubscriptionExpiry(account)
res.json({ res.json({
success: true, success: true,
data: account, data: responseAccount,
message: 'Azure OpenAI account created successfully' message: 'Azure OpenAI account created successfully'
}) })
} catch (error) { } catch (error) {
@@ -8019,10 +8069,11 @@ router.put('/azure-openai-accounts/:id', authenticateAdmin, async (req, res) =>
} }
const account = await azureOpenaiAccountService.updateAccount(id, mappedUpdates) const account = await azureOpenaiAccountService.updateAccount(id, mappedUpdates)
const responseAccount = formatSubscriptionExpiry(account)
res.json({ res.json({
success: true, success: true,
data: account, data: responseAccount,
message: 'Azure OpenAI account updated successfully' message: 'Azure OpenAI account updated successfully'
}) })
} catch (error) { } catch (error) {
@@ -8275,6 +8326,7 @@ router.get('/openai-responses-accounts', authenticateAdmin, async (req, res) =>
// 处理额度信息、使用统计和绑定的 API Key 数量 // 处理额度信息、使用统计和绑定的 API Key 数量
const accountsWithStats = await Promise.all( const accountsWithStats = await Promise.all(
accounts.map(async (account) => { accounts.map(async (account) => {
const formattedAccount = formatSubscriptionExpiry(account)
try { try {
// 检查是否需要重置额度 // 检查是否需要重置额度
const today = redis.getDateStringInTimezone() const today = redis.getDateStringInTimezone()
@@ -8329,7 +8381,7 @@ router.get('/openai-responses-accounts', authenticateAdmin, async (req, res) =>
} }
return { return {
...account, ...formattedAccount,
boundApiKeysCount: boundCount, boundApiKeysCount: boundCount,
usage: { usage: {
daily: usageStats.daily, daily: usageStats.daily,
@@ -8340,7 +8392,7 @@ router.get('/openai-responses-accounts', authenticateAdmin, async (req, res) =>
} catch (error) { } catch (error) {
logger.error(`Failed to process OpenAI-Responses account ${account.id}:`, error) logger.error(`Failed to process OpenAI-Responses account ${account.id}:`, error)
return { return {
...account, ...formattedAccount,
boundApiKeysCount: 0, boundApiKeysCount: 0,
usage: { usage: {
daily: { requests: 0, tokens: 0, allTokens: 0 }, daily: { requests: 0, tokens: 0, allTokens: 0 },
@@ -8352,7 +8404,9 @@ router.get('/openai-responses-accounts', authenticateAdmin, async (req, res) =>
}) })
) )
res.json({ success: true, data: accountsWithStats }) const formattedAccounts = accountsWithStats.map(formatSubscriptionExpiry)
res.json({ success: true, data: formattedAccounts })
} catch (error) { } catch (error) {
logger.error('Failed to get OpenAI-Responses accounts:', error) logger.error('Failed to get OpenAI-Responses accounts:', error)
res.status(500).json({ success: false, message: error.message }) res.status(500).json({ success: false, message: error.message })
@@ -8363,7 +8417,8 @@ router.get('/openai-responses-accounts', authenticateAdmin, async (req, res) =>
router.post('/openai-responses-accounts', authenticateAdmin, async (req, res) => { router.post('/openai-responses-accounts', authenticateAdmin, async (req, res) => {
try { try {
const account = await openaiResponsesAccountService.createAccount(req.body) const account = await openaiResponsesAccountService.createAccount(req.body)
res.json({ success: true, account }) const responseAccount = formatSubscriptionExpiry(account)
res.json({ success: true, data: responseAccount })
} catch (error) { } catch (error) {
logger.error('Failed to create OpenAI-Responses account:', error) logger.error('Failed to create OpenAI-Responses account:', error)
res.status(500).json({ res.status(500).json({
@@ -8408,7 +8463,13 @@ router.put('/openai-responses-accounts/:id', authenticateAdmin, async (req, res)
return res.status(400).json(result) return res.status(400).json(result)
} }
res.json({ success: true, ...result }) const updatedAccountData = await openaiResponsesAccountService.getAccount(id)
if (updatedAccountData) {
updatedAccountData.apiKey = '***'
}
const responseAccount = formatSubscriptionExpiry(updatedAccountData)
res.json({ success: true, data: responseAccount })
} catch (error) { } catch (error) {
logger.error('Failed to update OpenAI-Responses account:', error) logger.error('Failed to update OpenAI-Responses account:', error)
res.status(500).json({ res.status(500).json({
@@ -8738,6 +8799,7 @@ router.get('/droid-accounts', authenticateAdmin, async (req, res) => {
// 添加使用统计 // 添加使用统计
const accountsWithStats = await Promise.all( const accountsWithStats = await Promise.all(
accounts.map(async (account) => { accounts.map(async (account) => {
const formattedAccount = formatSubscriptionExpiry(account)
try { try {
const usageStats = await redis.getAccountUsageStats(account.id, 'droid') const usageStats = await redis.getAccountUsageStats(account.id, 'droid')
let groupInfos = [] let groupInfos = []
@@ -8767,12 +8829,7 @@ router.get('/droid-accounts', authenticateAdmin, async (req, res) => {
}, 0) }, 0)
return { return {
...account, ...formattedAccount,
expiresAt: account.expiresAt || null,
subscriptionExpiresAt:
account.subscriptionExpiresAt && account.subscriptionExpiresAt !== ''
? account.subscriptionExpiresAt
: null,
schedulable: account.schedulable === 'true', schedulable: account.schedulable === 'true',
boundApiKeysCount, boundApiKeysCount,
groupInfos, groupInfos,
@@ -8785,12 +8842,7 @@ router.get('/droid-accounts', authenticateAdmin, async (req, res) => {
} catch (error) { } catch (error) {
logger.warn(`Failed to get stats for Droid account ${account.id}:`, error.message) logger.warn(`Failed to get stats for Droid account ${account.id}:`, error.message)
return { return {
...account, ...formattedAccount,
expiresAt: account.expiresAt || null,
subscriptionExpiresAt:
account.subscriptionExpiresAt && account.subscriptionExpiresAt !== ''
? account.subscriptionExpiresAt
: null,
boundApiKeysCount: 0, boundApiKeysCount: 0,
groupInfos: [], groupInfos: [],
usage: { usage: {
@@ -8803,7 +8855,9 @@ router.get('/droid-accounts', authenticateAdmin, async (req, res) => {
}) })
) )
return res.json({ success: true, data: accountsWithStats }) const formattedAccounts = accountsWithStats.map(formatSubscriptionExpiry)
return res.json({ success: true, data: formattedAccounts })
} catch (error) { } catch (error) {
logger.error('Failed to get Droid accounts:', error) logger.error('Failed to get Droid accounts:', error)
return res.status(500).json({ error: 'Failed to get Droid accounts', message: error.message }) return res.status(500).json({ error: 'Failed to get Droid accounts', message: error.message })
@@ -8860,7 +8914,8 @@ router.post('/droid-accounts', authenticateAdmin, async (req, res) => {
} }
logger.success(`Created Droid account: ${account.name} (${account.id})`) logger.success(`Created Droid account: ${account.name} (${account.id})`)
return res.json({ success: true, data: account }) const responseAccount = formatSubscriptionExpiry(account)
return res.json({ success: true, data: responseAccount })
} catch (error) { } catch (error) {
logger.error('Failed to create Droid account:', error) logger.error('Failed to create Droid account:', error)
return res.status(500).json({ error: 'Failed to create Droid account', message: error.message }) return res.status(500).json({ error: 'Failed to create Droid account', message: error.message })
@@ -8948,7 +9003,8 @@ router.put('/droid-accounts/:id', authenticateAdmin, async (req, res) => {
} }
} }
return res.json({ success: true, data: account }) const responseAccount = formatSubscriptionExpiry(account)
return res.json({ success: true, data: responseAccount })
} catch (error) { } catch (error) {
logger.error(`Failed to update Droid account ${req.params.id}:`, error) logger.error(`Failed to update Droid account ${req.params.id}:`, error)
return res.status(500).json({ error: 'Failed to update Droid account', message: error.message }) return res.status(500).json({ error: 'Failed to update Droid account', message: error.message })