You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
378 lines
15 KiB
378 lines
15 KiB
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;
|
|
|