const express = require('express'); const cors = require('cors'); const helmet = require('helmet'); const securityConfig = require('./config/security'); const { apiLimiter } = require('./middleware/rateLimiter'); const app = express(); // Security Headers (helmet) app.use(helmet({ contentSecurityPolicy: { directives: { defaultSrc: ["'self'"], styleSrc: ["'self'", "'unsafe-inline'", "https://ui-avatars.com"], scriptSrc: ["'self'"], imgSrc: ["'self'", "data:", "https:", "https://ui-avatars.com"], connectSrc: ["'self'"], fontSrc: ["'self'"], objectSrc: ["'none'"], mediaSrc: ["'self'"], frameSrc: ["'none'"], }, }, hsts: { maxAge: 31536000, includeSubDomains: true, preload: true, }, noSniff: true, xssFilter: true, hidePoweredBy: true, })); // CORS app.use(cors({ origin: (origin, callback) => { const allowedOrigins = securityConfig.cors.origin; // Allow requests with no origin (mobile apps, curl, etc.) if (!origin) return callback(null, true); if (allowedOrigins.includes(origin)) { callback(null, true); } else { callback(new Error('Not allowed by CORS')); } }, credentials: securityConfig.cors.credentials, maxAge: 86400, // 24 hours })); // Body parsing with size limits app.use(express.json({ limit: securityConfig.bodyLimit })); app.use(express.urlencoded({ extended: true, limit: securityConfig.bodyLimit })); // Request logging middleware app.use((req, res, next) => { console.log(`${new Date().toISOString()} - ${req.method} ${req.path}`); next(); }); // Health check endpoint app.get('/api/health', (req, res) => { res.status(200).json({ status: 'ok', message: 'Backend is running', timestamp: new Date().toISOString(), environment: process.env.NODE_ENV || 'development' }); }); // Apply rate limiting to all API routes app.use('/api/', apiLimiter); // API routes app.use('/api/auth', require('./routes/auth')); app.use('/api/users', require('./routes/users')); app.use('/api/events', require('./routes/events')); app.use('/api/wsdc', require('./routes/wsdc')); app.use('/api/divisions', require('./routes/divisions')); app.use('/api/competition-types', require('./routes/competitionTypes')); app.use('/api/matches', require('./routes/matches')); // app.use('/api/ratings', require('./routes/ratings')); // 404 handler app.use((req, res) => { res.status(404).json({ error: 'Not Found', message: `Cannot ${req.method} ${req.path}` }); }); // Error handler app.use((err, req, res, next) => { // Log full error for debugging console.error('Error:', err); // Determine if we should show detailed errors const isDevelopment = process.env.NODE_ENV === 'development'; // Generic error response const errorResponse = { success: false, error: isDevelopment ? err.message : 'Internal Server Error', }; // Add stack trace only in development if (isDevelopment && err.stack) { errorResponse.stack = err.stack; } res.status(err.status || 500).json(errorResponse); }); module.exports = app;