Skip to content

Commit

Permalink
Merge pull request #437 from haseebzaki-07/reservations
Browse files Browse the repository at this point in the history
Create Profile Page for users to see Reservations
  • Loading branch information
RamakrushnaBiswal authored Nov 1, 2024
2 parents 138d00d + d73fb21 commit 8b20043
Show file tree
Hide file tree
Showing 13 changed files with 239 additions and 59 deletions.
3 changes: 2 additions & 1 deletion backend/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ GOOGLE_CLIENT_SECRET=your_google_client_secret
FRONTEND_URL=your_frontend_url
CALLBACK_URL=http://localhost:3000/auth/google/callback
PROD_CALLBACK_URL=https://play-cafe.vercel.app/auth/google/callback
NODE_ENV=development
NODE_ENV=development
SECRET_KEY=your_secret_key
33 changes: 11 additions & 22 deletions backend/controller/customer.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,15 +89,14 @@ async function loginCustomer(req, res) {
password: z.string().min(6, "Password must be at least 6 characters long"),
});


const validation = customerLoginSchema.safeParse(req.body);
if (!validation.success) {
return res.status(400).json({ error: validation.error.errors });
}

try {
const customer = await Customer.findOne({ email: req.body.email });

if (!customer) {
return res.status(401).json({ error: "Invalid email or password" });
}
Expand All @@ -106,35 +105,26 @@ async function loginCustomer(req, res) {
}

const validPassword = await bcrypt.compare(req.body.password, customer.password);

if (!validPassword) {
return res.status(401).json({ error: "Invalid email or password" });
}

const payload = {
sub: customer._id,
name: customer.name, // Optional
role: "customer", // Optional
email: customer.email, // Optional
};

const token = jwt.sign(
payload,
process.env.JWT_SECRET,
{ expiresIn: "1h" } // Expires in 1 hour
);

req.session.user = {
id: customer._id,
sub: customer._id, // Use `sub` as this is a standard JWT claim for subject (user ID)
name: customer.name,
role: "customer",
email: customer.email,
};

const token = jwt.sign(payload, process.env.JWT_SECRET, { expiresIn: "1h" });

res.cookie("authToken", token, {
maxAge: 1000 * 60 * 60,
httpOnly: true,
secure: true,
maxAge: 60 * 60 * 1000, // 1 hour
httpOnly: false, // Set to false if you need access on the frontend
secure: process.env.NODE_ENV === "production", // Set `secure: true` only in production with HTTPS
sameSite: "strict", // Use `strict` to avoid CSRF in most cases
});

return res.json({
message: "Login successful",
token,
Expand All @@ -147,7 +137,6 @@ async function loginCustomer(req, res) {
});
} catch (error) {
console.error("Error during login:", error);

res.status(500).json({ error: "Internal server error" });
}
}
Expand Down
76 changes: 69 additions & 7 deletions backend/controller/reservation.controller.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
const { z } = require("zod");
const Reservation = require("../models/reservation.model");
const Customer = require("../models/customer.model"); // Import Customer model
const logger = require("../config/logger");
const { sendReservationConfirmation } = require("../config/nodemailer"); // Import your email function
const { sendReservationConfirmation } = require("../config/nodemailer"); // Import email function

// Define the Zod schema for reservation validation
const reservationSchema = z
.object({
guests: z.string(),
date: z.string(),
time: z.string(),
email: z.string().email(), // Include email validation in the schema
email: z.string().email(), // Include email validation
userId: z.string().optional(), // Make userId optional for validation
})
.strict(); // Disallow unknown keys

// Controller to create a reservation
async function createReservation(req, res) {
try {
const validationResult = reservationSchema.safeParse(req.body);
Expand All @@ -28,13 +31,29 @@ async function createReservation(req, res) {
errors: validationResult.error.errors,
});
}
const userId = req.params.id

// Create the reservation in the database
const reservation = await Reservation.create(validationResult.data);
const { email, date, guests, time } = validationResult.data;

// Find the customer by userId if provided
const customer = await Customer.findById(userId);
if (!customer) {
return res.status(404).json({
success: false,
message: "Customer not found.",
});
}

// Create the reservation in the database with userId reference
const reservation = await Reservation.create({
guests,
date,
time,
customer: customer._id, // Associate with customer
});

