Skip to content

Commit e6c9005

Browse files
committed
edited app.js
1 parent 6c0f154 commit e6c9005

36 files changed

+5844
-200
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
node_modules/
2-
package.json
2+
.env
33
package-lock.json
4+
images/

ErrorSchemas.js

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,42 @@
1-
const Joi = require('joi')
1+
const BaseJoi = require('joi');
2+
const sanitizeHtml = require('sanitize-html');
3+
4+
const extension = (joi) => ({
5+
type: 'string',
6+
base: joi.string(),
7+
messages: {
8+
'string.escapeHTML': '{{#label}} must not include HTML!'
9+
},
10+
rules: {
11+
escapeHTML: {
12+
validate(value, helpers) {
13+
const clean = sanitizeHtml(value, {
14+
allowedTags: [],
15+
allowedAttributes: {},
16+
});
17+
if (clean !== value) return helpers.error('string.escapeHTML', { value })
18+
return clean;
19+
}
20+
}
21+
}
22+
});
23+
24+
const Joi = BaseJoi.extend(extension)
25+
226
module.exports.campgroundSchema = Joi.object({
327
campground: Joi.object({
4-
title: Joi.string().required(),
28+
title: Joi.string().required().escapeHTML(),
529
price: Joi.number().required().min(0),
6-
image: Joi.string().required(),
7-
location: Joi.string().required(),
8-
description: Joi.string().required()
9-
}).required()
30+
// image: Joi.string().required(),
31+
location: Joi.string().required().escapeHTML(),
32+
description: Joi.string().required().escapeHTML()
33+
}).required(),
34+
deleteImages: Joi.array()
1035
});
1136

1237
module.exports.reviewSchema = Joi.object({
1338
review: Joi.object({
1439
rating: Joi.number().required().min(1).max(5),
15-
body: Joi.string().required()
40+
body: Joi.string().required().escapeHTML()
1641
}).required()
1742
})

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# ratemycamp
2+
$ npm install passport passport-local pass passport-local-mongoose
3+
// Passport local passportLocalMongoose will add a Username,hash and salt field to store the username,the hashed password and the salt value.

app.js

Lines changed: 85 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,32 @@
1+
if (process.env.NODE_ENV !== "production") {
2+
require('dotenv').config();
3+
}
4+
15
const express = require('express');
26
const path = require('path');
37
const mongoose = require('mongoose');
48
const ejsMate = require('ejs-mate');
5-
const Joi = require('joi')
6-
const { campgroundSchema, reviewSchema } = require('./ErrorSchemas')
9+
10+
const session = require('express-session');
11+
const flash = require('connect-flash');
12+
const passport = require('passport');
13+
const LocalStrategy = require('passport-local');
14+
const User = require('./models/user');
15+
716
const ExpressError = require('./utils/ExpressError')
8-
const CatchAsync = require('./utils/CatchAsync')
9-
const Campground = require('./models/campground')
10-
const Review = require('./models/review')
1117
const methodOverride = require('method-override');
12-
const review = require('./models/review');
1318

