wraith/backend/src/main.ts

105 lines
3.8 KiB
TypeScript

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();