import express from 'express'; import cors from 'cors'; import mongoose from 'mongoose'; import morgan from 'morgan'; import rateLimit from 'express-rate-limit'; import swaggerJsdoc from 'swagger-jsdoc'; import swaggerUi from 'swagger-ui-express'; import { config } from './config.js'; import productsRouter from './routes/products.js'; import scraperRouter from './routes/scraper.js'; const app = express(); // Swagger configuration const swaggerServerUrl = process.env.CORS_ORIGIN ? process.env.CORS_ORIGIN.replace(/\/$/, '') // strip trailing slash : `http://localhost:${config.port}`; const swaggerOptions = { definition: { openapi: '3.0.0', info: { title: 'FarmaFinder Parapharmacy API', version: '1.0.0', description: 'API for parapharmacy products from multiple Spanish pharmacies', }, servers: [ { url: swaggerServerUrl, description: process.env.CORS_ORIGIN ? 'Production server' : 'Development server', }, ], components: { schemas: { Product: { type: 'object', properties: { _id: { type: 'string' }, name: { type: 'string' }, brand: { type: 'string' }, category: { type: 'string' }, price: { type: 'number' }, source: { type: 'string' }, }, }, }, }, }, apis: ['./src/routes/*.js'], }; const swaggerSpec = swaggerJsdoc(swaggerOptions); // Middleware app.use(cors(config.cors)); app.use(express.json({ limit: '10mb' })); app.use(morgan('combined')); // Rate limiting const limiter = rateLimit({ windowMs: config.rateLimit.windowMs, max: config.rateLimit.max, standardHeaders: true, legacyHeaders: false, }); app.use(limiter); // Swagger UI app.use('/api/docs', swaggerUi.serve, swaggerUi.setup(swaggerSpec, { explorer: true, customCss: '.swagger-ui .topbar { display: none }', customSiteTitle: 'FarmaFinder Parapharmacy API', })); // Routes app.use('/api/products', productsRouter); app.use('/api', scraperRouter); // Health check app.get('/api/health', (req, res) => { res.json({ status: 'ok', timestamp: new Date().toISOString(), mongodb: mongoose.connection.readyState === 1 ? 'connected' : 'disconnected', }); }); // Sources info app.get('/api/sources', (req, res) => { res.json(config.sources); }); // Error handling middleware app.use((err, req, res, next) => { console.error('Unhandled error:', err); res.status(500).json({ error: 'Internal server error' }); }); // 404 handler app.use((req, res) => { res.status(404).json({ error: 'Not found' }); }); // Connect to MongoDB and start server async function start() { try { console.log(`[Parapharmacy API] Connecting to MongoDB: ${config.mongodb.uri}`); await mongoose.connect(config.mongodb.uri); console.log('[Parapharmacy API] Connected to MongoDB'); app.listen(config.port, () => { console.log(`[Parapharmacy API] Server running on port ${config.port}`); console.log(`[Parapharmacy API] Swagger docs: http://localhost:${config.port}/api/docs`); }); } catch (error) { console.error('[Parapharmacy API] Failed to start:', error); process.exit(1); } } // Handle graceful shutdown process.on('SIGTERM', async () => { console.log('[Parapharmacy API] SIGTERM received, shutting down...'); await mongoose.disconnect(); process.exit(0); }); process.on('SIGINT', async () => { console.log('[Parapharmacy API] SIGINT received, shutting down...'); await mongoose.disconnect(); process.exit(0); }); start(); export default app;