feat: add parapharmacy API with MongoDB and N8N setup
- Create parapharmacy-api microservice with Express + Swagger + MongoDB - Add product model with full-text search and deduplication - Add CRUD endpoints and bulk upsert for scrapers - Update docker-compose.yml with MongoDB, N8N, and parapharmacy-api services - Add proxy endpoints in FarmaFinder backend for parapharmacy - Update frontend services to use parapharmacy API - Add Swagger documentation at /api/docs
This commit is contained in:
@@ -0,0 +1,130 @@
|
||||
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';
|
||||
|
||||
const app = express();
|
||||
|
||||
// Swagger configuration
|
||||
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: `http://localhost:${config.port}`,
|
||||
description: '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);
|
||||
|
||||
// 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;
|
||||
Reference in New Issue
Block a user