import express, { Application, Request, Response } from 'express'; import cors from 'cors'; import helmet from 'helmet'; import rateLimit from 'express-rate-limit'; import swaggerUi from 'swagger-ui-express'; import { config } from './config'; import { logger } from './config/logger'; import { errorHandler } from './middleware/errorHandler'; import { RegisterRoutes } from './routes/routes'; export function createApp(): Application { const app = express(); // Security middleware - Disable CSP for Swagger UI app.use(helmet({ contentSecurityPolicy: false })); // CORS - Allow multiple origins const allowedOrigins = config.cors.origin.split(',').map(o => o.trim()); app.use(cors({ origin: (origin, callback) => { // Allow requests with no origin (like mobile apps, Postman, curl) if (!origin) return callback(null, true); // Check if origin is allowed if (allowedOrigins.includes('*') || allowedOrigins.includes(origin)) { callback(null, true); } else { callback(new Error('Not allowed by CORS')); } }, credentials: true })); // Rate limiting const limiter = rateLimit({ windowMs: config.rateLimit.windowMs, max: config.rateLimit.maxRequests, message: { error: { code: 'RATE_LIMIT_EXCEEDED', message: 'Too many requests, please try again later' } } }); app.use('/api/', limiter); // Body parsing app.use(express.json()); app.use(express.urlencoded({ extended: true })); // Request logging app.use((req: Request, res: Response, next) => { logger.info('Incoming request', { method: req.method, path: req.path, ip: req.ip }); next(); }); // Swagger documentation try { const swaggerDocument = require('../public/swagger.json'); app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerDocument)); logger.info('Swagger documentation available at /api-docs'); } catch (error) { logger.warn('Swagger documentation not available. Run "npm run tsoa:gen" to generate it.'); } // Simple health check endpoint (not using TSOA) app.get('/health', (req: Request, res: Response) => { res.json({ status: 'ok', timestamp: new Date().toISOString(), uptime: process.uptime() }); }); // Register TSOA routes RegisterRoutes(app); // 404 handler app.use((req: Request, res: Response) => { res.status(404).json({ error: { code: 'NOT_FOUND', message: 'Route not found' } }); }); // Error handler (must be last) app.use(errorHandler); return app; }