105 lines
3.8 KiB
TypeScript
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();
|