UNPKG

thrilled-be-core

Version:

Core Express backend package with middleware, logging, security, and base application setup

130 lines (129 loc) 4.48 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.GracefulShutdown = void 0; const Logger_1 = require("../logging/Logger"); class GracefulShutdown { server; logger; config; shutdownHandlers = []; isShuttingDown = false; constructor(config = {}, logger) { this.config = { enabled: true, timeout: 30000, // 30 seconds signals: ['SIGTERM', 'SIGINT', 'SIGUSR2'], ...config, }; this.logger = logger || Logger_1.Logger.create({ level: 'info' }); if (this.config.enabled) { this.setupSignalHandlers(); } } /** * Set the HTTP server instance */ setServer(server) { this.server = server; } /** * Add a shutdown handler */ addHandler(handler) { this.shutdownHandlers.push(handler); } /** * Setup signal handlers for graceful shutdown */ setupSignalHandlers() { this.config.signals.forEach((signal) => { process.on(signal, () => { this.logger.info(`Received ${signal}, starting graceful shutdown...`); this.shutdown(); }); }); // Handle uncaught exceptions process.on('uncaughtException', (error) => { this.logger.error(error, { context: 'UncaughtException' }); this.shutdown(1); }); // Handle unhandled promise rejections process.on('unhandledRejection', (reason, promise) => { this.logger.error(new Error(`Unhandled Rejection: ${reason}`), { context: 'UnhandledRejection', promise: promise.toString(), }); this.shutdown(1); }); } /** * Perform graceful shutdown */ async shutdown(exitCode = 0) { if (this.isShuttingDown) { this.logger.warn('Shutdown already in progress'); return; } this.isShuttingDown = true; const startTime = Date.now(); try { // Set a timeout for the entire shutdown process const shutdownTimeout = setTimeout(() => { this.logger.error('Shutdown timeout exceeded, forcing exit'); process.exit(1); }, this.config.timeout); // Run custom shutdown handlers if (this.shutdownHandlers.length > 0) { this.logger.info(`Running ${this.shutdownHandlers.length} shutdown handlers...`); await Promise.all(this.shutdownHandlers.map(async (handler, index) => { try { await handler(); this.logger.debug(`Shutdown handler ${index + 1} completed`); } catch (error) { this.logger.error(error, { context: `ShutdownHandler-${index + 1}`, }); } })); } // Close HTTP server if (this.server) { this.logger.info('Closing HTTP server...'); await new Promise((resolve, reject) => { if (this.server) { this.server.close((error) => { if (error) { this.logger.error(error, { context: 'ServerClose' }); reject(error); } else { this.logger.info('HTTP server closed'); resolve(); } }); } else { resolve(); } }); } clearTimeout(shutdownTimeout); const shutdownTime = Date.now() - startTime; this.logger.info(`Graceful shutdown completed in ${shutdownTime}ms`); process.exit(exitCode); } catch (error) { this.logger.error(error, { context: 'GracefulShutdown' }); process.exit(1); } } /** * Force shutdown (for testing or emergency situations) */ forceShutdown(exitCode = 0) { this.logger.warn('Force shutdown initiated'); process.exit(exitCode); } } exports.GracefulShutdown = GracefulShutdown;