Kraft102's picture
Initial deployment - WidgeTDC Cortex Backend v2.1.0
529090e
import { Router } from 'express';
import { neo4jService } from '../database/Neo4jService';
import { checkPrismaConnection, prisma } from '../database/prisma';
const router = Router();
/**
* Overall system health check
*/
router.get('/health', async (req, res) => {
const health = {
status: 'healthy',
timestamp: new Date().toISOString(),
services: {
database: 'unknown',
neo4j: 'unknown',
redis: 'unknown',
},
uptime: process.uptime(),
memory: process.memoryUsage(),
};
try {
// Check Prisma/PostgreSQL
const prismaHealthy = await checkPrismaConnection();
health.services.database = prismaHealthy ? 'healthy' : 'unhealthy';
if (!prismaHealthy) health.status = 'degraded';
} catch {
health.services.database = 'unhealthy';
health.status = 'degraded';
}
try {
// Check Neo4j
await neo4jService.connect();
const neo4jHealthy = await neo4jService.healthCheck();
health.services.neo4j = neo4jHealthy ? 'healthy' : 'unhealthy';
await neo4jService.disconnect();
if (!neo4jHealthy) {
health.status = 'degraded';
}
} catch (error) {
health.services.neo4j = 'unhealthy';
health.status = 'degraded';
}
// Check Redis
if (process.env.REDIS_URL) {
// Redis URL is configured but ioredis client is not yet installed
// Once ioredis is installed, this can be updated to perform actual health check
health.services.redis = 'configured_but_client_unavailable';
} else {
health.services.redis = 'not_configured';
}
const statusCode = health.status === 'healthy' ? 200 : 503;
res.status(statusCode).json(health);
});
/**
* Database-specific health check
*/
router.get('/health/database', async (req, res) => {
try {
const prismaHealthy = await checkPrismaConnection();
if (!prismaHealthy) {
return res.status(503).json({
status: 'unhealthy',
error: 'Prisma unreachable',
});
}
const result = await prisma.$queryRaw`SELECT 1 as test`;
res.json({
status: 'healthy',
type: 'PostgreSQL (Prisma)',
tables: 'n/a',
test: (result as any[])[0]?.test === 1,
});
} catch (error) {
res.status(503).json({
status: 'unhealthy',
error: String(error),
});
}
});
/**
* Neo4j-specific health check
*/
router.get('/health/neo4j', async (req, res) => {
try {
await neo4jService.connect();
const healthy = await neo4jService.healthCheck();
if (healthy) {
const stats = await neo4jService.runQuery('MATCH (n) RETURN count(n) as nodeCount');
await neo4jService.disconnect();
res.json({
status: 'healthy',
connected: true,
nodeCount: stats[0]?.nodeCount || 0,
});
} else {
throw new Error('Health check failed');
}
} catch (error) {
res.status(503).json({
status: 'unhealthy',
connected: false,
error: String(error),
});
}
});
/**
* Readiness check (for Kubernetes)
*/
router.get('/ready', async (req, res) => {
try {
// Use Prisma connection check instead of SQLite
const prismaHealthy = await checkPrismaConnection();
if (!prismaHealthy) {
throw new Error('Database not ready');
}
res.json({
status: 'ready',
timestamp: new Date().toISOString(),
});
} catch (error) {
res.status(503).json({
status: 'not_ready',
error: String(error),
});
}
});
/**
* Liveness check (for Kubernetes)
*/
router.get('/live', (req, res) => {
res.json({
status: 'alive',
timestamp: new Date().toISOString(),
uptime: process.uptime(),
});
});
export default router;