aboutsummaryrefslogtreecommitdiffstats
path: root/backend/src
diff options
context:
space:
mode:
authorLibravatarLibravatar Biswa Kalyan Bhuyan <[email protected]> 2025-04-29 10:47:43 +0530
committerLibravatarLibravatar Biswa Kalyan Bhuyan <[email protected]> 2025-04-29 10:47:43 +0530
commita2e0a65b2599267efe94d665d6305f59b225bbd5 (patch)
treee2cef2031e3f7655e0c5f419020a3f1064c3b7b8 /backend/src
parent570bf0f3f065d583d6f94ecfc61aae93ba3e43de (diff)
downloadrestaurant-master.tar.gz
restaurant-master.tar.bz2
restaurant-master.zip
feat: added initlaized the frontend and backendHEADmaster
Diffstat (limited to 'backend/src')
-rw-r--r--backend/src/config/database.js14
-rw-r--r--backend/src/config/env.js11
-rw-r--r--backend/src/config/logger.js7
-rw-r--r--backend/src/controllers/auth.controller.js130
-rw-r--r--backend/src/index.js48
-rw-r--r--backend/src/middleware/auth.middleware.js63
-rw-r--r--backend/src/middleware/error.middleware.js40
-rw-r--r--backend/src/models/Category.js34
-rw-r--r--backend/src/models/Feedback.js61
-rw-r--r--backend/src/models/MenuItem.js73
-rw-r--r--backend/src/models/Order.js60
-rw-r--r--backend/src/models/OrderItem.js32
-rw-r--r--backend/src/models/Reservation.js54
-rw-r--r--backend/src/models/Table.js36
-rw-r--r--backend/src/models/User.js70
-rw-r--r--backend/src/routes/auth.routes.js14
-rw-r--r--backend/src/routes/index.js20
-rw-r--r--backend/src/socket/handlers.js51
-rw-r--r--backend/src/socket/index.js42
-rw-r--r--backend/src/utils/jwt.js37
20 files changed, 897 insertions, 0 deletions
diff --git a/backend/src/config/database.js b/backend/src/config/database.js
new file mode 100644
index 0000000..8fd758e
--- /dev/null
+++ b/backend/src/config/database.js
@@ -0,0 +1,14 @@
+const mongoose = require('mongoose');
+
+const connectDB = async () => {
+ try {
+ const conn = await mongoose.connect(process.env.MONGODB_URI || 'mongodb://localhost:27017/restaurant');
+ console.log(`MongoDB Connected: ${conn.connection.host}`);
+ return conn;
+ } catch (error) {
+ console.error(`Error: ${error.message}`);
+ process.exit(1);
+ }
+};
+
+module.exports = connectDB; \ No newline at end of file
diff --git a/backend/src/config/env.js b/backend/src/config/env.js
new file mode 100644
index 0000000..9353571
--- /dev/null
+++ b/backend/src/config/env.js
@@ -0,0 +1,11 @@
+require('dotenv').config();
+
+const env = {
+ PORT: process.env.PORT || 5000,
+ MONGODB_URI: process.env.MONGODB_URI || 'mongodb://localhost:27017/restaurant',
+ JWT_SECRET: process.env.JWT_SECRET || 'your_jwt_secret_here',
+ NODE_ENV: process.env.NODE_ENV || 'development',
+ JWT_EXPIRES_IN: process.env.JWT_EXPIRES_IN || '30d'
+};
+
+module.exports = env; \ No newline at end of file
diff --git a/backend/src/config/logger.js b/backend/src/config/logger.js
new file mode 100644
index 0000000..c9897b0
--- /dev/null
+++ b/backend/src/config/logger.js
@@ -0,0 +1,7 @@
+const morgan = require('morgan');
+const env = require('./env');
+
+// Configure morgan logger
+const logger = morgan(env.NODE_ENV === 'development' ? 'dev' : 'combined');
+
+module.exports = logger; \ No newline at end of file
diff --git a/backend/src/controllers/auth.controller.js b/backend/src/controllers/auth.controller.js
new file mode 100644
index 0000000..b5142d2
--- /dev/null
+++ b/backend/src/controllers/auth.controller.js
@@ -0,0 +1,130 @@
+const User = require('../models/User');
+const { generateToken } = require('../utils/jwt');
+
+/**
+ * Register a new user
+ * @param {Object} req - Express request object
+ * @param {Object} res - Express response object
+ */
+const register = async (req, res) => {
+ try {
+ const { name, email, password, phone, address } = req.body;
+
+ // Check if user already exists
+ const existingUser = await User.findOne({ email });
+ if (existingUser) {
+ return res.status(400).json({ message: 'User already exists with this email' });
+ }
+
+ // Create new user
+ const user = await User.create({
+ name,
+ email,
+ password,
+ phone,
+ address,
+ role: 'customer' // Default role for registrations
+ });
+
+ // Generate token
+ const token = generateToken(user);
+
+ // Send response
+ res.status(201).json({
+ message: 'User registered successfully',
+ token,
+ user: {
+ id: user._id,
+ name: user.name,
+ email: user.email,
+ role: user.role
+ }
+ });
+ } catch (error) {
+ res.status(500).json({
+ message: 'Error registering user',
+ error: error.message
+ });
+ }
+};
+
+/**
+ * Login a user
+ * @param {Object} req - Express request object
+ * @param {Object} res - Express response object
+ */
+const login = async (req, res) => {
+ try {
+ const { email, password } = req.body;
+
+ // Find user by email
+ const user = await User.findOne({ email }).select('+password');
+
+ // Check if user exists and password is correct
+ if (!user || !(await user.comparePassword(password))) {
+ return res.status(401).json({ message: 'Invalid email or password' });
+ }
+
+ // Check if user is active
+ if (!user.active) {
+ return res.status(401).json({ message: 'Your account has been deactivated' });
+ }
+
+ // Generate token
+ const token = generateToken(user);
+
+ // Send response
+ res.status(200).json({
+ message: 'Logged in successfully',
+ token,
+ user: {
+ id: user._id,
+ name: user.name,
+ email: user.email,
+ role: user.role
+ }
+ });
+ } catch (error) {
+ res.status(500).json({
+ message: 'Error logging in',
+ error: error.message
+ });
+ }
+};
+
+/**
+ * Get current user profile
+ * @param {Object} req - Express request object
+ * @param {Object} res - Express response object
+ */
+const getProfile = async (req, res) => {
+ try {
+ const user = await User.findById(req.user._id);
+
+ if (!user) {
+ return res.status(404).json({ message: 'User not found' });
+ }
+
+ res.status(200).json({
+ user: {
+ id: user._id,
+ name: user.name,
+ email: user.email,
+ role: user.role,
+ phone: user.phone,
+ address: user.address
+ }
+ });
+ } catch (error) {
+ res.status(500).json({
+ message: 'Error retrieving profile',
+ error: error.message
+ });
+ }
+};
+
+module.exports = {
+ register,
+ login,
+ getProfile
+}; \ No newline at end of file
diff --git a/backend/src/index.js b/backend/src/index.js
new file mode 100644
index 0000000..6ee6435
--- /dev/null
+++ b/backend/src/index.js
@@ -0,0 +1,48 @@
+const express = require('express');
+const cors = require('cors');
+const path = require('path');
+const http = require('http');
+const connectDB = require('./config/database');
+const env = require('./config/env');
+const logger = require('./config/logger');
+const { notFound, errorHandler } = require('./middleware/error.middleware');
+const apiRoutes = require('./routes');
+const initializeSocket = require('./socket');
+
+// Initialize express app
+const app = express();
+const PORT = env.PORT;
+
+// Create HTTP server
+const server = http.createServer(app);
+
+// Initialize Socket.IO
+const io = initializeSocket(server);
+
+// Make io accessible to routes
+app.set('io', io);
+
+// Connect to MongoDB
+connectDB();
+
+// Middleware
+app.use(cors());
+app.use(express.json());
+app.use(logger);
+
+// Routes
+app.get('/', (req, res) => {
+ res.json({ message: 'Welcome to Restaurant Management System API' });
+});
+
+// API Routes
+app.use('/api', apiRoutes);
+
+// Error handling middleware
+app.use(notFound);
+app.use(errorHandler);
+
+// Start the server
+server.listen(PORT, () => {
+ console.log(`Server is running on port ${PORT}`);
+}); \ No newline at end of file
diff --git a/backend/src/middleware/auth.middleware.js b/backend/src/middleware/auth.middleware.js
new file mode 100644
index 0000000..62c7aa9
--- /dev/null
+++ b/backend/src/middleware/auth.middleware.js
@@ -0,0 +1,63 @@
+const { verifyToken } = require('../utils/jwt');
+const User = require('../models/User');
+
+/**
+ * Authentication middleware
+ * @param {Object} req - Express request object
+ * @param {Object} res - Express response object
+ * @param {Function} next - Express next function
+ */
+const authenticate = async (req, res, next) => {
+ try {
+ let token;
+
+ // Get token from Authorization header
+ if (req.headers.authorization && req.headers.authorization.startsWith('Bearer')) {
+ token = req.headers.authorization.split(' ')[1];
+ }
+
+ if (!token) {
+ return res.status(401).json({ message: 'Authentication required. Please log in.' });
+ }
+
+ // Verify token
+ const decoded = verifyToken(token);
+
+ // Find user by id
+ const user = await User.findById(decoded.id);
+
+ if (!user || !user.active) {
+ return res.status(401).json({ message: 'The user no longer exists or is inactive.' });
+ }
+
+ // Attach user to request object
+ req.user = user;
+ next();
+ } catch (error) {
+ res.status(401).json({ message: 'Authentication failed. Invalid token.' });
+ }
+};
+
+/**
+ * Authorization middleware factory
+ * @param {String[]} roles - Array of allowed roles
+ * @returns {Function} Express middleware
+ */
+const authorize = (...roles) => {
+ return (req, res, next) => {
+ if (!req.user) {
+ return res.status(401).json({ message: 'Authentication required.' });
+ }
+
+ if (!roles.includes(req.user.role)) {
+ return res.status(403).json({ message: 'You do not have permission to perform this action.' });
+ }
+
+ next();
+ };
+};
+
+module.exports = {
+ authenticate,
+ authorize
+}; \ No newline at end of file
diff --git a/backend/src/middleware/error.middleware.js b/backend/src/middleware/error.middleware.js
new file mode 100644
index 0000000..e037cff
--- /dev/null
+++ b/backend/src/middleware/error.middleware.js
@@ -0,0 +1,40 @@
+const env = require('../config/env');
+
+/**
+ * Not found error handler middleware
+ * @param {Object} req - Express request object
+ * @param {Object} res - Express response object
+ * @param {Function} next - Express next function
+ */
+const notFound = (req, res, next) => {
+ const error = new Error(`Not Found - ${req.originalUrl}`);
+ res.status(404);
+ next(error);
+};
+
+/**
+ * General error handler middleware
+ * @param {Error} err - Error object
+ * @param {Object} req - Express request object
+ * @param {Object} res - Express response object
+ * @param {Function} next - Express next function
+ */
+const errorHandler = (err, req, res, next) => {
+ // Log the error
+ console.error(err.stack);
+
+ // Set status code
+ const statusCode = res.statusCode === 200 ? 500 : res.statusCode;
+
+ // Send response
+ res.status(statusCode).json({
+ message: err.message,
+ stack: env.NODE_ENV === 'production' ? '🥞' : err.stack,
+ error: env.NODE_ENV === 'development' ? err : {}
+ });
+};
+
+module.exports = {
+ notFound,
+ errorHandler
+}; \ No newline at end of file
diff --git a/backend/src/models/Category.js b/backend/src/models/Category.js
new file mode 100644
index 0000000..bb28ba3
--- /dev/null
+++ b/backend/src/models/Category.js
@@ -0,0 +1,34 @@
+const mongoose = require('mongoose');
+
+const categorySchema = new mongoose.Schema(
+ {
+ name: {
+ type: String,
+ required: [true, 'Category name is required'],
+ trim: true,
+ unique: true
+ },
+ description: {
+ type: String,
+ trim: true
+ },
+ image: {
+ type: String
+ },
+ active: {
+ type: Boolean,
+ default: true
+ },
+ displayOrder: {
+ type: Number,
+ default: 0
+ }
+ },
+ {
+ timestamps: true
+ }
+);
+
+const Category = mongoose.model('Category', categorySchema);
+
+module.exports = Category; \ No newline at end of file
diff --git a/backend/src/models/Feedback.js b/backend/src/models/Feedback.js
new file mode 100644
index 0000000..b433fed
--- /dev/null
+++ b/backend/src/models/Feedback.js
@@ -0,0 +1,61 @@
+const mongoose = require('mongoose');
+
+const feedbackSchema = new mongoose.Schema(
+ {
+ user: {
+ type: mongoose.Schema.Types.ObjectId,
+ ref: 'User',
+ required: [true, 'User is required']
+ },
+ order: {
+ type: mongoose.Schema.Types.ObjectId,
+ ref: 'Order'
+ },
+ rating: {
+ type: Number,
+ required: [true, 'Rating is required'],
+ min: 1,
+ max: 5
+ },
+ comment: {
+ type: String,
+ trim: true
+ },
+ foodRating: {
+ type: Number,
+ min: 1,
+ max: 5
+ },
+ serviceRating: {
+ type: Number,
+ min: 1,
+ max: 5
+ },
+ ambience: {
+ type: Number,
+ min: 1,
+ max: 5
+ },
+ photos: [
+ {
+ type: String
+ }
+ ],
+ status: {
+ type: String,
+ enum: ['pending', 'approved', 'rejected'],
+ default: 'pending'
+ },
+ adminResponse: {
+ type: String,
+ trim: true
+ }
+ },
+ {
+ timestamps: true
+ }
+);
+
+const Feedback = mongoose.model('Feedback', feedbackSchema);
+
+module.exports = Feedback; \ No newline at end of file
diff --git a/backend/src/models/MenuItem.js b/backend/src/models/MenuItem.js
new file mode 100644
index 0000000..47701c8
--- /dev/null
+++ b/backend/src/models/MenuItem.js
@@ -0,0 +1,73 @@
+const mongoose = require('mongoose');
+
+const menuItemSchema = new mongoose.Schema(
+ {
+ name: {
+ type: String,
+ required: [true, 'Menu item name is required'],
+ trim: true
+ },
+ description: {
+ type: String,
+ trim: true
+ },
+ price: {
+ type: Number,
+ required: [true, 'Price is required'],
+ min: 0
+ },
+ category: {
+ type: mongoose.Schema.Types.ObjectId,
+ ref: 'Category',
+ required: [true, 'Category is required']
+ },
+ image: {
+ type: String
+ },
+ ingredients: {
+ type: [String],
+ default: []
+ },
+ available: {
+ type: Boolean,
+ default: true
+ },
+ vegetarian: {
+ type: Boolean,
+ default: false
+ },
+ vegan: {
+ type: Boolean,
+ default: false
+ },
+ glutenFree: {
+ type: Boolean,
+ default: false
+ },
+ spiceLevel: {
+ type: Number,
+ min: 0,
+ max: 5,
+ default: 0
+ },
+ preparationTime: {
+ type: Number,
+ min: 0,
+ default: 15
+ },
+ featuredItem: {
+ type: Boolean,
+ default: false
+ }
+ },
+ {
+ timestamps: true
+ }
+);
+
+// Create text index for search functionality
+menuItemSchema.index({ name: 'text', description: 'text', ingredients: 'text' });
+
+const MenuItem = mongoose.model('MenuItem', menuItemSchema);
+
+module.exports = MenuItem; \ No newline at end of file
diff --git a/backend/src/models/Order.js b/backend/src/models/Order.js
new file mode 100644
index 0000000..c60f986
--- /dev/null
+++ b/backend/src/models/Order.js
@@ -0,0 +1,60 @@
+const mongoose = require('mongoose');
+
+const orderSchema = new mongoose.Schema(
+ {
+ user: {
+ type: mongoose.Schema.Types.ObjectId,
+ ref: 'User',
+ required: [true, 'User is required']
+ },
+ items: [
+ {
+ type: mongoose.Schema.Types.ObjectId,
+ ref: 'OrderItem'
+ }
+ ],
+ table: {
+ type: mongoose.Schema.Types.ObjectId,
+ ref: 'Table'
+ },
+ status: {
+ type: String,
+ enum: ['pending', 'preparing', 'ready', 'served', 'completed', 'cancelled'],
+ default: 'pending'
+ },
+ totalAmount: {
+ type: Number,
+ required: [true, 'Total amount is required'],
+ min: 0
+ },
+ paymentStatus: {
+ type: String,
+ enum: ['pending', 'completed', 'failed', 'refunded'],
+ default: 'pending'
+ },
+ paymentMethod: {
+ type: String,
+ enum: ['cash', 'card', 'mobile'],
+ default: 'cash'
+ },
+ specialInstructions: {
+ type: String,
+ trim: true
+ },
+ isDelivery: {
+ type: Boolean,
+ default: false
+ },
+ deliveryAddress: {
+ type: String,
+ trim: true
+ }
+ },
+ {
+ timestamps: true
+ }
+);
+
+const Order = mongoose.model('Order', orderSchema);
+
+module.exports = Order; \ No newline at end of file
diff --git a/backend/src/models/OrderItem.js b/backend/src/models/OrderItem.js
new file mode 100644
index 0000000..0625c96
--- /dev/null
+++ b/backend/src/models/OrderItem.js
@@ -0,0 +1,32 @@
+const mongoose = require('mongoose');
+
+const orderItemSchema = new mongoose.Schema(
+ {
+ menuItem: {
+ type: mongoose.Schema.Types.ObjectId,
+ ref: 'MenuItem',
+ required: [true, 'Menu item is required']
+ },
+ quantity: {
+ type: Number,
+ required: [true, 'Quantity is required'],
+ min: 1
+ },
+ price: {
+ type: Number,
+ required: [true, 'Price is required'],
+ min: 0
+ },
+ specialInstructions: {
+ type: String,
+ trim: true
+ }
+ },
+ {
+ timestamps: true
+ }
+);
+
+const OrderItem = mongoose.model('OrderItem', orderItemSchema);
+
+module.exports = OrderItem; \ No newline at end of file
diff --git a/backend/src/models/Reservation.js b/backend/src/models/Reservation.js
new file mode 100644
index 0000000..8ddba54
--- /dev/null
+++ b/backend/src/models/Reservation.js
@@ -0,0 +1,54 @@
+const mongoose = require('mongoose');
+
+const reservationSchema = new mongoose.Schema(
+ {
+ user: {
+ type: mongoose.Schema.Types.ObjectId,
+ ref: 'User',
+ required: [true, 'User is required']
+ },
+ table: {
+ type: mongoose.Schema.Types.ObjectId,
+ ref: 'Table',
+ required: [true, 'Table is required']
+ },
+ date: {
+ type: Date,
+ required: [true, 'Reservation date is required']
+ },
+ startTime: {
+ type: String,
+ required: [true, 'Start time is required']
+ },
+ endTime: {
+ type: String,
+ required: [true, 'End time is required']
+ },
+ numberOfGuests: {
+ type: Number,
+ required: [true, 'Number of guests is required'],
+ min: 1
+ },
+ status: {
+ type: String,
+ enum: ['pending', 'confirmed', 'cancelled', 'completed'],
+ default: 'pending'
+ },
+ specialRequests: {
+ type: String,
+ trim: true
+ },
+ occasion: {
+ type: String,
+ enum: ['regular', 'birthday', 'anniversary', 'business', 'other'],
+ default: 'regular'
+ }
+ },
+ {
+ timestamps: true
+ }
+);
+
+const Reservation = mongoose.model('Reservation', reservationSchema);
+
+module.exports = Reservation; \ No newline at end of file
diff --git a/backend/src/models/Table.js b/backend/src/models/Table.js
new file mode 100644
index 0000000..7653b3e
--- /dev/null
+++ b/backend/src/models/Table.js
@@ -0,0 +1,36 @@
+const mongoose = require('mongoose');
+
+const tableSchema = new mongoose.Schema(
+ {
+ tableNumber: {
+ type: Number,
+ required: [true, 'Table number is required'],
+ unique: true
+ },
+ capacity: {
+ type: Number,
+ required: [true, 'Capacity is required'],
+ min: 1
+ },
+ location: {
+ type: String,
+ enum: ['indoor', 'outdoor', 'balcony', 'private'],
+ default: 'indoor'
+ },
+ available: {
+ type: Boolean,
+ default: true
+ },
+ isReserved: {
+ type: Boolean,
+ default: false
+ }
+ },
+ {
+ timestamps: true
+ }
+);
+
+const Table = mongoose.model('Table', tableSchema);
+
+module.exports = Table; \ No newline at end of file
diff --git a/backend/src/models/User.js b/backend/src/models/User.js
new file mode 100644
index 0000000..dda71d0
--- /dev/null
+++ b/backend/src/models/User.js
@@ -0,0 +1,70 @@
+const mongoose = require('mongoose');
+const bcrypt = require('bcryptjs');
+
+const userSchema = new mongoose.Schema(
+ {
+ name: {
+ type: String,
+ required: [true, 'Name is required'],
+ trim: true
+ },
+ email: {
+ type: String,
+ required: [true, 'Email is required'],
+ unique: true,
+ trim: true,
+ lowercase: true,
+ match: [/^\w+([.-]?\w+)*@\w+([.-]?\w+)*(\.\w{2,3})+$/, 'Please enter a valid email']
+ },
+ password: {
+ type: String,
+ required: [true, 'Password is required'],
+ minlength: [6, 'Password must be at least 6 characters long'],
+ select: false
+ },
+ role: {
+ type: String,
+ enum: ['customer', 'staff', 'admin'],
+ default: 'customer'
+ },
+ phone: {
+ type: String,
+ trim: true
+ },
+ address: {
+ type: String,
+ trim: true
+ },
+ active: {
+ type: Boolean,
+ default: true
+ }
+ },
+ {
+ timestamps: true
+ }
+);
+
+// Password hashing middleware
+userSchema.pre('save', async function (next) {
+ if (!this.isModified('password')) {
+ return next();
+ }
+
+ try {
+ const salt = await bcrypt.genSalt(10);
+ this.password = await bcrypt.hash(this.password, salt);
+ next();
+ } catch (error) {
+ next(error);
+ }
+});
+
+// Compare password method
+userSchema.methods.comparePassword = async function (candidatePassword) {
+ return await bcrypt.compare(candidatePassword, this.password);
+};
+
+const User = mongoose.model('User', userSchema);
+
+module.exports = User; \ No newline at end of file
diff --git a/backend/src/routes/auth.routes.js b/backend/src/routes/auth.routes.js
new file mode 100644
index 0000000..d01423c
--- /dev/null
+++ b/backend/src/routes/auth.routes.js
@@ -0,0 +1,14 @@
+const express = require('express');
+const { register, login, getProfile } = require('../controllers/auth.controller');
+const { authenticate } = require('../middleware/auth.middleware');
+
+const router = express.Router();
+
+// Public routes
+router.post('/register', register);
+router.post('/login', login);
+
+// Protected routes
+router.get('/profile', authenticate, getProfile);
+
+module.exports = router; \ No newline at end of file
diff --git a/backend/src/routes/index.js b/backend/src/routes/index.js
new file mode 100644
index 0000000..4ea5c46
--- /dev/null
+++ b/backend/src/routes/index.js
@@ -0,0 +1,20 @@
+const express = require('express');
+const authRoutes = require('./auth.routes');
+// Import other routes as they are created
+// const menuRoutes = require('./menu.routes');
+// const reservationRoutes = require('./reservations.routes');
+// const orderRoutes = require('./orders.routes');
+// const feedbackRoutes = require('./feedback.routes');
+
+const router = express.Router();
+
+// Auth routes
+router.use('/auth', authRoutes);
+
+// Other routes to be added
+// router.use('/menu', menuRoutes);
+// router.use('/reservations', reservationRoutes);
+// router.use('/orders', orderRoutes);
+// router.use('/feedback', feedbackRoutes);
+
+module.exports = router; \ No newline at end of file
diff --git a/backend/src/socket/handlers.js b/backend/src/socket/handlers.js
new file mode 100644
index 0000000..7e110b5
--- /dev/null
+++ b/backend/src/socket/handlers.js
@@ -0,0 +1,51 @@
+/**
+ * Register all event handlers for a socket connection
+ * @param {object} io - Socket.IO server instance
+ * @param {object} socket - Socket connection instance
+ */
+const registerHandlers = (io, socket) => {
+ // Handle joining a room (e.g., for orders or staff notifications)
+ socket.on('join-room', (room) => {
+ socket.join(room);
+ console.log(`Client ${socket.id} joined room: ${room}`);
+ });
+
+ // Handle order updates
+ socket.on('order-update', (data) => {
+ // Broadcast order updates to all clients in the room
+ io.to('staff-notifications').emit('order-status-changed', data);
+ io.to(`user-${data.userId}`).emit('order-status-changed', data);
+ console.log(`Order update: ${JSON.stringify(data)}`);
+ });
+
+ // Handle table reservation updates
+ socket.on('reservation-update', (data) => {
+ // Broadcast reservation updates to staff
+ io.to('staff-notifications').emit('reservation-changed', data);
+ console.log(`Reservation update: ${JSON.stringify(data)}`);
+ });
+
+ // Handle staff notifications
+ socket.on('staff-notification', (data) => {
+ // Broadcast to specific staff members or all staff
+ io.to('staff-notifications').emit('new-notification', data);
+ console.log(`Staff notification: ${JSON.stringify(data)}`);
+ });
+
+ // Handle customer notifications
+ socket.on('customer-notification', (data) => {
+ // Send notification to specific customer
+ io.to(`user-${data.userId}`).emit('new-notification', data);
+ console.log(`Customer notification: ${JSON.stringify(data)}`);
+ });
+
+ // Leave room
+ socket.on('leave-room', (room) => {
+ socket.leave(room);
+ console.log(`Client ${socket.id} left room: ${room}`);
+ });
+};
+
+module.exports = {
+ registerHandlers
+}; \ No newline at end of file
diff --git a/backend/src/socket/index.js b/backend/src/socket/index.js
new file mode 100644
index 0000000..0c25ed4
--- /dev/null
+++ b/backend/src/socket/index.js
@@ -0,0 +1,42 @@
+const socketIO = require('socket.io');
+const handlers = require('./handlers');
+
+/**
+ * Initialize Socket.IO
+ * @param {object} server - HTTP server instance
+ * @returns {object} Socket.IO instance
+ */
+const initializeSocket = (server) => {
+ const io = socketIO(server, {
+ cors: {
+ origin: '*',
+ methods: ['GET', 'POST']
+ }
+ });
+
+ // Authentication middleware for socket connections
+ io.use((socket, next) => {
+ // Simple authentication can be added here if needed
+ // const token = socket.handshake.auth.token;
+ // if (token validation logic) next();
+ // else next(new Error('Authentication error'));
+ next();
+ });
+
+ // Handle connection
+ io.on('connection', (socket) => {
+ console.log(`New client connected: ${socket.id}`);
+
+ // Register event handlers
+ handlers.registerHandlers(io, socket);
+
+ // Handle disconnection
+ socket.on('disconnect', () => {
+ console.log(`Client disconnected: ${socket.id}`);
+ });
+ });
+
+ return io;
+};
+
+module.exports = initializeSocket; \ No newline at end of file
diff --git a/backend/src/utils/jwt.js b/backend/src/utils/jwt.js
new file mode 100644
index 0000000..283b986
--- /dev/null
+++ b/backend/src/utils/jwt.js
@@ -0,0 +1,37 @@
+const jwt = require('jsonwebtoken');
+const env = require('../config/env');
+
+/**
+ * Generate JWT token for a user
+ * @param {Object} user - User object
+ * @returns {String} JWT token
+ */
+const generateToken = (user) => {
+ return jwt.sign(
+ {
+ id: user._id,
+ role: user.role,
+ email: user.email
+ },
+ env.JWT_SECRET,
+ { expiresIn: env.JWT_EXPIRES_IN }
+ );
+};
+
+/**
+ * Verify JWT token
+ * @param {String} token - JWT token
+ * @returns {Object} Decoded token
+ */
+const verifyToken = (token) => {
+ try {
+ return jwt.verify(token, env.JWT_SECRET);
+ } catch (error) {
+ throw new Error('Invalid token');
+ }
+};
+
+module.exports = {
+ generateToken,
+ verifyToken
+}; \ No newline at end of file