const express = require('express'); const cors = require('cors'); const helmet = require('helmet'); const morgan = require('morgan'); const compression = require('compression'); const rateLimit = require('express-rate-limit'); require('dotenv').config(); const authRoutes = require('./routes/auth'); const userRoutes = require('./routes/users'); const propertyRoutes = require('./routes/properties'); const equipmentRoutes = require('./routes/equipment'); const productRoutes = require('./routes/products'); const applicationRoutes = require('./routes/applications'); const weatherRoutes = require('./routes/weather'); const adminRoutes = require('./routes/admin'); const { errorHandler } = require('./middleware/errorHandler'); const { authenticateToken } = require('./middleware/auth'); const app = express(); const PORT = process.env.PORT || 5000; // Trust proxy for rate limiting app.set('trust proxy', 1); // Security middleware app.use(helmet({ contentSecurityPolicy: { directives: { defaultSrc: ["'self'"], scriptSrc: ["'self'", "'unsafe-inline'", "https://maps.googleapis.com"], styleSrc: ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com"], fontSrc: ["'self'", "https://fonts.gstatic.com"], imgSrc: ["'self'", "data:", "https://maps.googleapis.com", "https://maps.gstatic.com"], connectSrc: ["'self'", "https://api.openweathermap.org"] } } })); // Rate limiting const limiter = rateLimit({ windowMs: 15 * 60 * 1000, // 15 minutes max: 100, // Limit each IP to 100 requests per windowMs message: 'Too many requests from this IP, please try again later.', standardHeaders: true, legacyHeaders: false, }); app.use(limiter); // Stricter rate limiting for auth routes const authLimiter = rateLimit({ windowMs: 15 * 60 * 1000, // 15 minutes max: 50, // Limit each IP to 50 auth requests per windowMs (increased for development) message: 'Too many authentication attempts, please try again later.', standardHeaders: true, legacyHeaders: false, }); // Middleware app.use(compression()); app.use(cors({ origin: process.env.FRONTEND_URL || 'http://localhost:3000', credentials: true })); app.use(express.json({ limit: '10mb' })); app.use(express.urlencoded({ extended: true, limit: '10mb' })); app.use(morgan('combined')); // Health check endpoint app.get('/health', (req, res) => { res.status(200).json({ status: 'OK', timestamp: new Date().toISOString(), uptime: process.uptime(), environment: process.env.NODE_ENV || 'development' }); }); // Routes app.use('/api/auth', authLimiter, authRoutes); app.use('/api/users', authenticateToken, userRoutes); app.use('/api/properties', authenticateToken, propertyRoutes); app.use('/api/equipment', authenticateToken, equipmentRoutes); app.use('/api/products', authenticateToken, productRoutes); app.use('/api/applications', authenticateToken, applicationRoutes); app.use('/api/weather', authenticateToken, weatherRoutes); app.use('/api/admin', authenticateToken, adminRoutes); // 404 handler app.use('*', (req, res) => { res.status(404).json({ success: false, message: 'API endpoint not found' }); }); // Global error handler app.use(errorHandler); // Graceful shutdown process.on('SIGTERM', () => { console.log('SIGTERM received, shutting down gracefully'); process.exit(0); }); process.on('SIGINT', () => { console.log('SIGINT received, shutting down gracefully'); process.exit(0); }); app.listen(PORT, '0.0.0.0', () => { console.log(`TurfTracker API server running on port ${PORT}`); console.log(`Environment: ${process.env.NODE_ENV || 'development'}`); });