14-
mongoose.connect('mongodb://localhost:27017/yelp-camp', {
19+
const userRoutes = require('./routes/users')
20+
const campgroundRoutes = require('./routes/campgrounds')
21+
const reviewRoutes = require('./routes/reviews')
22+
const mongoSanitize = require('express-mongo-sanitize');
23+
const helmet = require("helmet");
24+
25+
// setting Mongo Atlas
26+
const dbUrl = process.env.DB_URL || 'mongodb://localhost:27017/yelp-camp';
27+
const MongoStore = require('connect-mongo');
28+
29+
mongoose.connect(dbUrl, {
1530
useNewUrlParser: true,
1631
// useCreateIndex: true,
1732
useUnifiedTopology: true
@@ -28,98 +43,80 @@ const app = express();
2843
app.engine('ejs', ejsMate);
2944
app.set('view engine', 'ejs');
3045
app.set('views', path.join(__dirname, 'views'))
46+
3147
app.use(express.urlencoded({ extended: true }))
3248
app.use(methodOverride('_method'))
49+
app.use(express.static(path.join(__dirname, 'public')))
50+
51+
app.use(
52+
mongoSanitize({
53+
replaceWith: '_',
54+
}),
55+
);
56+
57+
app.use(
58+
helmet({
59+
contentSecurityPolicy: false,
60+
})
61+
);
62+
63+
const secret = process.env.SECRET || 'thisshouldbeabettersecret!'
64+
const store = MongoStore.create({
65+
mongoUrl: dbUrl,
66+
secret,
67+
touchAfter: 24 * 60 * 60,
68+
// crypto: {
69+
// secret: 'thisshouldbeabettersecret!',
70+
// }
71+
});
3372

34-
const validateCampground = (req, res, next) => {
35-
const { error } = campgroundSchema.validate(req.body)
36-
if (error) {
37-
const msg = error.details.map(el => el.message).join(',')
38-
throw new ExpressError(msg, 400)
39-
} else {
40-
next();
73+
store.on("error", function(e) {
74+
console.log("SESSION STORE ERROR", e)
75+
})
76+
const sessionConfig = {
77+
store,
78+
name: 'session',
79+
secret,
80+
resave: false,
81+
saveUninitialized: true,
82+
cookie: {
83+
httpOnly: true,
84+
// secure: true
85+
expires: Date.now() + 1000 * 60 * 60 * 24 * 7,
86+
maxAge: 1000 * 60 * 60 * 24 * 7
4187
}
4288
}
89+
app.use(session(sessionConfig))
90+
app.use(flash());
91+
92+
// middleware for passport
93+
app.use(passport.initialize());
94+
app.use(passport.session());
95+
// use static authenticate method of model in LocalStrategy
96+
passport.use(new LocalStrategy(User.authenticate()));
97+
// use static serialize and deserialize of model for passport session support
98+
passport.serializeUser(User.serializeUser());
99+
passport.deserializeUser(User.deserializeUser());
100+
101+
// Flash middleware
102+
app.use((req, res, next) => {
103+
// console.log(req.session);
104+
// checking for the current status of user whether user is already logged in or not
105+
res.locals.currentUser = req.user;
106+
res.locals.success = req.flash('success');
107+
res.locals.error = req.flash('error');
108+
next();
109+
})
43110

44-
const validateReview = (req, res, next) => {
45-
const { error } = reviewSchema.validate(req.body)
46-
if (error) {
47-
const msg = error.details.map(el => el.message).join(',')
48-
throw new ExpressError(msg, 400)
49-
} else {
50-
next();
51-
}
52-
}
111+
// Defining Routes
112+
app.use('/', userRoutes)
113+
app.use('/campgrounds', campgroundRoutes)
114+
app.use('/campgrounds/:id/reviews', reviewRoutes)
53115

54116
app.get('/', (req, res) => {
55117
res.render('home')
56118
})
57119

58-
app.get('/campgrounds', CatchAsync(async(req, res) => {
59-
const campgrounds = await Campground.find({});
60-
res.render('campgrounds/index', { campgrounds })
61-
}))
62-
63-
// Creating New Campground
64-
app.get('/campgrounds/new', (req, res) => {
65-
res.render('campgrounds/new')
66-
})
67-
68-
app.post('/campgrounds', validateCampground, CatchAsync(async(req, res, next) => {
69-
// if (!req.body.campground) throw new ExpressError('Invalid campground data', 400);
70-
const campground = new Campground(req.body.campground)
71-
await campground.save();
72-
res.redirect(`/campgrounds/${campground._id}`)
73-
}))
74-
75-
// To show all campgrounds
76-
app.get('/campgrounds/:id', CatchAsync(async(req, res) => {
77-
const campground = await Campground.findById(req.params.id).populate('reviews');
78-
// We used .populate method so that review of that particular campground(because of id) can be shown
79-
res.render('campgrounds/show', { campground })
80-
}))
81-
82-
// Update and edit
83-
app.get('/campgrounds/:id/edit', CatchAsync(async(req, res) => {
84-
const campground = await Campground.findById(req.params.id);
85-
res.render('campgrounds/edit', { campground })
86-
}))
87-
88-
app.put('/campgrounds/:id', validateCampground, CatchAsync(async(req, res) => {
89-
const { id } = req.params;
90-
const campground = await Campground.findByIdAndUpdate(id, req.body.campground);
91-
res.redirect(`/campgrounds/${campground._id}`)
92-
}))
93-
94-
// Delete
95-
app.delete('/campgrounds/:id', CatchAsync(async(req, res) => {
96-
const { id } = req.params;
97-
await Campground.findByIdAndDelete(id)
98-
res.redirect('/campgrounds');
99-
}))
100-
101-
// Review Routes
102-
app.post('/campgrounds/:id/reviews', validateReview, CatchAsync(async(req, res) => {
103-
const campground = await Campground.findById(req.params.id);
104-
// To instantiate new Review model we need to import the module
105-
const review = new Review(req.body.review);
106-
// Here req.body.review is what we gave in show.ejs>form>review[rating] and review[body]
107-
// Now push the new review into campground.reviews array
108-
campground.reviews.push(review);
109-
campground.save();
110-
review.save();
111-
res.redirect(`/campgrounds/${campground._id}`);
112-
}))
113-
114-
// delete reviews
115-
app.delete('/campgrounds/:id/reviews/:reviewID', CatchAsync(async(req, res) => {
116-
const { id, reviewID } = req.params;
117-
// so the problem is our reviewID is assosciated to campgroundId so if we delete using reviewID the whole campground gets deleted. [13213,123123,141324] suupose this is an array of object ID's adn we wamt to delete the specific ID that belogs to our review id so we will use an poerator in mongo called $pull operator
118-
await Campground.findByIdAndUpdate(id, { $pull: { reviews: reviewID } });
119-
await Review.findByIdAndDelete(reviewID);
120-
res.redirect(`/campgrounds/${id}`);
121-
}))
122-
123120
// handling errors
124121

125122
app.all('*', (req, res, next) => {

cloudinary/index.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
const cloudinary = require('cloudinary').v2;
2+
const { CloudinaryStorage } = require('multer-storage-cloudinary');
3+
4+
5+
// specifying our credentials
6+
cloudinary.config({
7+
cloud_name: process.env.CLOUDINARY_CLOUD_NAME,
8+
api_key: process.env.CLOUDINARY_KEY,
9+
api_secret: process.env.CLOUDINARY_SECRET
10+
});
11+
12+
// making the new instance of cloudinary storage and passing cloudinary through multer instead of storing images to our local pc
13+
const storage = new CloudinaryStorage({
14+
cloudinary,
15+
params: {
16+
folder: 'RateMyCamp',
17+
allowedformats: ['jpeg', 'jpg', 'png']
18+
}
19+
});
20+
21+
module.exports = {
22+
cloudinary,
23+
storage
24+
}

controllers/campgrounds.js

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
const Campground = require('../models/campground');
2+
const { cloudinary } = require('../cloudinary');
3+
const mbxGeocoding = require("@mapbox/mapbox-sdk/services/geocoding");
4+
const mapBoxToken = process.env.MAPBOX_TOKEN;
5+
const geocoder = mbxGeocoding({ accessToken: mapBoxToken });
6+
7+
module.exports.index = async(req, res) => {
8+
const campgrounds = await Campground.find({})
9+
res.render('campgrounds/index', { campgrounds })
10+
}
11+
12+
module.exports.renderNewForm = (req, res) => {
13+
res.render('campgrounds/new');
14+
}
15+
16+
module.exports.createCampground = async(req, res, next) => {
17+
const geoData = await geocoder.forwardGeocode({
18+
query: req.body.campground.location,
19+
limit: 1
20+
}).send()
21+
const campground = new Campground(req.body.campground)
22+
campground.geometry = geoData.body.features[0].geometry;
23+
campground.images = req.files.map(f => ({ url: f.path, filename: f.filename }));
24+
campground.author = req.user._id;
25+
// here if some new user logs in it will detect its id so that they can upload the campground and author's name will pop up on the show page
26+
await campground.save();
27+
console.log(campground)
28+
req.flash('success', 'Successfully created a new campground!')
29+
res.redirect(`/campgrounds/${campground._id}`)
30+
}
31+
32+
module.exports.showCampground = async(req, res) => {
33+
const campground = await Campground.findById(req.params.id).populate({
34+
path: 'reviews',
35+
populate: {
36+
path: 'author'
37+
}
38+
}).populate('author');
39+
console.log(campground);
40+
// We used .populate method so that review of that particular campground(because of id) can be shown
41+
if (!campground) {
42+
req.flash('error', 'Oops :( Cannot find that campground!')
43+
return res.redirect('/campgrounds')
44+
}
45+
res.render('campgrounds/show', { campground })
46+
}
47+
48+
module.exports.renderEditForm = async(req, res) => {
49+
const { id } = req.params;
50+
const campground = await Campground.findById(id)
51+
if (!campground) {
52+
req.flash('error', 'Oops :( Cannot find that campground!')
53+
return res.redirect('/campgrounds')
54+
}
55+
res.render('campgrounds/edit', { campground })
56+
}
57+
58+
module.exports.updateCampground = async(req, res) => {
59+
const { id } = req.params;
60+
console.log(req.body);
61+
const campground = await Campground.findByIdAndUpdate(id, req.body.campground);
62+
const imgs = req.files.map(f => ({ url: f.path, filename: f.filename }));
63+
campground.images.push(...imgs);
64+
await campground.save();
65+
if (req.body.deleteImages) {
66+
for (let filename of req.body.deleteImages) {
67+
await cloudinary.uploader.destroy(filename);
68+
}
69+
await campground.updateOne({ $pull: { images: { filename: { $in: req.body.deleteImages } } } })
70+
}
71+
req.flash('success', 'Successfully updated campground!');
72+
res.redirect(`/campgrounds/${campground._id}`);
73+
}
74+
75+
module.exports.deleteCampground = async(req, res) => {
76+
const { id } = req.params;
77+
await Campground.findByIdAndDelete(id);
78+
req.flash('success', 'Successfully deleted a campground!')
79+
res.redirect('/campgrounds');
80+
}

controllers/reviews.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
const Campground = require('../models/campground');
2+
const Review = require('../models/review');
3+
4+
module.exports.createReview = async(req, res) => {
5+
const campground = await Campground.findById(req.params.id);
6+
// To instantiate new Review model we need to import the module
7+
const review = new Review(req.body.review);
8+
// Here req.body.review is what we gave in show.ejs>form>review[rating] and review[body]
9+
// Now push the new review into campground.reviews array
10+
review.author = req.user._id;
11+
campground.reviews.push(review);
12+
campground.save();
13+
review.save();
14+
req.flash('success', 'Successfully created a review!')
15+
res.redirect(`/campgrounds/${campground._id}`);
16+
}
17+
18+
module.exports.deleteReview = async(req, res) => {
19+
const { id, reviewID } = req.params;
20+
// so the problem is our reviewID is assosciated to campgroundId so if we delete using reviewID the whole campground gets deleted.[13213,123123,141324] suupose this is an array of object ID's and we want to delete the specific ID that belogs to our review id so we will use an operator in mongo called $pull operator
21+
await Campground.findByIdAndUpdate(id, { $pull: { reviews: reviewID } });
22+
await Review.findByIdAndDelete(reviewID);
23+
req.flash('success', 'Successfully deleted a review!')
24+
res.redirect(`/campgrounds/${id}`);
25+
}

0 commit comments

Comments
 (0)