Files
Momento/memento-note/app/api/admin/health/route.ts
Antigravity 8950e83db5
Some checks failed
CI / Deploy production (on server) (push) Has been cancelled
CI / Lint, Test & Build (push) Has been cancelled
feat: P0 backup system (WAL+snapshot+restore+verify), monitoring stack, admin health API
2026-05-17 14:13:01 +00:00

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,
})
}