Files
rspade_system/app/RSpade/resource/DebugProxy/src/server.ts
root f6fac6c4bc Fix bin/publish: copy docs.dist from project root
Fix bin/publish: use correct .env path for rspade_system
Fix bin/publish script: prevent grep exit code 1 from terminating script

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-21 02:08:33 +00:00

151 lines
4.6 KiB
TypeScript
Executable File

import express, { Request, Response } from 'express';
import * as http from 'http';
import { Config } from './utils/config';
import { Logger } from './utils/logger';
import { WebSocketHandler } from './http/websocket';
import { AuthValidator } from './http/auth';
class DebugProxyServer {
private app: express.Application;
private server: http.Server | null = null;
private logger: Logger;
private isShuttingDown: boolean = false;
private wsHandler: WebSocketHandler;
private authValidator: AuthValidator;
constructor() {
this.logger = new Logger('DebugProxyServer');
this.app = express();
this.authValidator = new AuthValidator();
this.wsHandler = new WebSocketHandler(this.authValidator);
this.setupMiddleware();
this.setupRoutes();
this.setupShutdownHandlers();
}
private setupMiddleware(): void {
// Parse JSON bodies
this.app.use(express.json());
// Request logging
this.app.use((req, _res, next) => {
this.logger.info(`${req.method} ${req.path}`);
next();
});
}
private setupRoutes(): void {
// Public endpoints (no auth)
this.app.get('/_ide/debug/status', (_req: Request, res: Response) => {
res.json({
status: 'healthy',
service: 'rspade-debug-proxy',
version: '1.0.0',
uptime: process.uptime(),
memory: process.memoryUsage()
});
});
// Ping endpoint for basic connectivity test
this.app.get('/_ide/debug/ping', (_req: Request, res: Response) => {
res.json({ pong: true });
});
// Authenticated endpoints
this.app.use('/_ide/debug/api', this.authValidator.authMiddleware());
this.app.post('/_ide/debug/api/test', (req: Request, res: Response) => {
const sessionId = (req as any).sessionId;
res.json({
success: true,
message: 'Authenticated successfully',
sessionId: sessionId
});
});
// 404 handler
this.app.use((req: Request, res: Response) => {
res.status(404).json({
error: 'Not Found',
path: req.path
});
});
// Error handler
this.app.use((err: Error, _req: Request, res: Response, _next: Function) => {
this.logger.error('Request error:', err);
res.status(500).json({
error: 'Internal Server Error',
message: err.message
});
});
}
private setupShutdownHandlers(): void {
const shutdown = async (signal: string) => {
if (this.isShuttingDown) {
return;
}
this.isShuttingDown = true;
this.logger.info(`Received ${signal}, starting graceful shutdown...`);
// Close HTTP server
if (this.server) {
this.server.close(() => {
this.logger.info('HTTP server closed');
});
}
// Give connections time to close
setTimeout(() => {
this.logger.info('Shutdown complete');
process.exit(0);
}, 5000);
};
process.on('SIGTERM', () => shutdown('SIGTERM'));
process.on('SIGINT', () => shutdown('SIGINT'));
}
public async start(): Promise<void> {
const port = Config.HTTP_PORT;
return new Promise((resolve, reject) => {
this.server = this.app.listen(port, () => {
this.logger.info(`Debug proxy service started on port ${port}`);
this.logger.info(`Status endpoint: http://localhost:${port}/_ide/debug/status`);
this.logger.info(`WebSocket endpoint: ws://localhost:${port}/_ide/debug/ws`);
// Initialize WebSocket handler
this.wsHandler.initialize(this.server!);
this.logger.info('WebSocket handler initialized');
resolve();
});
this.server.on('error', (error: Error) => {
this.logger.error('Failed to start server:', error);
reject(error);
});
});
}
}
// Main entry point
async function main() {
const server = new DebugProxyServer();
try {
await server.start();
} catch (error) {
console.error('Failed to start debug proxy:', error);
process.exit(1);
}
}
// Start the server
main().catch(error => {
console.error('Uncaught error:', error);
process.exit(1);
});