import express from 'express'; import cors from 'cors'; import dotenv from 'dotenv'; import path from 'path'; import fs from 'fs'; import { fileURLToPath } from 'url'; import { createServer } from 'http'; import rateLimit from 'express-rate-limit'; import { initDB } from './db/index.js'; import { validateConfig, config } from './config.js'; import { errorHandler } from './middleware/errorHandler.js'; import { initializeSocket } from './sockets/index.js'; import authRoutes from './routes/auth.js'; import challengeRoutes from './routes/challenges.js'; import predictionRoutes from './routes/predictions.js'; import friendRoutes from './routes/friends.js'; import tmdbRoutes from './routes/tmdb.js'; import leaderboardRoutes from './routes/leaderboard.js'; dotenv.config(); // Validate environment configuration validateConfig(); const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const app = express(); const httpServer = createServer(app); // Initialize Socket.io initializeSocket(httpServer); // Rate limiting const authLimiter = rateLimit({ windowMs: 15 * 60 * 1000, // 15 minutes max: 5, // 5 requests per window message: { error: 'Too many authentication attempts, please try again later.' }, standardHeaders: true, legacyHeaders: false, }); const apiLimiter = rateLimit({ windowMs: 15 * 60 * 1000, // 15 minutes max: 100, // 100 requests per window message: { error: 'Too many requests, please try again later.' }, standardHeaders: true, legacyHeaders: false, }); const tmdbLimiter = rateLimit({ windowMs: 60 * 1000, // 1 minute max: 20, // 20 requests per minute message: { error: 'Too many search requests, please slow down.' }, standardHeaders: true, legacyHeaders: false, }); // Middleware app.use(cors()); app.use(express.json()); // API Routes app.use('/api/auth', authLimiter, authRoutes); app.use('/api/challenges', apiLimiter, challengeRoutes); app.use('/api/predictions', apiLimiter, predictionRoutes); app.use('/api/friends', apiLimiter, friendRoutes); app.use('/api/tmdb', tmdbLimiter, tmdbRoutes); app.use('/api/leaderboard', apiLimiter, leaderboardRoutes); // Health check app.get('/api/health', (req, res) => { res.json({ status: 'ok', message: "What's The Point API" }); }); // Serve static frontend files (for production) const frontendPath = path.join(__dirname, '../../frontend/dist'); const frontendExists = fs.existsSync(frontendPath); if (frontendExists) { app.use(express.static(frontendPath)); // Serve index.html for all non-API routes (SPA support) app.get('*', (req, res) => { res.sendFile(path.join(frontendPath, 'index.html')); }); } else { console.log('â„šī¸ Frontend dist not found - running in API-only mode (dev)'); app.get('*', (req, res) => { res.json({ message: "What's The Point API - Frontend running separately", frontend: "http://192.168.1.175:5173" }); }); } // Error handling middleware (must be last) app.use(errorHandler); const PORT = config.server.port; // Initialize database and start server initDB() .then(() => { httpServer.listen(PORT, '0.0.0.0', () => { console.log(`✅ Server running on port ${PORT}`); console.log(`🔌 Socket.io ready for real-time updates`); }); }) .catch(err => { console.error('Failed to start server:', err); process.exit(1); });