#!/usr/bin/env node /** * CHORUS Services Website - Health Check Script * Validates that both Nginx and Next.js are running correctly * Used by Docker HEALTHCHECK instruction */ const http = require('http'); const process = require('process'); const NGINX_PORT = 80; const NEXTJS_PORT = 3000; const TIMEOUT = 5000; // 5 seconds /** * Make HTTP request to check service health * @param {number} port - Port to check * @param {string} path - Path to request * @param {string} service - Service name for logging * @returns {Promise} - True if healthy */ function checkService(port, path, service) { return new Promise((resolve) => { const options = { hostname: 'localhost', port: port, path: path, method: 'GET', timeout: TIMEOUT, headers: { 'User-Agent': 'HealthCheck/1.0' } }; const req = http.request(options, (res) => { const isHealthy = res.statusCode >= 200 && res.statusCode < 400; if (isHealthy) { console.log(`✓ ${service} is healthy (${res.statusCode})`); } else { console.error(`✗ ${service} returned status ${res.statusCode}`); } resolve(isHealthy); }); req.on('error', (error) => { console.error(`✗ ${service} error: ${error.message}`); resolve(false); }); req.on('timeout', () => { console.error(`✗ ${service} timeout after ${TIMEOUT}ms`); req.destroy(); resolve(false); }); req.setTimeout(TIMEOUT); req.end(); }); } /** * Check system resources and limits * @returns {boolean} - True if resources are healthy */ function checkResources() { try { const memUsage = process.memoryUsage(); const memUsedMB = Math.round(memUsage.rss / 1024 / 1024); const memLimit = 128; // 128MB limit as per docker-compose if (memUsedMB > memLimit * 0.9) { console.error(`✗ High memory usage: ${memUsedMB}MB (limit: ${memLimit}MB)`); return false; } console.log(`✓ Memory usage: ${memUsedMB}MB`); return true; } catch (error) { console.error(`✗ Resource check failed: ${error.message}`); return false; } } /** * Main health check function */ async function main() { console.log('CHORUS Website Health Check'); console.log('==========================='); const startTime = Date.now(); try { // Check all services in parallel const [nginxHealthy, nextjsHealthy, resourcesHealthy] = await Promise.all([ checkService(NGINX_PORT, '/health', 'Nginx'), checkService(NEXTJS_PORT, '/', 'Next.js'), Promise.resolve(checkResources()) ]); const allHealthy = nginxHealthy && nextjsHealthy && resourcesHealthy; const duration = Date.now() - startTime; console.log(`\nHealth check completed in ${duration}ms`); if (allHealthy) { console.log('✓ All services are healthy'); process.exit(0); } else { console.error('✗ One or more services are unhealthy'); process.exit(1); } } catch (error) { console.error(`Health check failed: ${error.message}`); process.exit(1); } } // Handle process signals process.on('SIGTERM', () => { console.log('Health check interrupted by SIGTERM'); process.exit(1); }); process.on('SIGINT', () => { console.log('Health check interrupted by SIGINT'); process.exit(1); }); // Run health check main().catch((error) => { console.error(`Unexpected error: ${error.message}`); process.exit(1); });