This repository has been archived on 2026-05-04. You can view files and clone it, but cannot push or open issues or pull requests.
Galaxy-Strike-Online/Server/server.js
2026-01-24 16:47:19 -04:00

196 lines
5.6 KiB
JavaScript

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 };