Files
turftracker/backend/src/app.js
2025-08-21 13:24:09 -05:00

115 lines
3.6 KiB
JavaScript

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'}`);
});