"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.webSocketService = void 0; const express_1 = __importDefault(require("express")); const http_1 = require("http"); const cookie_parser_1 = __importDefault(require("cookie-parser")); const helmet_1 = __importDefault(require("helmet")); const ormconfig_1 = require("../Infrastructure/ormconfig"); const userRouter_1 = __importDefault(require("./routers/userRouter")); const organizationRouter_1 = __importDefault(require("./routers/organizationRouter")); const deckRouter_1 = __importDefault(require("./routers/deckRouter")); const chatRouter_1 = __importDefault(require("./routers/chatRouter")); const contactRouter_1 = __importDefault(require("./routers/contactRouter")); const adminRouter_1 = __importDefault(require("./routers/adminRouter")); const deckImportExportRouter_1 = __importDefault(require("./routers/deckImportExportRouter")); const Logger_1 = require("../Application/Services/Logger"); const WebSocketService_1 = require("../Application/Services/WebSocketService"); const swaggerUiSetup_1 = require("./swagger/swaggerUiSetup"); const app = (0, express_1.default)(); const httpServer = (0, http_1.createServer)(app); const PORT = process.env.PORT || 3000; const isDevelopment = process.env.NODE_ENV === 'development'; const loggingService = Logger_1.LoggingService.getInstance(); (0, Logger_1.logStartup)('SerpentRace Backend starting up', { environment: process.env.NODE_ENV || 'development', port: PORT, nodeVersion: process.version, chatInactivityTimeout: process.env.CHAT_INACTIVITY_TIMEOUT_MINUTES || '30' }); app.use((0, helmet_1.default)({ contentSecurityPolicy: isDevelopment ? false : undefined })); app.use(express_1.default.json({ limit: '10mb' })); app.use(express_1.default.urlencoded({ extended: true, limit: '10mb' })); app.use((0, cookie_parser_1.default)()); app.use(loggingService.requestLoggingMiddleware()); app.use((req, res, next) => { const origin = req.headers.origin; const allowedOrigins = ['http://localhost:3000', 'http://localhost:3001', 'http://localhost:8080']; if (!origin || allowedOrigins.includes(origin)) { res.setHeader('Access-Control-Allow-Origin', origin || '*'); } res.setHeader('Access-Control-Allow-Credentials', 'true'); res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, PATCH, OPTIONS'); res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization, Cookie'); if (req.method === 'OPTIONS') { res.status(200).end(); return; } next(); }); if (isDevelopment) { app.use((req, res, next) => { (0, Logger_1.logRequest)(`${req.method} ${req.path}`, req, res); next(); }); } // Setup Swagger documentation (0, swaggerUiSetup_1.setupSwagger)(app); app.get('/', (req, res) => { res.json({ service: 'SerpentRace Backend API', status: 'running', version: '1.0.0', endpoints: { swagger: '/api-docs', users: '/api/users', organizations: '/api/organizations', decks: '/api/decks', chats: '/api/chats', contacts: '/api/contacts', admin: '/api/admin', deckImportExport: '/api/deck-import-export', health: '/health' }, websocket: { enabled: true, events: [ 'chat:join', 'chat:leave', 'message:send', 'group:create', 'chat:direct', 'game:chat:create', 'chat:history' ] } }); }); app.get('/health', async (req, res) => { try { const isDbConnected = ormconfig_1.AppDataSource.isInitialized; res.json({ status: 'healthy', timestamp: new Date().toISOString(), service: 'SerpentRace Backend API', version: '1.0.0', environment: process.env.NODE_ENV || 'development', database: { connected: isDbConnected, type: ormconfig_1.AppDataSource.options.type }, websocket: { enabled: true }, uptime: process.uptime() }); } catch (error) { res.status(503).json({ status: 'unhealthy', timestamp: new Date().toISOString(), error: 'Service health check failed' }); } }); // API Routes app.use('/api/users', userRouter_1.default); app.use('/api/organizations', organizationRouter_1.default); app.use('/api/decks', deckRouter_1.default); app.use('/api/chats', chatRouter_1.default); app.use('/api/contacts', contactRouter_1.default); app.use('/api/admin', adminRouter_1.default); app.use('/api/deck-import-export', deckImportExportRouter_1.default); // Global error handler (must be after routes) app.use(loggingService.errorLoggingMiddleware()); app.use((error, req, res, next) => { (0, Logger_1.logError)('Global error handler caught unhandled error', error, req, res); // Don't expose internal error details in production const isDevelopment = process.env.NODE_ENV === 'development'; res.status(500).json({ error: 'Internal server error', timestamp: new Date().toISOString(), ...(isDevelopment && { details: error.message, stack: error.stack }) }); }); // Handle 404 routes app.use((req, res) => { res.status(404).json({ error: 'Route not found', path: req.originalUrl, method: req.method, timestamp: new Date().toISOString() }); }); // Initialize WebSocket service after database connection let webSocketService; // Initialize database connection ormconfig_1.AppDataSource.initialize() .then(() => { const dbOptions = ormconfig_1.AppDataSource.options; (0, Logger_1.logConnection)('Database connection established', 'postgresql', 'success', { type: dbOptions.type, host: dbOptions.host, database: dbOptions.database }); // Initialize WebSocket service after database is connected exports.webSocketService = webSocketService = new WebSocketService_1.WebSocketService(httpServer); (0, Logger_1.logStartup)('WebSocket service initialized', { chatInactivityTimeout: process.env.CHAT_INACTIVITY_TIMEOUT_MINUTES || '30' }); }) .catch((error) => { const dbOptions = ormconfig_1.AppDataSource.options; (0, Logger_1.logConnection)('Database connection failed', 'postgresql', 'failure', { error: error.message, type: dbOptions.type, host: dbOptions.host, database: dbOptions.database }); process.exit(1); }); // Start server with WebSocket support const server = httpServer.listen(PORT, () => { (0, Logger_1.logStartup)('Server started successfully', { port: PORT, environment: process.env.NODE_ENV || 'development', timestamp: new Date().toISOString(), endpoints: { health: `/health`, swagger: `/api-docs`, users: `/api/users`, organizations: `/api/organizations`, decks: `/api/decks`, chats: `/api/chats` }, websocket: { enabled: true, chatInactivityTimeout: `${process.env.CHAT_INACTIVITY_TIMEOUT_MINUTES || '30'} minutes` } }); }); // Graceful shutdown const gracefulShutdown = async (signal) => { (0, Logger_1.logStartup)(`Received ${signal}. Shutting down gracefully...`); server.close(() => { (0, Logger_1.logStartup)('HTTP server closed'); if (ormconfig_1.AppDataSource.isInitialized) { ormconfig_1.AppDataSource.destroy() .then(() => { (0, Logger_1.logConnection)('Database connection closed', 'postgresql', 'success'); process.exit(0); }) .catch((error) => { (0, Logger_1.logError)('Error during database shutdown', error); process.exit(1); }); } else { process.exit(0); } }); }; process.on('SIGTERM', () => gracefulShutdown('SIGTERM')); process.on('SIGINT', () => gracefulShutdown('SIGINT')); // Handle uncaught exceptions process.on('uncaughtException', (error) => { (0, Logger_1.logError)('Uncaught Exception - Server will shut down', error); process.exit(1); }); // Handle unhandled promise rejections process.on('unhandledRejection', (reason, promise) => { (0, Logger_1.logError)('Unhandled Rejection - Server will shut down', new Error(String(reason)), undefined, undefined); process.exit(1); }); //# sourceMappingURL=index.js.map