import { NestFactory } from '@nestjs/core'; import { ValidationPipe } from '@nestjs/common'; import { WsAdapter } from '@nestjs/platform-ws'; import { AppModule } from './app.module'; import { WebSocketServer } from 'ws'; import { TerminalGateway } from './terminal/terminal.gateway'; import { SftpGateway } from './terminal/sftp.gateway'; import { RdpGateway } from './rdp/rdp.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() { const app = await NestFactory.create(AppModule); app.setGlobalPrefix('api'); app.useGlobalPipes(new ValidationPipe({ whitelist: true, transform: true })); app.useWebSocketAdapter(new WsAdapter(app)); app.enableCors({ origin: process.env.NODE_ENV === 'production' ? false : 'http://localhost:3001', credentials: true, }); await app.listen(3000); console.log('Wraith backend running on port 3000'); const server = app.getHttpServer(); const terminalGateway = app.get(TerminalGateway); const sftpGateway = app.get(SftpGateway); const rdpGateway = app.get(RdpGateway); const terminalWss = new WebSocketServer({ noServer: true }); const sftpWss = new WebSocketServer({ noServer: true }); const rdpWss = new WebSocketServer({ noServer: true }); terminalWss.on('connection', (ws, req) => { try { 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) => { try { console.log(`[WS] SFTP connection established`); sftpGateway.handleConnection(ws, req); } catch (err: any) { console.error(`[FATAL] SFTP handleConnection crashed: ${err.message}\n${err.stack}`); } }); rdpWss.on('connection', (ws, req) => { try { console.log(`[WS] RDP connection established`); rdpGateway.handleConnection(ws, req); } catch (err: any) { console.error(`[FATAL] RDP handleConnection crashed: ${err.message}\n${err.stack}`); } }); // Remove ALL existing upgrade listeners (WsAdapter's) so we handle upgrades first const existingListeners = server.listeners('upgrade'); server.removeAllListeners('upgrade'); // Our handler runs first — routes terminal/sftp, passes everything else to WsAdapter server.on('upgrade', (req: any, socket: any, head: any) => { try { const pathname = req.url?.split('?')[0]; console.log(`[HTTP-UPGRADE] path=${pathname} socket.destroyed=${socket.destroyed}`); if (pathname === '/api/ws/terminal') { terminalWss.handleUpgrade(req, socket, head, (ws) => { console.log(`[WS] Terminal upgrade complete`); terminalWss.emit('connection', ws, req); }); } else if (pathname === '/api/ws/sftp') { sftpWss.handleUpgrade(req, socket, head, (ws) => { console.log(`[WS] SFTP upgrade complete`); sftpWss.emit('connection', ws, req); }); } else if (pathname === '/api/ws/rdp') { rdpWss.handleUpgrade(req, socket, head, (ws) => { console.log(`[WS] RDP upgrade complete`); rdpWss.emit('connection', ws, req); }); } else { // Pass to WsAdapter's original handlers for (const listener of existingListeners) { listener.call(server, req, socket, head); } } } catch (err: any) { console.error(`[FATAL] Upgrade handler crashed: ${err.message}\n${err.stack}`); socket.destroy(); } }); } bootstrap();