const express = require('express'); const cors = require('cors'); const helmet = require('helmet'); const rateLimit = require('express-rate-limit'); require('dotenv').config(); // Import models and associations const sequelize = require('./config/database'); require('./models/associations'); const app = express(); const PORT = process.env.PORT || 3000; // Rate limiting const limiter = rateLimit({ windowMs: 15 * 60 * 1000, // 15 minutes max: 100, // limit each IP to 100 requests per windowMs message: { success: false, message: 'Too many requests from this IP, please try again later.' } }); // Middleware app.use(limiter); app.use(helmet()); app.use(cors()); app.use(express.json({ limit: '10mb' })); app.use(express.urlencoded({ extended: true, limit: '10mb' })); // Health check endpoint app.get('/health', (req, res) => { res.json({ success: true, message: 'Restaurant API is running!', timestamp: new Date().toISOString(), environment: process.env.NODE_ENV || 'development' }); }); // API Documentation endpoint app.get('/api/docs', (req, res) => { const baseUrl = `${req.protocol}://${req.get('host')}`; res.json({ success: true, message: 'Restaurant Management API Documentation', version: '1.0.0', baseUrl: baseUrl, endpoints: { // Table Management tables: { description: 'Gestion des tables du restaurant', endpoints: { 'GET /api/tables': 'Obtenir toutes les tables', 'GET /api/tables/stats': 'Statistiques des tables', 'GET /api/tables/:id': 'Obtenir une table par ID', 'POST /api/tables': 'Créer une nouvelle table', 'PUT /api/tables/:id': 'Mettre à jour une table', 'DELETE /api/tables/:id': 'Supprimer une table' }, queryParams: { 'GET /api/tables': { page: 'Numéro de page (défaut: 1)', limit: 'Nombre d\'éléments par page (défaut: 10)', status: 'Filtrer par statut (available, occupied, reserved, maintenance)', location: 'Filtrer par emplacement', capacity_min: 'Capacité minimale', capacity_max: 'Capacité maximale', sort_by: 'Champ de tri (défaut: created_at)', sort_order: 'Ordre de tri (ASC, DESC)' } }, sampleRequest: { 'POST /api/tables': { nom: 'Table VIP 1', capacity: 4, status: 'available', location: 'Main Hall' } } }, // Menu Categories Management menuCategories: { description: 'Gestion des catégories de menu', endpoints: { 'GET /api/menu-categories': 'Obtenir toutes les catégories', 'GET /api/menu-categories/:id': 'Obtenir une catégorie par ID', 'POST /api/menu-categories': 'Créer une nouvelle catégorie', 'PUT /api/menu-categories/:id': 'Mettre à jour une catégorie', 'DELETE /api/menu-categories/:id': 'Supprimer une catégorie' }, sampleRequest: { 'POST /api/menu-categories': { nom: 'Entrées', description: 'Plats d\'entrée', ordre: 1, actif: true } } }, // Menu Management menus: { description: 'Gestion des plats du menu', endpoints: { 'GET /api/menus': 'Obtenir tous les plats', 'GET /api/menus/stats': 'Statistiques des menus', 'GET /api/menus/category/:categoryId': 'Obtenir les plats par catégorie', 'GET /api/menus/:id': 'Obtenir un plat par ID', 'POST /api/menus': 'Créer un nouveau plat', 'PUT /api/menus/:id': 'Mettre à jour un plat', 'DELETE /api/menus/:id': 'Supprimer un plat' }, queryParams: { 'GET /api/menus': { page: 'Numéro de page (défaut: 1)', limit: 'Nombre d\'éléments par page (défaut: 10)', search: 'Rechercher par nom', category_id: 'Filtrer par catégorie', disponible: 'Filtrer par disponibilité (true/false)', prix_min: 'Prix minimum', prix_max: 'Prix maximum' } }, sampleRequest: { 'POST /api/menus': { nom: 'Salade César', commentaire: 'Salade fraîche avec croûtons', prix: 12.50, categorie_id: 1, disponible: true, ingredients: 'Salade, croûtons, parmesan', allergenes: 'Gluten, Lactose', calories: 350 } } }, // Client Management clients: { description: 'Gestion des clients', endpoints: { 'GET /api/clients': 'Obtenir tous les clients', 'GET /api/clients/stats': 'Statistiques des clients', 'GET /api/clients/:id': 'Obtenir un client par ID', 'POST /api/clients': 'Créer un nouveau client', 'PUT /api/clients/:id': 'Mettre à jour un client', 'DELETE /api/clients/:id': 'Supprimer un client' }, queryParams: { 'GET /api/clients': { page: 'Numéro de page (défaut: 1)', limit: 'Nombre d\'éléments par page (défaut: 10)', search: 'Rechercher par nom, prénom, email ou téléphone', actif: 'Filtrer par statut actif (true/false)', sort_by: 'Champ de tri (défaut: created_at)', sort_order: 'Ordre de tri (ASC, DESC)' } }, sampleRequest: { 'POST /api/clients': { nom: 'Dupont', prenom: 'Jean', email: 'jean.dupont@email.com', telephone: '0123456789', adresse: '123 Rue de la Paix, Paris', date_naissance: '1980-05-15' } } }, // Reservation Management reservations: { description: 'Gestion des réservations', endpoints: { 'GET /api/reservations': 'Obtenir toutes les réservations', 'GET /api/reservations/stats': 'Statistiques des réservations', 'GET /api/reservations/today': 'Réservations du jour', 'GET /api/reservations/status/:status': 'Réservations par statut', 'GET /api/reservations/:id': 'Obtenir une réservation par ID', 'POST /api/reservations': 'Créer une nouvelle réservation', 'PUT /api/reservations/:id': 'Mettre à jour une réservation', 'DELETE /api/reservations/:id': 'Supprimer une réservation' }, queryParams: { 'GET /api/reservations': { page: 'Numéro de page (défaut: 1)', limit: 'Nombre d\'éléments par page (défaut: 10)', statut: 'Filtrer par statut (en_attente, confirmee, annulee, terminee)', date_debut: 'Date de début (YYYY-MM-DD)', date_fin: 'Date de fin (YYYY-MM-DD)', table_id: 'Filtrer par table' } }, sampleRequest: { 'POST /api/reservations': { client_id: 1, table_id: 1, date_reservation: '2025-01-28 19:30:00', nombre_personnes: 4, commentaires: 'Anniversaire', statut: 'en_attente' } }, statusValues: ['en_attente', 'confirmee', 'annulee', 'terminee'] }, // Order Management commandes: { description: 'Gestion des commandes', endpoints: { 'GET /api/commandes': 'Obtenir toutes les commandes', 'GET /api/commandes/stats': 'Statistiques des commandes', 'GET /api/commandes/kitchen': 'Commandes pour la cuisine', 'GET /api/commandes/status/:status': 'Commandes par statut', 'GET /api/commandes/:id': 'Obtenir une commande par ID', 'POST /api/commandes': 'Créer une nouvelle commande', 'PUT /api/commandes/:id/status': 'Mettre à jour le statut d\'une commande', 'DELETE /api/commandes/:id': 'Supprimer une commande' }, queryParams: { 'GET /api/commandes': { page: 'Numéro de page (défaut: 1)', limit: 'Nombre d\'éléments par page (défaut: 10)', statut: 'Filtrer par statut', date_debut: 'Date de début (YYYY-MM-DD)', date_fin: 'Date de fin (YYYY-MM-DD)', table_id: 'Filtrer par table', client_id: 'Filtrer par client' } }, sampleRequest: { 'POST /api/commandes': { client_id: 1, table_id: 1, reservation_id: 1, serveur: 'Marie', commentaires: 'Sans oignons', items: [ { menu_id: 1, quantite: 2, commentaires: 'Bien cuit' }, { menu_id: 2, quantite: 1 } ] }, 'PUT /api/commandes/:id/status': { statut: 'en_preparation', mode_paiement: 'carte', serveur: 'Marie' } }, statusValues: ['en_attente', 'en_preparation', 'prete', 'servie', 'payee', 'annulee'] } }, // Common Response Format responseFormat: { success: { success: true, data: '...', message: 'Optional success message' }, error: { success: false, message: 'Error description', error: 'Detailed error message' }, pagination: { success: true, data: { items: '...', pagination: { currentPage: 1, totalPages: 10, totalItems: 100, itemsPerPage: 10 } } } }, // Common HTTP Status Codes statusCodes: { 200: 'OK - Successful GET, PUT', 201: 'Created - Successful POST', 400: 'Bad Request - Invalid data', 404: 'Not Found - Resource not found', 500: 'Internal Server Error - Server error' } }); }); // Import routes const tableRoutes = require('./routes/tableRoutes'); const menuCategoryRoutes = require('./routes/menuCategoryRoutes'); const menuRoutes = require('./routes/menuRoutes'); const clientRoutes = require('./routes/clientRoutes'); const reservationRoutes = require('./routes/reservationRoutes'); const commandeRoutes = require('./routes/commandeRoutes'); // Use routes app.use('/api/tables', tableRoutes); app.use('/api/menu-categories', menuCategoryRoutes); app.use('/api/menus', menuRoutes); app.use('/api/clients', clientRoutes); app.use('/api/reservations', reservationRoutes); app.use('/api/commandes', commandeRoutes); // 404 handler app.use('*', (req, res) => { res.status(404).json({ success: false, message: 'Route not found', availableRoutes: { documentation: '/api/docs', health: '/health', tables: '/api/tables', menuCategories: '/api/menu-categories', menus: '/api/menus', clients: '/api/clients', reservations: '/api/reservations', orders: '/api/commandes' } }); }); // Global error handler app.use((error, req, res, next) => { console.error('Global Error:', error); res.status(error.status || 500).json({ success: false, message: error.message || 'Internal server error', ...(process.env.NODE_ENV === 'development' && { stack: error.stack }) }); }); // Start server function const startServer = async () => { try { // Test database connection await sequelize.authenticate(); console.log('✅ Database connection established successfully.'); // Sync database models await sequelize.sync({ alter: false }); console.log('✅ Database synchronized successfully.'); // Start server app.listen(PORT, () => { console.log(`🚀 Server is running on port ${PORT}`); console.log(`📚 API Documentation: http://localhost:${PORT}/api/docs`); console.log(`❤️ Health Check: http://localhost:${PORT}/health`); console.log('🔗 Available endpoints:'); console.log(' - Tables: /api/tables'); console.log(' - Menu Categories: /api/menu-categories'); console.log(' - Menus: /api/menus'); console.log(' - Clients: /api/clients'); console.log(' - Reservations: /api/reservations'); console.log(' - Orders: /api/commandes'); }); } catch (error) { console.error('❌ Unable to start server:', error); process.exit(1); } }; // Start the server startServer(); module.exports = app;