mirror of
https://github.com/Wei-Shaw/claude-relay-service.git
synced 2026-01-22 16:43:35 +00:00
恢复并保存本地修改:仪表盘服务账户分类显示、WindowCountdown组件等功能
This commit is contained in:
@@ -2300,6 +2300,7 @@ router.get('/dashboard', authenticateAdmin, async (req, res) => {
|
||||
claudeAccounts,
|
||||
claudeConsoleAccounts,
|
||||
geminiAccounts,
|
||||
bedrockAccountsResult,
|
||||
todayStats,
|
||||
systemAverages,
|
||||
realtimeMetrics
|
||||
@@ -2309,11 +2310,15 @@ router.get('/dashboard', authenticateAdmin, async (req, res) => {
|
||||
claudeAccountService.getAllAccounts(),
|
||||
claudeConsoleAccountService.getAllAccounts(),
|
||||
geminiAccountService.getAllAccounts(),
|
||||
bedrockAccountService.getAllAccounts(),
|
||||
redis.getTodayStats(),
|
||||
redis.getSystemAverages(),
|
||||
redis.getRealtimeSystemMetrics()
|
||||
])
|
||||
|
||||
// 处理Bedrock账户数据
|
||||
const bedrockAccounts = bedrockAccountsResult.success ? bedrockAccountsResult.data : []
|
||||
|
||||
// 计算使用统计(统一使用allTokens)
|
||||
const totalTokensUsed = apiKeys.reduce(
|
||||
(sum, key) => sum + (key.usage?.total?.allTokens || 0),
|
||||
@@ -2345,34 +2350,167 @@ router.get('/dashboard', authenticateAdmin, async (req, res) => {
|
||||
)
|
||||
|
||||
const activeApiKeys = apiKeys.filter((key) => key.isActive).length
|
||||
const activeClaudeAccounts = claudeAccounts.filter(
|
||||
(acc) => acc.isActive && acc.status === 'active'
|
||||
|
||||
// Claude账户统计 - 根据账户管理页面的判断逻辑
|
||||
const normalClaudeAccounts = claudeAccounts.filter(
|
||||
(acc) =>
|
||||
acc.isActive &&
|
||||
acc.status !== 'blocked' &&
|
||||
acc.status !== 'unauthorized' &&
|
||||
acc.schedulable !== false
|
||||
).length
|
||||
const abnormalClaudeAccounts = claudeAccounts.filter(
|
||||
(acc) => !acc.isActive || acc.status === 'blocked' || acc.status === 'unauthorized'
|
||||
).length
|
||||
const pausedClaudeAccounts = claudeAccounts.filter(
|
||||
(acc) =>
|
||||
acc.schedulable === false &&
|
||||
acc.isActive &&
|
||||
acc.status !== 'blocked' &&
|
||||
acc.status !== 'unauthorized'
|
||||
).length
|
||||
const rateLimitedClaudeAccounts = claudeAccounts.filter(
|
||||
(acc) => acc.rateLimitStatus && acc.rateLimitStatus.isRateLimited
|
||||
).length
|
||||
const activeClaudeConsoleAccounts = claudeConsoleAccounts.filter(
|
||||
(acc) => acc.isActive && acc.status === 'active'
|
||||
|
||||
// Claude Console账户统计
|
||||
const normalClaudeConsoleAccounts = claudeConsoleAccounts.filter(
|
||||
(acc) =>
|
||||
acc.isActive &&
|
||||
acc.status !== 'blocked' &&
|
||||
acc.status !== 'unauthorized' &&
|
||||
acc.schedulable !== false
|
||||
).length
|
||||
const abnormalClaudeConsoleAccounts = claudeConsoleAccounts.filter(
|
||||
(acc) => !acc.isActive || acc.status === 'blocked' || acc.status === 'unauthorized'
|
||||
).length
|
||||
const pausedClaudeConsoleAccounts = claudeConsoleAccounts.filter(
|
||||
(acc) =>
|
||||
acc.schedulable === false &&
|
||||
acc.isActive &&
|
||||
acc.status !== 'blocked' &&
|
||||
acc.status !== 'unauthorized'
|
||||
).length
|
||||
const rateLimitedClaudeConsoleAccounts = claudeConsoleAccounts.filter(
|
||||
(acc) => acc.rateLimitStatus && acc.rateLimitStatus.isRateLimited
|
||||
).length
|
||||
const activeGeminiAccounts = geminiAccounts.filter(
|
||||
(acc) => acc.isActive && acc.status === 'active'
|
||||
|
||||
// Gemini账户统计
|
||||
const normalGeminiAccounts = geminiAccounts.filter(
|
||||
(acc) =>
|
||||
acc.isActive &&
|
||||
acc.status !== 'blocked' &&
|
||||
acc.status !== 'unauthorized' &&
|
||||
acc.schedulable !== false
|
||||
).length
|
||||
const abnormalGeminiAccounts = geminiAccounts.filter(
|
||||
(acc) => !acc.isActive || acc.status === 'blocked' || acc.status === 'unauthorized'
|
||||
).length
|
||||
const pausedGeminiAccounts = geminiAccounts.filter(
|
||||
(acc) =>
|
||||
acc.schedulable === false &&
|
||||
acc.isActive &&
|
||||
acc.status !== 'blocked' &&
|
||||
acc.status !== 'unauthorized'
|
||||
).length
|
||||
const rateLimitedGeminiAccounts = geminiAccounts.filter(
|
||||
(acc) => acc.rateLimitStatus === 'limited'
|
||||
).length
|
||||
|
||||
// Bedrock账户统计
|
||||
const normalBedrockAccounts = bedrockAccounts.filter(
|
||||
(acc) =>
|
||||
acc.isActive &&
|
||||
acc.status !== 'blocked' &&
|
||||
acc.status !== 'unauthorized' &&
|
||||
acc.schedulable !== false
|
||||
).length
|
||||
const abnormalBedrockAccounts = bedrockAccounts.filter(
|
||||
(acc) => !acc.isActive || acc.status === 'blocked' || acc.status === 'unauthorized'
|
||||
).length
|
||||
const pausedBedrockAccounts = bedrockAccounts.filter(
|
||||
(acc) =>
|
||||
acc.schedulable === false &&
|
||||
acc.isActive &&
|
||||
acc.status !== 'blocked' &&
|
||||
acc.status !== 'unauthorized'
|
||||
).length
|
||||
const rateLimitedBedrockAccounts = bedrockAccounts.filter(
|
||||
(acc) => acc.rateLimitStatus && acc.rateLimitStatus.isRateLimited
|
||||
).length
|
||||
|
||||
const dashboard = {
|
||||
overview: {
|
||||
totalApiKeys: apiKeys.length,
|
||||
activeApiKeys,
|
||||
// 总账户统计(所有平台)
|
||||
totalAccounts:
|
||||
claudeAccounts.length +
|
||||
claudeConsoleAccounts.length +
|
||||
geminiAccounts.length +
|
||||
bedrockAccounts.length,
|
||||
normalAccounts:
|
||||
normalClaudeAccounts +
|
||||
normalClaudeConsoleAccounts +
|
||||
normalGeminiAccounts +
|
||||
normalBedrockAccounts,
|
||||
abnormalAccounts:
|
||||
abnormalClaudeAccounts +
|
||||
abnormalClaudeConsoleAccounts +
|
||||
abnormalGeminiAccounts +
|
||||
abnormalBedrockAccounts,
|
||||
pausedAccounts:
|
||||
pausedClaudeAccounts +
|
||||
pausedClaudeConsoleAccounts +
|
||||
pausedGeminiAccounts +
|
||||
pausedBedrockAccounts,
|
||||
rateLimitedAccounts:
|
||||
rateLimitedClaudeAccounts +
|
||||
rateLimitedClaudeConsoleAccounts +
|
||||
rateLimitedGeminiAccounts +
|
||||
rateLimitedBedrockAccounts,
|
||||
// 各平台详细统计
|
||||
accountsByPlatform: {
|
||||
claude: {
|
||||
total: claudeAccounts.length,
|
||||
normal: normalClaudeAccounts,
|
||||
abnormal: abnormalClaudeAccounts,
|
||||
paused: pausedClaudeAccounts,
|
||||
rateLimited: rateLimitedClaudeAccounts
|
||||
},
|
||||
'claude-console': {
|
||||
total: claudeConsoleAccounts.length,
|
||||
normal: normalClaudeConsoleAccounts,
|
||||
abnormal: abnormalClaudeConsoleAccounts,
|
||||
paused: pausedClaudeConsoleAccounts,
|
||||
rateLimited: rateLimitedClaudeConsoleAccounts
|
||||
},
|
||||
gemini: {
|
||||
total: geminiAccounts.length,
|
||||
normal: normalGeminiAccounts,
|
||||
abnormal: abnormalGeminiAccounts,
|
||||
paused: pausedGeminiAccounts,
|
||||
rateLimited: rateLimitedGeminiAccounts
|
||||
},
|
||||
bedrock: {
|
||||
total: bedrockAccounts.length,
|
||||
normal: normalBedrockAccounts,
|
||||
abnormal: abnormalBedrockAccounts,
|
||||
paused: pausedBedrockAccounts,
|
||||
rateLimited: rateLimitedBedrockAccounts
|
||||
}
|
||||
},
|
||||
// 保留旧字段以兼容
|
||||
activeAccounts:
|
||||
normalClaudeAccounts +
|
||||
normalClaudeConsoleAccounts +
|
||||
normalGeminiAccounts +
|
||||
normalBedrockAccounts,
|
||||
totalClaudeAccounts: claudeAccounts.length + claudeConsoleAccounts.length,
|
||||
activeClaudeAccounts: activeClaudeAccounts + activeClaudeConsoleAccounts,
|
||||
activeClaudeAccounts: normalClaudeAccounts + normalClaudeConsoleAccounts,
|
||||
rateLimitedClaudeAccounts: rateLimitedClaudeAccounts + rateLimitedClaudeConsoleAccounts,
|
||||
totalGeminiAccounts: geminiAccounts.length,
|
||||
activeGeminiAccounts,
|
||||
activeGeminiAccounts: normalGeminiAccounts,
|
||||
rateLimitedGeminiAccounts,
|
||||
totalTokensUsed,
|
||||
totalRequestsUsed,
|
||||
@@ -2403,8 +2541,8 @@ router.get('/dashboard', authenticateAdmin, async (req, res) => {
|
||||
},
|
||||
systemHealth: {
|
||||
redisConnected: redis.isConnected,
|
||||
claudeAccountsHealthy: activeClaudeAccounts + activeClaudeConsoleAccounts > 0,
|
||||
geminiAccountsHealthy: activeGeminiAccounts > 0,
|
||||
claudeAccountsHealthy: normalClaudeAccounts + normalClaudeConsoleAccounts > 0,
|
||||
geminiAccountsHealthy: normalGeminiAccounts > 0,
|
||||
uptime: process.uptime()
|
||||
},
|
||||
systemTimezone: config.system.timezoneOffset || 8
|
||||
|
||||
@@ -213,12 +213,45 @@ class ApiKeyService {
|
||||
if (key.rateLimitWindow > 0) {
|
||||
const requestCountKey = `rate_limit:requests:${key.id}`
|
||||
const tokenCountKey = `rate_limit:tokens:${key.id}`
|
||||
const windowStartKey = `rate_limit:window_start:${key.id}`
|
||||
|
||||
key.currentWindowRequests = parseInt((await client.get(requestCountKey)) || '0')
|
||||
key.currentWindowTokens = parseInt((await client.get(tokenCountKey)) || '0')
|
||||
|
||||
// 获取窗口开始时间和计算剩余时间
|
||||
const windowStart = await client.get(windowStartKey)
|
||||
if (windowStart) {
|
||||
const now = Date.now()
|
||||
const windowStartTime = parseInt(windowStart)
|
||||
const windowDuration = key.rateLimitWindow * 60 * 1000 // 转换为毫秒
|
||||
const windowEndTime = windowStartTime + windowDuration
|
||||
|
||||
// 如果窗口还有效
|
||||
if (now < windowEndTime) {
|
||||
key.windowStartTime = windowStartTime
|
||||
key.windowEndTime = windowEndTime
|
||||
key.windowRemainingSeconds = Math.max(0, Math.floor((windowEndTime - now) / 1000))
|
||||
} else {
|
||||
// 窗口已过期,下次请求会重置
|
||||
key.windowStartTime = null
|
||||
key.windowEndTime = null
|
||||
key.windowRemainingSeconds = 0
|
||||
// 重置计数为0,因为窗口已过期
|
||||
key.currentWindowRequests = 0
|
||||
key.currentWindowTokens = 0
|
||||
}
|
||||
} else {
|
||||
// 窗口还未开始(没有任何请求)
|
||||
key.windowStartTime = null
|
||||
key.windowEndTime = null
|
||||
key.windowRemainingSeconds = null
|
||||
}
|
||||
} else {
|
||||
key.currentWindowRequests = 0
|
||||
key.currentWindowTokens = 0
|
||||
key.windowStartTime = null
|
||||
key.windowEndTime = null
|
||||
key.windowRemainingSeconds = null
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user