const express = require('express'); const http = require('http'); const socketIo = require('socket.io'); const cors = require('cors'); const helmet = require('helmet'); const compression = require('compression'); const rateLimit = require('rate-limiter-flexible'); require('dotenv').config(); const logger = require('./utils/logger'); const connectDB = require('./config/database'); const authRoutes = require('./routes/auth'); const gameRoutes = require('./routes/game'); const serverRoutes = require('./routes/servers'); const { initializeGameSystems } = require('./systems/GameSystem'); const SocketHandlers = require('./socket/socketHandlers'); const { errorHandler, notFound } = require('./middleware/errorHandler'); // Override console.error to properly log error objects const originalConsoleError = console.error; console.error = (...args) => { args.forEach(arg => { if (arg instanceof Error) { logger.error('Console Error:', { message: arg.message, stack: arg.stack, name: arg.name }); } else if (typeof arg === 'object' && arg !== null) { logger.error('Console Error Object:', arg); } else { logger.error('Console Error:', arg); } }); }; const app = express(); const server = http.createServer(app); const io = socketIo(server, { cors: { origin: ["https://galaxystrike.online", "https://api.korvarix.com", "http://api.korvarix.com:3001"], methods: ["GET", "POST"] } }); // Middleware app.use(helmet()); app.use(compression()); app.use(cors({ origin: ["https://galaxystrike.online", "https://api.korvarix.com", "http://api.korvarix.com:3001"], credentials: true })); app.use(express.json({ limit: '10mb' })); app.use(express.urlencoded({ extended: true })); // Static file serving app.use(express.static('../Website/dist')); // Rate limiting const { RateLimiterMemory } = require('rate-limiter-flexible'); const limiter = new RateLimiterMemory({ keyGenerator: (req) => req.ip, points: 100, // limit each IP to 100 requests per windowMs duration: 900, // 15 minutes blockDuration: 900, // Block for 15 minutes }); app.use('/api/', async (req, res, next) => { try { const resLimiter = await limiter.consume(req.ip); if (!resLimiter.remainingPoints) { return res.status(429).json({ error: 'Too many requests, please try again later.' }); } next(); } catch (error) { next(); } }); // Routes app.use('/api/auth', authRoutes); app.use('/api/game', gameRoutes); app.use('/api/servers', serverRoutes); // Health check app.get('/health', (req, res) => { res.status(200).json({ status: 'OK', timestamp: new Date().toISOString(), uptime: process.uptime() }); }); // API version endpoint app.get('/api/ssc/version', (req, res) => { res.status(200).json({ version: '1.0.0', service: 'galaxystrikeonline-server', timestamp: new Date().toISOString() }); }); // Fallback route for SPA - only serve index.html for non-API routes app.get('*', (req, res) => { // Don't try to serve index.html for API routes if (req.path.startsWith('/api/')) { return res.status(404).json({ error: 'API endpoint not found' }); } // Try dist first (for built files), fallback to public (for development) const distPath = require('path').resolve(__dirname, '../dist/index.html'); const publicPath = require('path').resolve(__dirname, '../public/index.html'); const fs = require('fs'); if (fs.existsSync(distPath)) { res.sendFile(distPath); } else if (fs.existsSync(publicPath)) { res.sendFile(publicPath); } else { res.status(404).json({ error: 'Frontend not found' }); } }); // Socket.IO connection handling const socketHandlers = new SocketHandlers(io); io.on('connection', (socket) => { socketHandlers.handleConnection(socket); }); // Error handling app.use(notFound); app.use(errorHandler); // Initialize database and game systems async function startServer() { try { // Connect to database await connectDB(); logger.info('Database connected successfully'); // Initialize game systems await initializeGameSystems(); logger.info('Game systems initialized'); // Check for duplicate accounts in database logger.info('Checking for duplicate accounts in database...'); const socketHandlers = new SocketHandlers(io); const duplicateCheck = await socketHandlers.checkForDuplicateAccounts(); if (duplicateCheck.error) { logger.error('Error checking duplicate accounts:', duplicateCheck.error); } else { logger.info('Duplicate account check completed successfully'); } // Start server const PORT = process.env.PORT || 3003; // Combined Server on port 3003 server.listen(PORT, () => { logger.info(`Combined Server running on port ${PORT}`); logger.info('Combined Server handles: Authentication, Server Browser, Multiplayer, User Management'); // Log connection statistics every 5 minutes setInterval(() => { const stats = socketHandlers.getConnectionStats(); logger.info('Connection stats:', stats); }, 300000); }); } catch (error) { logger.error('Failed to start server:', error); process.exit(1); } } // Handle uncaught errors process.on('uncaughtException', (error) => { logger.error('Uncaught Exception:', error); }); process.on('unhandledRejection', (reason, promise) => { logger.error('Unhandled Rejection at:', promise, 'reason:', reason); }); // Handle HTTP server errors server.on('error', (error) => { logger.error('HTTP Server error:', error); }); // Handle Socket.IO errors io.on('error', (error) => { logger.error('Socket.IO error:', error); }); startServer(); module.exports = { app, server, io };