// Send a confirmation email
try {
const { email, date, guests, time } = validationResult.data;
await sendReservationConfirmation(email, {
reservationDate: date,
guests,
Expand All @@ -45,10 +64,10 @@ async function createReservation(req, res) {
logger.error("Error sending reservation confirmation email:", {
message: emailError.message,
});
// Email error should not block the main reservation process, so no need to return a failure response
// Email error does not block reservation creation
}

// Send the success response
// Respond with success
res.status(201).json({
success: true,
message: "Reservation created successfully",
Expand All @@ -68,6 +87,49 @@ async function createReservation(req, res) {
}
}

// Controller to fetch all reservations for a specific user
async function fetchUserReservations(req, res) {
try {
const userId = req.params.id; // Extract user ID from route parameters

if (!userId) {
return res.status(400).json({
success: false,
message: "User ID is required.",
});
}

// Find all reservations associated with the customer
const reservations = await Reservation.find({ customer: userId }).populate("customer", "name email");

if (reservations.length === 0) {
logger.info(`No reservations found for user ID: ${userId}`);
return res.status(404).json({
success: false,
message: "No reservations found for this user.",
});
}

res.status(200).json({
success: true,
message: "Reservations retrieved successfully.",
data: reservations,
});
} catch (error) {
logger.error("Error fetching user reservations:", {
message: error.message,
stack: error.stack,
userId: req.params.id,
});

res.status(500).json({
success: false,
message: "An error occurred while fetching reservations.",
});
}
}

module.exports = {
createReservation,
fetchUserReservations
};
3 changes: 2 additions & 1 deletion backend/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,16 @@ const app = express();
const port = process.env.PORT || 3000;
const session = require("express-session");
const MongoStore = require("connect-mongo");

// CORS configuration
const corsOptions = {
origin: ["http://localhost:5173", "https://play-cafe.vercel.app"],
credentials: true,
optionsSuccessStatus: 200,
};

app.use(cors(corsOptions));


app.use(express.json());
app.use('/api', newsletterRoute);

Expand Down
21 changes: 11 additions & 10 deletions backend/middlewares/sessionMiddleware.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
const sessionMiddleware = async (req, res, next)=>{
console.log(req.session.user);


if (req.session.user !== undefined) {
next();
const sessionMiddleware = (req, res, next) => {
if (req.session.user && req.session.user.id) {
next(); // Continue if session is valid and has user ID
} else {
res.status(401).send("Invalid session. Please log in again.");
res.status(401).json({
success: false,
message: "Invalid session. Please log in again.",
});
}
}

module.exports = sessionMiddleware;
};

module.exports = sessionMiddleware;

16 changes: 9 additions & 7 deletions backend/models/customer.model.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

const mongoose = require("mongoose");
const Schema = mongoose.Schema;

Expand All @@ -20,18 +19,16 @@ const customerSchema = new Schema(
verificationCode: {
type: String,
default: "",

},
otp: {
type: String,
type: String,
},
otpExpiry: {
type: Date,
type: Date,
},
isVerified: {
type: Boolean,
type: Boolean,
default: false,

},
role: {
type: String,
Expand All @@ -52,7 +49,12 @@ const customerSchema = new Schema(
ref: "Order",
},
],

reservations: [
{
type: Schema.Types.ObjectId,
ref: "Reservation", // Link to Reservation schema
},
],
},
{ timestamps: true }
);
Expand Down
5 changes: 5 additions & 0 deletions backend/models/reservation.model.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ const reservationSchema = new Schema({
type: String,
required: true,
},
customer: {
type: Schema.Types.ObjectId,
ref: "Customer", // Link back to Customer schema
required: true,
},
});

const Reservation = mongoose.model("Reservation", reservationSchema);
Expand Down
6 changes: 4 additions & 2 deletions backend/routes/reservationRouter.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
const express = require("express");
const { createReservation } = require("../controller/reservation.controller");
const { createReservation, fetchUserReservations } = require("../controller/reservation.controller");
const sessionMiddleware = require("../middlewares/sessionMiddleware");
const authenticateCustomer = require("../middlewares/authCustomer");
const router = express.Router();

router.post("/create", sessionMiddleware, createReservation);
router.post("/create/:id", authenticateCustomer, createReservation);
router.get("/get/:id", authenticateCustomer, fetchUserReservations);
router.get("/", (req, res) => {
res.json({
message: "Welcome to the restaurant reservation API!",
Expand Down
1 change: 1 addition & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"framer-motion": "^11.5.6",
"gsap": "^3.12.5",
"js-cookie": "^3.0.5",
"jwt-decode": "^4.0.0",
"lucide-react": "^0.454.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
Expand Down
Loading

0 comments on commit 8b20043

Please sign in to comment.