Files
Momento/memento-note/app/api/admin/health/route.ts
Antigravity 6a8d0eb0a5
Some checks failed
CI / Lint, Test & Build (push) Failing after 8m1s
CI / Deploy production (on server) (push) Has been cancelled
feat: embedding dimension validation + migration system
- Add /api/admin/embeddings/dimension (GET column dim, POST test model dim)
- Add /api/admin/embeddings/migrate (alter column, clear, re-index)
- Admin form warns on dimension mismatch after save, offers migrate button
- Remove hardcoded 1536 from validate endpoint and embedding service
- Add validateDimension() utility to EmbeddingService
- Fix health route: import prisma correctly, use router instead of missing registry
- i18n keys for dimension warning (EN/FR)
2026-05-19 18:45:50 +00:00

93 lines
2.9 KiB
TypeScript

import { NextResponse } from 'next/server'
import { prisma } 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 [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 { getSystemConfig } = await import('@/lib/config')
const config = await getSystemConfig()
const { resolveAiRoute } = await import('@/lib/ai/router')
const tagsRoute = resolveAiRoute('tags', config)
const chatRoute = resolveAiRoute('chat', config)
checks.ai = {
status: 'configured',
embedding: { provider: tagsRoute.providerType, model: tagsRoute.modelName },
chat: { provider: chatRoute.providerType, model: chatRoute.modelName },
}
} 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,
})
}