92 lines
2.8 KiB
TypeScript
92 lines
2.8 KiB
TypeScript
import { NextResponse } from 'next/server'
|
|
import { getPrisma } from '@/lib/prisma'
|
|
import { redis } from '@/lib/redis'
|
|
|
|
export const dynamic = 'force-dynamic'
|
|
|
|
export async function GET() {
|
|
const start = Date.now()
|
|
const checks: Record<string, { status: string; latency?: string; error?: string; [k: string]: unknown }> = {}
|
|
|
|
// Database check
|
|
try {
|
|
const dbStart = Date.now()
|
|
const prisma = getPrisma()
|
|
const [noteCount, notebookCount, userCount] = await Promise.all([
|
|
prisma.note.count(),
|
|
prisma.notebook.count(),
|
|
prisma.user.count(),
|
|
])
|
|
checks.database = {
|
|
status: 'healthy',
|
|
latency: `${Date.now() - dbStart}ms`,
|
|
notes: noteCount,
|
|
notebooks: notebookCount,
|
|
users: userCount,
|
|
}
|
|
} catch (e) {
|
|
checks.database = { status: 'unhealthy', error: e instanceof Error ? e.message : 'Unknown error' }
|
|
}
|
|
|
|
// Redis check
|
|
try {
|
|
const redisStart = Date.now()
|
|
await redis.ping()
|
|
const info = await redis.info('memory')
|
|
const dbSize = await redis.dbsize()
|
|
const memMatch = info.match(/used_memory_human:(\S+)/)
|
|
checks.redis = {
|
|
status: 'healthy',
|
|
latency: `${Date.now() - redisStart}ms`,
|
|
keys: dbSize,
|
|
memory: memMatch ? memMatch[1] : 'unknown',
|
|
}
|
|
} catch (e) {
|
|
checks.redis = { status: 'unhealthy', error: e instanceof Error ? e.message : 'Unknown error' }
|
|
}
|
|
|
|
// AI providers check
|
|
try {
|
|
const { getAIProvider, getChatProvider } = await import('@/lib/ai/providers/registry')
|
|
const embeddingProvider = getAIProvider()
|
|
const chatProvider = getChatProvider()
|
|
checks.ai = {
|
|
status: 'configured',
|
|
embedding: { provider: embeddingProvider },
|
|
chat: { provider: chatProvider },
|
|
}
|
|
} catch (e) {
|
|
checks.ai = { status: 'unhealthy', error: e instanceof Error ? e.message : 'Unknown error' }
|
|
}
|
|
|
|
// Disk check
|
|
try {
|
|
const { execSync } = await import('child_process')
|
|
const diskInfo = execSync("df -h /opt/memento | awk 'NR==2{print $2,$3,$4,$5}'").toString().trim()
|
|
const [total, used, available, percent] = diskInfo.split(/\s+/)
|
|
checks.storage = {
|
|
status: parseInt(percent) > 90 ? 'warning' : 'healthy',
|
|
total,
|
|
used,
|
|
available,
|
|
usagePercent: percent,
|
|
}
|
|
} catch {
|
|
checks.storage = { status: 'unknown' }
|
|
}
|
|
|
|
const allHealthy = Object.values(checks).every(c => c.status === 'healthy' || c.status === 'configured')
|
|
const hasDegraded = Object.values(checks).some(c => c.status === 'warning')
|
|
|
|
return NextResponse.json({
|
|
status: allHealthy ? (hasDegraded ? 'degraded' : 'healthy') : 'unhealthy',
|
|
uptime: process.uptime(),
|
|
version: process.env.npm_package_version || '0.2.0',
|
|
timestamp: new Date().toISOString(),
|
|
responseTime: `${Date.now() - start}ms`,
|
|
components: checks,
|
|
}, {
|
|
status: allHealthy ? 200 : 503,
|
|
})
|
|
}
|