const express = require('express'); const cors = require('cors'); const helmet = require('helmet'); const morgan = require('morgan'); const rateLimit = require('express-rate-limit'); const { globalErrorHandler } = require('./middleware/errorHandler'); const experimentsRouter = require('./routes/experiments'); const animalsRouter = require('./routes/animals'); const dailyStatusesRouter = require('./routes/dailyStatuses'); const auditLogsRouter = require('./routes/auditLogs'); const analysesRouter = require('./routes/analyses'); const sessionFilesRouter = require('./routes/sessionFiles'); const app = express(); // Security headers app.use(helmet({ crossOriginResourcePolicy: { policy: 'cross-origin' }, })); // CORS — restrict to the configured frontend origin in production const allowedOrigin = process.env.CORS_ORIGIN || '*'; app.use(cors({ origin: allowedOrigin, methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'], allowedHeaders: ['Content-Type', 'Authorization'], })); // Rate limiting — 100 requests per minute per IP on all API routes const apiLimiter = rateLimit({ windowMs: 60 * 1000, max: 100, standardHeaders: true, legacyHeaders: false, message: { error: 'Too many requests, please try again later.' }, skip: () => process.env.NODE_ENV === 'test', }); app.use(express.json({ limit: '1mb' })); app.use(morgan(process.env.NODE_ENV === 'test' ? 'silent' : process.env.NODE_ENV === 'production' ? 'combined' : 'dev')); // Health check (no rate limit) app.get('/health', (req, res) => { res.json({ status: 'ok', timestamp: new Date().toISOString() }); }); // DB info — returns database timezone (no rate limit) const prisma = require('./lib/prisma'); app.get('/api/info', async (req, res) => { try { const [{ timezone }] = await prisma.$queryRaw`SELECT current_setting('TimeZone') AS timezone`; res.json({ dbTimezone: timezone }); } catch { res.json({ dbTimezone: null }); } }); // Apply rate limiter to all API routes app.use('/api', apiLimiter); app.use('/api/experiments', experimentsRouter); app.use('/api/animals', animalsRouter); app.use('/api/daily-statuses', dailyStatusesRouter); app.use('/api/audit-logs', auditLogsRouter); app.use('/api', analysesRouter); app.use('/api', sessionFilesRouter); app.use((req, res) => { res.status(404).json({ error: `Route ${req.method} ${req.path} not found` }); }); app.use(globalErrorHandler); module.exports = app;