diff --git a/Schema.js b/Schema.js index 09e385f..35585dc 100644 --- a/Schema.js +++ b/Schema.js @@ -7,6 +7,9 @@ module.exports.listingSchema = Joi.object({ location: Joi.string().required(), country: Joi.string().required(), price: Joi.number().required().min(0), + image: Joi.string().allow("", null), + category: Joi.string().valid('mountains', 'arctic', 'farms', 'rooms', 'trending', 'cities', 'castles', 'pools', 'camping', 'beach').required(), + maxGuests: Joi.number().integer().min(1).optional(), // multiple images images: Joi.array().items( @@ -47,10 +50,18 @@ module.exports.reviewSchema = Joi.object({ }).required() }); +module.exports.reviewSchema = Joi.object({ + review: Joi.object({ + rating: Joi.number().min(1).max(5).required(), + comment: Joi.string().required() + }).required() +}) + module.exports.bookingSchema = Joi.object({ booking: Joi.object({ checkIn: Joi.date().required(), checkOut: Joi.date().greater(Joi.ref("checkIn")).required(), + guests: Joi.number().integer().min(1).required(), guests: Joi.number().integer().min(1).required() }).required() }); \ No newline at end of file diff --git a/app.js b/app.js index f843b6a..8504ef4 100644 --- a/app.js +++ b/app.js @@ -1,3 +1,5 @@ +if (process.env.Node_ENV != "production") { + require('dotenv').config(); if (process.env.NODE_ENV !== "production") { require("dotenv").config(); } @@ -24,6 +26,16 @@ const passport = require("passport"); const LocalStrategy = require("passport-local"); const User = require("./models/user.js"); +const listingRouter = require("./routes/listing.js"); +const reviewRouter = require("./routes/review.js") +const userRouter = require("./routes/user.js"); +const plannerRouter = require("./routes/planner.js"); +const bookingRouter = require("./routes/booking.js"); +const { log } = require('console'); + +const dbUrl = process.env.ATLASDB_URL; + +const i18n = require("i18n"); // Routers const listingRouter = require("./routes/listing.js"); @@ -60,6 +72,25 @@ app.use(express.static(path.join(__dirname, "public"))); const store = MongoStore.create({ mongoUrl: dbUrl, crypto: { + secret: process.env.SECRET, + }, + touchAfter: 24 * 3600, +}) + + +store.on("error", () => { + console.log("Error in MONGO SESSION STORE"); + +}) + +const sessionOptions = { + store, + secret: process.env.SECRET, + resave: false, + saveUninitialized: true, + secret: process.env.SESSION_SECRET, + crypto:{ + secret: process.env.SESSION_SECRET secret: process.env.SESSION_SECRET, }, touchAfter: 24 * 3600, @@ -71,6 +102,7 @@ store.on("error", () => { const sessionOptions = { store, + secret: process.env.SESSION_SECRET, secret: process.env.SESSION_SECRET || "fallback_secret", resave: false, saveUninitialized: false, @@ -78,6 +110,22 @@ const sessionOptions = { expires: Date.now() + 7 * 24 * 60 * 60 * 1000, maxAge: 7 * 24 * 60 * 60 * 1000, httpOnly: true, + } +} + +// app.get("/", (req, res) => { +// res.send("hi i am root"); +// }); + + + }, + resave:false, + saveUninitialized:false, + cookie:{ + expires: Date.now() + 7*24*60*60*1000, + maxAge: 7*24*60*60*1000, + httpOnly:true + } }, }; @@ -101,6 +149,11 @@ app.use((req, res, next) => { res.locals.error = req.flash("error"); res.locals.currUser = req.user; next(); +}) + +app.use((req, res, next) => { + res.locals.currUser = req.user; + next(); }); // ================= ROUTES ================= @@ -113,6 +166,25 @@ app.use("/planner", plannerRouter); // ================= ERROR HANDLING ================= +app.use("/listings", listingRouter); +app.use("/listings/:id/reviews", reviewRouter); +app.use("/listings/:id/bookings", bookingRouter); +app.use("/", userRouter); +app.use("/planner", plannerRouter); + +app.all("*", (req, res, next) => { + next(new ExpressError(404, "page not found")); +}) + +app.use((err, req, res, next) => { + let { statusCode = 400, message = "something went wrong" } = err; + // res.status(statusCode).send(message); + res.status(statusCode).render("error.ejs", { message }); +// ================== ERROR HANDLING ================== +// ================= ERROR HANDLER ================= + +app.all("*",(req,res,next)=>{ + next(new ExpressError(404,"Page Not Found")); app.all("*", (req, res, next) => { next(new ExpressError(404, "Page Not Found")); }); diff --git a/controllers/bookings.js b/controllers/bookings.js index 6f5838f..52a0c8c 100644 --- a/controllers/bookings.js +++ b/controllers/bookings.js @@ -12,6 +12,11 @@ module.exports.createBooking = async (req, res) => { return res.redirect("/listings"); } + if (listing.owner.equals(req.user._id)) { + req.flash("error", "You cannot book your own listing."); + return res.redirect(`/listings/${id}`); + } + const { checkIn, checkOut, guests } = req.body.booking; const numGuests = parseInt(guests); diff --git a/controllers/users.js b/controllers/users.js index 6337d9d..81c8a48 100644 --- a/controllers/users.js +++ b/controllers/users.js @@ -1,4 +1,9 @@ const User = require("../models/user"); +const Booking = require("../models/booking"); + +module.exports.renderSignUpForm = (req, res) => { + res.render("users/signup.ejs") +} // --- Signup --- module.exports.renderSignUpForm = (req, res) => { @@ -8,6 +13,48 @@ module.exports.renderSignUpForm = (req, res) => { module.exports.signUp = async (req, res, next) => { try { let { username, email, password } = req.body; + const newUSer = new User({ email, username }) + const registeredUser = await User.register(newUSer, password); + + req.login(registeredUser, (err) => { + if (err) { + return next(err); + } + req.flash("success", "Welcome to WanderLust "); + res.redirect("/listings"); + }) + + } + catch (err) { + req.flash("error", err.message) + res.redirect("/signup") + } +} + +module.exports.renderLoginForm = (req, res) => { + res.render("users/login.ejs") +} + +module.exports.login = async (req, res) => { + req.flash("success", "welcome back to WanderLust ! ") + let redirectUrl = res.locals.redirectUrl || "/listings" + res.redirect(redirectUrl); +} + +module.exports.logout = (req, res, next) => { + req.logOut((err => { + if (err) { + next(err); + } + req.flash("success", "You are logged Out!"); + res.redirect("/listings"); + })) +} + +module.exports.renderDashboard = async (req, res) => { + // Populate listing to show details on the dashboard + const bookings = await Booking.find({ user: req.user._id }).populate("listing"); + res.render("users/dashboard.ejs", { bookings }); const newUser = new User({ email, username }); const registeredUser = await User.register(newUser, password); diff --git a/models/listing.js b/models/listing.js index 086ea1e..a2abb14 100644 --- a/models/listing.js +++ b/models/listing.js @@ -10,6 +10,20 @@ const listingSchema = new Schema({ required: true, }, description: String, + image: { + url: String, + filename: String, + }, + price: Number, + maxGuests: { + type: Number, + default: 1, + min: 1, + }, + location: String, + country: String, + }, + description: String, // main thumbnail image: { @@ -42,6 +56,34 @@ const listingSchema = new Schema({ ref: "Review", } ], + owner: { + type: Schema.Types.ObjectId, + ref: "User", + }, + geometry: { + type: { + type: String, // Don't do `{ location: { type: String } }` + enum: ['Point'], // 'location.type' must be 'Point' + required: true + }, + coordinates: { + type: [Number], + required: true + } + + }, + category: { + type: String, + enum: ["mountains", "arctic", "farms", "rooms", "cities", "castles", "pools", "camping", "beach", "trending"] + }, +}) + +listingSchema.post("findOneAndDelete", async (listing) => { + if (listing) { + await Review.deleteMany({ _id: { $in: listing.reviews } }) + } + +}) owner: { type: Schema.Types.ObjectId, diff --git a/routes/user.js b/routes/user.js index 866752b..1926c50 100644 --- a/routes/user.js +++ b/routes/user.js @@ -1,9 +1,9 @@ const express = require("express"); -const router = express.Router({mergeParams:true}); +const router = express.Router({ mergeParams: true }); const User = require("../models/user.js"); const wrapAsync = require("../utils/wrapAsync"); const passport = require("passport"); -const {saveRedirectUrl}= require("../middleware.js") +const { saveRedirectUrl, isLoggedIn } = require("../middleware.js") const userController = require("../controllers/users.js") router.get("/forgot-password", userController.renderForgotPasswordForm); @@ -12,16 +12,18 @@ router.post("/forgot-password", userController.forgotPassword); router.get("/reset-password/:token", userController.renderResetPasswordForm); router.post("/reset-password/:token", userController.resetPassword); router.route("/signup") -.get(userController.renderSignUpForm) -.post(wrapAsync(userController.signUp)); + .get(userController.renderSignUpForm) + .post(wrapAsync(userController.signUp)); router.route("/login") -.get(userController.renderLoginForm) -.post( - saveRedirectUrl, - passport.authenticate("local",{failureRedirect:"/login",failureFlash:true}) ,userController.login); -router.get("/logout",userController.logout) - + .get(userController.renderLoginForm) + .post( + saveRedirectUrl, + passport.authenticate("local", { failureRedirect: "/login", failureFlash: true }), userController.login); +router.get("/logout", userController.logout) + +router.route("/dashboard") + .get(isLoggedIn, wrapAsync(userController.renderDashboard)); // Render Forgot Password form module.exports.renderForgotPasswordForm = (req, res) => { res.render("users/forgotPassword.ejs"); @@ -89,4 +91,4 @@ module.exports.resetPassword = async (req, res) => { res.redirect("/login"); }; -module.exports =router; \ No newline at end of file +module.exports = router; \ No newline at end of file diff --git a/views/includes/navbar.ejs b/views/includes/navbar.ejs index a57c632..0d6ed9f 100644 --- a/views/includes/navbar.ejs +++ b/views/includes/navbar.ejs @@ -62,6 +62,7 @@ Login <% } %> <% if( currUser){ %> + My Bookings My Trips Logout diff --git a/views/listings/edit.ejs b/views/listings/edit.ejs index fe91250..3472a63 100644 --- a/views/listings/edit.ejs +++ b/views/listings/edit.ejs @@ -1,5 +1,29 @@ <%= layout("/layouts/boilerplate.ejs") %> +
+