/** * @cc-platform/server/shutdown — Graceful shutdown handler * * Extracted from both servers' SIGINT/SIGTERM handling. * Inventory-Manager has the more thorough implementation (SIGTERM, * unhandled rejection, uncaught exception); remoteturtle only has SIGINT. * This module provides the comprehensive version for all services. * * Usage: * const { setupGracefulShutdown } = require('@cc-platform/server/shutdown'); * setupGracefulShutdown({ * cleanup: [ * () => wsManager.close(), * () => db.close(), * ], * }); */ 'use strict'; /** * Set up graceful shutdown handlers for SIGINT, SIGTERM, and optionally * unhandled rejections and uncaught exceptions. * * @param {Object} [opts={}] - Options * @param {Function[]} [opts.cleanup=[]] - Array of cleanup functions (sync or async) * @param {boolean} [opts.catchUnhandled=true] - Whether to handle unhandled rejections/exceptions * @param {string} [opts.serviceName] - Service name for log messages * @returns {{ shutdown: Function }} Object with manual shutdown trigger */ function setupGracefulShutdown(opts = {}) { const cleanupFns = opts.cleanup || []; const serviceName = opts.serviceName || 'platform'; let shuttingDown = false; async function shutdown(signal) { if (shuttingDown) return; shuttingDown = true; console.log(`\n[${serviceName}] ${signal} received, cleaning up...`); for (const fn of cleanupFns) { try { await fn(); } catch (e) { console.error(`[${serviceName}] Cleanup error:`, e.message); } } console.log(`[${serviceName}] Cleanup complete`); process.exit(0); } process.on('SIGINT', () => shutdown('SIGINT')); process.on('SIGTERM', () => shutdown('SIGTERM')); if (opts.catchUnhandled !== false) { process.on('unhandledRejection', (reason) => { console.error(`[${serviceName}] Unhandled rejection:`, reason); }); process.on('uncaughtException', (err) => { console.error(`[${serviceName}] Uncaught exception:`, err); shutdown('uncaughtException'); }); } return { shutdown }; } module.exports = { setupGracefulShutdown };