100 lines
4.7 KiB
TypeScript
100 lines
4.7 KiB
TypeScript
// src/app/api/monitor/status/route.ts
|
||
import { NextRequest, NextResponse } from 'next/server'
|
||
import { getDb } from '@/lib/db'
|
||
import { initDatabase } from '@/lib/db-schema'
|
||
import { getCurrentUser } from '@/lib/auth'
|
||
import { hasPermission } from '@/lib/permissions'
|
||
import { getSetting } from '@/lib/monitor/settings-manager'
|
||
|
||
export async function GET(request: NextRequest) {
|
||
try {
|
||
initDatabase()
|
||
const user = await getCurrentUser()
|
||
if (!user || !hasPermission(user, 'monitor:read')) {
|
||
return NextResponse.json({ error: '权限不足' }, { status: 403 })
|
||
}
|
||
const db = getDb()
|
||
const enabled = getSetting('monitor.enabled') === 'true'
|
||
const lastTick = db.prepare("SELECT created_at, details FROM monitor_logs WHERE action = 'tick_complete' ORDER BY id DESC LIMIT 1").get() as { created_at: string; details: string } | undefined
|
||
|
||
// 使用 UTC+8 日期(数据库中 created_at 是 UTC+8)
|
||
const now = new Date()
|
||
const todayStart = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')} 00:00:00`
|
||
const todayProcessed = (db.prepare("SELECT COUNT(*) as c FROM monitor_logs WHERE action = 'ticket_created' AND created_at >= ?").get(todayStart) as { c: number }).c
|
||
const totalProcessed = (db.prepare("SELECT COUNT(*) as c FROM monitor_logs WHERE action = 'ticket_created'").get() as { c: number }).c
|
||
// 最近一次 tick 的错误状态:action='error' → 1;tick_complete → 该 tick 的 errors 数;无记录 → 0
|
||
const lastLog = db.prepare(
|
||
"SELECT action, details FROM monitor_logs WHERE action IN ('error', 'tick_complete') ORDER BY id DESC LIMIT 1"
|
||
).get() as { action: string; details: string | null } | undefined
|
||
let errorCount = 0
|
||
if (lastLog) {
|
||
if (lastLog.action === 'error') {
|
||
errorCount = 1
|
||
} else {
|
||
try { errorCount = JSON.parse(lastLog.details || '{}').errors || 0 } catch { errorCount = 0 }
|
||
}
|
||
}
|
||
const ongoingFaultsCount = (db.prepare("SELECT COUNT(*) as c FROM fault_records WHERE status = 'ongoing'").get() as { c: number }).c
|
||
|
||
// 获取最近的活动(tick_complete 或 error)
|
||
const lastActivity = db.prepare("SELECT created_at, action FROM monitor_logs WHERE action IN ('tick_complete', 'error') ORDER BY id DESC LIMIT 1").get() as { created_at: string; action: string } | undefined
|
||
|
||
let workerHealth = 'unknown'
|
||
if (enabled && lastActivity) {
|
||
const elapsed = Date.now() - new Date(lastActivity.created_at.replace(' ', 'T') + '+08:00').getTime()
|
||
if (elapsed < 180_000) {
|
||
workerHealth = lastActivity.action === 'error' ? 'error' : 'healthy'
|
||
} else {
|
||
workerHealth = 'unresponsive'
|
||
}
|
||
} else if (!enabled) {
|
||
workerHealth = 'stopped'
|
||
}
|
||
|
||
const lastError = db.prepare("SELECT details, created_at FROM monitor_logs WHERE action = 'error' ORDER BY id DESC LIMIT 1").get() as { details: string; created_at: string } | undefined
|
||
|
||
// 今日处理详情:从 monitor_logs 获取今天的工单创建记录
|
||
const todayProcessedDetails = db.prepare(
|
||
"SELECT details, created_at FROM monitor_logs WHERE action = 'ticket_created' AND created_at >= ? ORDER BY id DESC LIMIT 20"
|
||
).all(todayStart) as { details: string; created_at: string }[]
|
||
|
||
// 进行中故障详情
|
||
const ongoingFaultsDetails = db.prepare(
|
||
"SELECT * FROM fault_records WHERE status = 'ongoing' ORDER BY fault_time DESC LIMIT 20"
|
||
).all() as Record<string, unknown>[]
|
||
|
||
// 最近错误列表
|
||
const recentErrors = db.prepare(
|
||
"SELECT details, created_at FROM monitor_logs WHERE action = 'error' ORDER BY id DESC LIMIT 10"
|
||
).all() as { details: string; created_at: string }[]
|
||
|
||
return NextResponse.json({
|
||
enabled,
|
||
status: enabled ? 'running' : 'stopped',
|
||
workerHealth,
|
||
lastCheck: lastTick?.created_at || null,
|
||
stats: {
|
||
totalProcessed,
|
||
todayProcessed,
|
||
errors: errorCount,
|
||
lastError: lastError?.details || null,
|
||
lastErrorTime: lastError?.created_at || null,
|
||
},
|
||
ongoingFaults: ongoingFaultsCount,
|
||
details: {
|
||
todayProcessed: todayProcessedDetails.map(d => {
|
||
try { return { ...JSON.parse(d.details), created_at: d.created_at } }
|
||
catch { return { details: d.details, created_at: d.created_at } }
|
||
}),
|
||
ongoingFaults: ongoingFaultsDetails,
|
||
recentErrors: recentErrors.map(d => {
|
||
try { return { ...JSON.parse(d.details), created_at: d.created_at } }
|
||
catch { return { details: d.details, created_at: d.created_at } }
|
||
}),
|
||
},
|
||
})
|
||
} catch (e) {
|
||
return NextResponse.json({ error: e instanceof Error ? e.message : 'Internal error' }, { status: 500 })
|
||
}
|
||
}
|