Files
users/app/api/monitoring/route.ts

326 lines
8.8 KiB
TypeScript

import { NextRequest, NextResponse } from 'next/server'
import { getMongoClient } from '@/lib/mongodb'
// 获取数据库详细状态
async function getDatabaseStats() {
try {
const client = await getMongoClient()
const adminDb = client.db('admin')
// 获取服务器状态
const serverStatus = await adminDb.command({ serverStatus: 1 })
// 获取所有KR_开头的数据库
const dbList = await adminDb.admin().listDatabases()
const krDatabases = dbList.databases.filter((db: any) =>
db.name.startsWith('KR')
)
// 统计各数据库信息
const databaseDetails = await Promise.all(
krDatabases.slice(0, 10).map(async (db: any) => {
try {
const database = client.db(db.name)
const stats = await database.command({ dbStats: 1 })
const collections = await database.listCollections().toArray()
return {
name: db.name,
sizeGB: (db.sizeOnDisk / (1024 * 1024 * 1024)).toFixed(2),
collections: collections.length,
objects: stats.objects || 0,
indexes: stats.indexes || 0,
avgObjSize: stats.avgObjSize || 0
}
} catch (e) {
return {
name: db.name,
sizeGB: (db.sizeOnDisk / (1024 * 1024 * 1024)).toFixed(2),
collections: 0,
objects: 0,
indexes: 0,
error: String(e)
}
}
})
)
return {
success: true,
server: {
version: serverStatus.version,
uptime: serverStatus.uptime,
uptimeHours: Math.floor(serverStatus.uptime / 3600),
host: serverStatus.host,
connections: {
current: serverStatus.connections?.current || 0,
available: serverStatus.connections?.available || 0,
totalCreated: serverStatus.connections?.totalCreated || 0
},
memory: {
resident: serverStatus.mem?.resident || 0,
virtual: serverStatus.mem?.virtual || 0,
mapped: serverStatus.mem?.mapped || 0
},
network: {
bytesIn: serverStatus.network?.bytesIn || 0,
bytesOut: serverStatus.network?.bytesOut || 0,
numRequests: serverStatus.network?.numRequests || 0
},
opcounters: {
insert: serverStatus.opcounters?.insert || 0,
query: serverStatus.opcounters?.query || 0,
update: serverStatus.opcounters?.update || 0,
delete: serverStatus.opcounters?.delete || 0
}
},
databases: {
total: krDatabases.length,
totalSizeGB: krDatabases.reduce((sum: number, db: any) =>
sum + db.sizeOnDisk / (1024 * 1024 * 1024), 0
).toFixed(2),
details: databaseDetails
}
}
} catch (error) {
return {
success: false,
error: String(error),
server: null,
databases: null
}
}
}
// 获取健康检查
async function getHealthStatus() {
const services = []
// 检查MongoDB
try {
const client = await getMongoClient()
const start = Date.now()
await client.db('admin').command({ ping: 1 })
const latency = Date.now() - start
services.push({
name: 'MongoDB',
status: latency < 100 ? 'healthy' : latency < 500 ? 'degraded' : 'unhealthy',
latency: `${latency}ms`,
message: latency < 100 ? '运行正常' : '响应较慢'
})
} catch (e) {
services.push({
name: 'MongoDB',
status: 'unhealthy',
latency: '-',
message: String(e)
})
}
// 检查卡若AI网关
try {
const gatewayUrl = process.env.GATEWAY_URL || 'http://localhost:8000'
const start = Date.now()
const res = await fetch(`${gatewayUrl}/health`, {
signal: AbortSignal.timeout(5000)
})
const latency = Date.now() - start
services.push({
name: '卡若AI网关',
status: res.ok ? 'healthy' : 'degraded',
latency: `${latency}ms`,
message: res.ok ? '运行正常' : '响应异常'
})
} catch (e) {
services.push({
name: '卡若AI网关',
status: 'unhealthy',
latency: '-',
message: '无法连接'
})
}
// 检查飞书服务
try {
const gatewayUrl = process.env.GATEWAY_URL || 'http://localhost:8000'
const res = await fetch(`${gatewayUrl}/feishu/test`, {
signal: AbortSignal.timeout(5000)
})
const data = await res.json()
services.push({
name: '飞书机器人',
status: data.status === 'success' ? 'healthy' : 'degraded',
latency: '-',
message: data.message || '未知状态'
})
} catch (e) {
services.push({
name: '飞书机器人',
status: 'unhealthy',
latency: '-',
message: '未配置或无法连接'
})
}
return {
success: true,
services,
overall: services.every(s => s.status === 'healthy') ? 'healthy' :
services.some(s => s.status === 'unhealthy') ? 'unhealthy' : 'degraded'
}
}
// 获取告警信息
async function getAlerts() {
// 从数据库状态生成告警
const alerts = []
try {
const client = await getMongoClient()
const adminDb = client.db('admin')
const serverStatus = await adminDb.command({ serverStatus: 1 })
// 检查连接数
const connCurrent = serverStatus.connections?.current || 0
const connAvailable = serverStatus.connections?.available || 0
if (connCurrent > connAvailable * 0.8) {
alerts.push({
id: 'conn-high',
type: 'warning',
message: `MongoDB连接数较高 (${connCurrent}/${connAvailable})`,
time: new Date().toISOString(),
status: 'active'
})
}
// 检查内存
const memResident = serverStatus.mem?.resident || 0
if (memResident > 8000) { // 8GB
alerts.push({
id: 'mem-high',
type: 'warning',
message: `MongoDB内存使用较高 (${(memResident/1024).toFixed(1)}GB)`,
time: new Date().toISOString(),
status: 'active'
})
}
} catch (e) {
alerts.push({
id: 'mongo-error',
type: 'error',
message: `MongoDB连接失败: ${String(e)}`,
time: new Date().toISOString(),
status: 'active'
})
}
// 默认告警(如果没有问题)
if (alerts.length === 0) {
alerts.push({
id: 'all-ok',
type: 'info',
message: '系统运行正常,无告警',
time: new Date().toISOString(),
status: 'resolved'
})
}
return {
success: true,
alerts,
activeCount: alerts.filter(a => a.status === 'active').length
}
}
// 获取业务指标
async function getBusinessMetrics() {
try {
const client = await getMongoClient()
// 从KR.用户估值获取统计
const krDb = client.db('KR')
const userCollection = krDb.collection('用户估值')
const totalUsers = await userCollection.estimatedDocumentCount()
// 获取用户等级分布(采样)
const levelStats = await userCollection.aggregate([
{ $sample: { size: 10000 } },
{ $match: { user_level: { $exists: true } } },
{ $group: { _id: '$user_level', count: { $sum: 1 } } }
], { maxTimeMS: 5000 }).toArray()
// 计算各等级占比
const levelDistribution = levelStats.map(l => ({
level: l._id || '未知',
count: l.count,
percentage: ((l.count / 10000) * 100).toFixed(1)
}))
return {
success: true,
metrics: {
totalUsers,
totalUsersFormatted: totalUsers >= 1000000000
? `${(totalUsers/1000000000).toFixed(2)}B`
: `${(totalUsers/1000000).toFixed(1)}M`,
levelDistribution,
lastUpdated: new Date().toISOString()
}
}
} catch (error) {
return {
success: false,
error: String(error)
}
}
}
export async function GET(request: NextRequest) {
const { searchParams } = new URL(request.url)
const action = searchParams.get('action')
try {
switch (action) {
case 'databases':
return NextResponse.json(await getDatabaseStats())
case 'health':
return NextResponse.json(await getHealthStatus())
case 'alerts':
return NextResponse.json(await getAlerts())
case 'metrics':
return NextResponse.json(await getBusinessMetrics())
default:
// 返回综合状态
const [dbStats, health, alerts, metrics] = await Promise.all([
getDatabaseStats(),
getHealthStatus(),
getAlerts(),
getBusinessMetrics()
])
return NextResponse.json({
success: true,
timestamp: new Date().toISOString(),
database: dbStats,
health,
alerts,
metrics
})
}
} catch (error) {
return NextResponse.json({
success: false,
error: String(error)
}, { status: 500 })
}
}