YelpCamp
A website that brings together all available campgrounds with user reviews so you can select the best from the best.
YelpCamp is a subtle clone of Yelp, a popular website used to rate and review Restaurants, Shopping, Nightlife, Food, Entertainment, Things to Do, Services, etc. The YelpCamp website provides you with similar services for campgrounds. Whenever you make any plans to go camping, all you need to do is, go to the site and check from the available options and select the one best suitable for you. User comments on these campgrounds will tell you about the reviews and make the selection process much easier.
Tech Stack
The front-end is designed using the features provided in Bootstrap and the back-end is implemented in Node.js using the Express framework. Passport is used to implement authentication and authorization, and all the flash messages are implemented using the connect-flash npm
package.
Implementation
- The Landing Page
This page contains a single button to go to the main website where information regarding all the campgrounds is available.
- The Feed
This page contains all the registered campgrounds. These campgrounds are fetched from the database using the .find()
method of mongoose and sent to the index.ejs
file. This file then loops through all the camprgounds and displays them. This page is rendered dynamically as it depends on the information stored in the database.
router.get("/", function(req, res) {
Campground.find({}, function(err, allCampgrounds) {
if(err) {
console.log(err);
} else {
res.render("campgrounds/index", {campgrounds: allCampgrounds});
}
});
});
- The Show Page
This page views individual campgrounds and their respective comments.
- Edit/Delete Campground
This option is provided for all the campgrounds but can only be implemented by the user that created the campground. The visibility of these buttons is controlled by the UI by comparing the id
s of the user and the author of the campground.
However, even if a user manages to reach the edit/delete page by manipulating the URL
using a service like Postman
, only authenticated users will be able to complete the request. This authentication is maintained using a middleware logic which ensures that the user is signed in and the rightful owner of that campground.
router.get("/:id/edit", middleware.isLoggedIn, middleware.checkCampgroundOwnership, function(req, res) {
Campground.findById(req.params.id, function(err, foundCampground) {
res.render("campgrounds/edit", {campground: foundCampground});
});
});
The middleware which checks for the logged in
logic is implemeneted using the .isAuthenticated
method provided in Passport.
isLoggedIn: function(req, res, next) {
if(req.isAuthenticated()) {
return next();
}
req.flash("error", "You need to be logged in to do that");
res.redirect("/login");
}
The middlware used for checking rightful ownership of the campground compares the author.id
and the user._id
of the logged in user. Only if they match, can the user edit or delete a campground.
middlewareObj.checkCampgroundOwnership = function(req, res, next) {
if(req.isAuthenticated()) {
Campground.findById(req.params.id, function(err, foundCampground) {
if(err || !foundCampground) {
req.flash("error", "Campground not found");
res.redirect("back");
} else {
if(foundCampground.author.id.equals(req.user._id)) {
next();
} else {
req.flash("error", "You don't have permission to do that");
res.redirect("back");
}
}
});
} else {
req.flash("error", "You need to be logged in to do that");
res.redirect("back");
}
}
- Edit/Delete Comments
Similar to campgrounds, a user can only delete their comments. The UI makes these options visible to the user only if they are the owner of that comment. The middleware to check comment ownership is similar to that used for campgrounds, as can be seen below.
middlewareObj.checkCommentOwnership = function(req, res, next) {
if(req.isAuthenticated()) {
Comment.findById(req.params.comment_id, function(err, foundComment) {
if(err || !foundComment) {
req.flash("error", "Comment not found");
res.redirect("back");
} else {
if(foundComment.author.id.equals(req.user._id)) {
next();
} else {
req.flash("error", "You don't have permission to do that");
res.redirect("back");
}
}
});
} else {
req.flash("error", "You need to be logged in to do that");
res.redirect("back");
}
}
Database models
There are three different models used, one for the users, one for the campgrounds and one for the comments.
- User Model
The user schema is as follows:
var UserSchema = new mongoose.Schema({
username: String,
password: String
});
- Comment Model
The comments schema is as follows:
var commentSchema = mongoose.Schema({
text: String,
author: {
id: {
type: mongoose.Schema.Types.ObjectId,
ref: "User"
},
username: String
}
});
- Campground Model
The campground schema is as follow:
var campgroundSchema = new mongoose.Schema ({
name: String,
price: String,
image: String,
description: String,
author: {
id: {
type: mongoose.Schema.Types.ObjectId,
ref: "User"
},
username: String
},
comments: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Comment"
}
]
});