fix: add crash handlers to catch process-killing exceptions

Process-level uncaughtException/unhandledRejection handlers plus
try/catch around upgrade and connection handlers. This will log
whatever is crashing the server on browser WebSocket connections
before the process dies, instead of silently restarting.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Vantz Stockwell 2026-03-14 00:36:44 -04:00
parent 8207b78b36
commit 8d95fc5652

View File

@ -6,6 +6,15 @@ import { WebSocketServer } from 'ws';
import { TerminalGateway } from './terminal/terminal.gateway'; import { TerminalGateway } from './terminal/terminal.gateway';
import { SftpGateway } from './terminal/sftp.gateway'; import { SftpGateway } from './terminal/sftp.gateway';
// Crash handlers — catch whatever is killing the process
process.on('uncaughtException', (err) => {
console.error(`[FATAL] Uncaught Exception: ${err.message}\n${err.stack}`);
});
process.on('unhandledRejection', (reason: any) => {
console.error(`[FATAL] Unhandled Rejection: ${reason?.message || reason}\n${reason?.stack || ''}`);
});
async function bootstrap() { async function bootstrap() {
const app = await NestFactory.create(AppModule); const app = await NestFactory.create(AppModule);
app.setGlobalPrefix('api'); app.setGlobalPrefix('api');
@ -26,13 +35,21 @@ async function bootstrap() {
const sftpWss = new WebSocketServer({ noServer: true }); const sftpWss = new WebSocketServer({ noServer: true });
terminalWss.on('connection', (ws, req) => { terminalWss.on('connection', (ws, req) => {
console.log(`[WS] Terminal connection established`); try {
terminalGateway.handleConnection(ws, req); console.log(`[WS] Terminal connection established`);
terminalGateway.handleConnection(ws, req);
} catch (err: any) {
console.error(`[FATAL] Terminal handleConnection crashed: ${err.message}\n${err.stack}`);
}
}); });
sftpWss.on('connection', (ws, req) => { sftpWss.on('connection', (ws, req) => {
console.log(`[WS] SFTP connection established`); try {
sftpGateway.handleConnection(ws, req); console.log(`[WS] SFTP connection established`);
sftpGateway.handleConnection(ws, req);
} catch (err: any) {
console.error(`[FATAL] SFTP handleConnection crashed: ${err.message}\n${err.stack}`);
}
}); });
// Remove ALL existing upgrade listeners (WsAdapter's) so we handle upgrades first // Remove ALL existing upgrade listeners (WsAdapter's) so we handle upgrades first
@ -41,24 +58,29 @@ async function bootstrap() {
// Our handler runs first — routes terminal/sftp, passes everything else to WsAdapter // Our handler runs first — routes terminal/sftp, passes everything else to WsAdapter
server.on('upgrade', (req: any, socket: any, head: any) => { server.on('upgrade', (req: any, socket: any, head: any) => {
const pathname = req.url?.split('?')[0]; try {
console.log(`[HTTP-UPGRADE] path=${pathname} socket.destroyed=${socket.destroyed}`); const pathname = req.url?.split('?')[0];
console.log(`[HTTP-UPGRADE] path=${pathname} socket.destroyed=${socket.destroyed}`);
if (pathname === '/api/ws/terminal') { if (pathname === '/api/ws/terminal') {
terminalWss.handleUpgrade(req, socket, head, (ws) => { terminalWss.handleUpgrade(req, socket, head, (ws) => {
console.log(`[WS] Terminal upgrade complete`); console.log(`[WS] Terminal upgrade complete`);
terminalWss.emit('connection', ws, req); terminalWss.emit('connection', ws, req);
}); });
} else if (pathname === '/api/ws/sftp') { } else if (pathname === '/api/ws/sftp') {
sftpWss.handleUpgrade(req, socket, head, (ws) => { sftpWss.handleUpgrade(req, socket, head, (ws) => {
console.log(`[WS] SFTP upgrade complete`); console.log(`[WS] SFTP upgrade complete`);
sftpWss.emit('connection', ws, req); sftpWss.emit('connection', ws, req);
}); });
} else { } else {
// Pass to WsAdapter's original handlers (for RDP etc) // Pass to WsAdapter's original handlers (for RDP etc)
for (const listener of existingListeners) { for (const listener of existingListeners) {
listener.call(server, req, socket, head); listener.call(server, req, socket, head);
}
} }
} catch (err: any) {
console.error(`[FATAL] Upgrade handler crashed: ${err.message}\n${err.stack}`);
socket.destroy();
} }
}); });
} }