diff --git a/config/database.js b/config/database.js index 24102b6d5..8dd7d2610 100644 --- a/config/database.js +++ b/config/database.js @@ -1,5 +1,6 @@ const mongoose = require("mongoose"); +// Connect to MongoDB using connection string from env variable const connectDB = async () => { try { const conn = await mongoose.connect(process.env.DB_STRING, { @@ -16,4 +17,5 @@ const connectDB = async () => { } }; +// Export the function so it can be used in our app module.exports = connectDB; diff --git a/config/passport.js b/config/passport.js index 6c058d1b8..9dfbaff74 100644 --- a/config/passport.js +++ b/config/passport.js @@ -2,13 +2,18 @@ const LocalStrategy = require("passport-local").Strategy; const mongoose = require("mongoose"); const User = require("../models/User"); +// Export the following passport configuration functions so it can be used in our app module.exports = function (passport) { + // Configure a local authentication strategy (email/password) passport.use( + // local Strategy to use "email" new LocalStrategy({ usernameField: "email" }, (email, password, done) => { + // Find a user in the database by email User.findOne({ email: email.toLowerCase() }, (err, user) => { if (err) { return done(err); } + // if no user found, auth fails if (!user) { return done(null, false, { msg: `Email ${email} not found.` }); } @@ -18,6 +23,7 @@ module.exports = function (passport) { "Your account was registered using a sign-in provider. To enable password login, sign in using a provider, and then set a password under your user profile.", }); } + // Compare provided password wth hashed password in DB user.comparePassword(password, (err, isMatch) => { if (err) { return done(err); @@ -30,11 +36,11 @@ module.exports = function (passport) { }); }) ); - + // Serialize user to session (store user id) passport.serializeUser((user, done) => { done(null, user.id); }); - + // Deserialize user from session by user id passport.deserializeUser((id, done) => { User.findById(id, (err, user) => done(err, user)); }); diff --git a/controllers/auth.js b/controllers/auth.js index 43f893aed..86bc9d85b 100644 --- a/controllers/auth.js +++ b/controllers/auth.js @@ -1,7 +1,8 @@ const passport = require("passport"); const validator = require("validator"); const User = require("../models/User"); - +// Export function so app can use them + // Show login page if logged in redirect to /profile exports.getLogin = (req, res) => { if (req.user) { return res.redirect("/profile"); @@ -10,8 +11,9 @@ exports.getLogin = (req, res) => { title: "Login", }); }; - + // Handle POST login exports.postLogin = (req, res, next) => { + // validate email const validationErrors = []; if (!validator.isEmail(req.body.email)) validationErrors.push({ msg: "Please enter a valid email address." }); @@ -22,10 +24,11 @@ exports.postLogin = (req, res, next) => { req.flash("errors", validationErrors); return res.redirect("/login"); } + // Normalize email req.body.email = validator.normalizeEmail(req.body.email, { gmail_remove_dots: false, }); - + // Use passport local strategy to authenticate and handle fail/error passport.authenticate("local", (err, user, info) => { if (err) { return next(err); @@ -43,7 +46,7 @@ exports.postLogin = (req, res, next) => { }); })(req, res, next); }; - +// Handle logout exports.logout = (req, res) => { req.logout(() => { console.log('User has logged out.') @@ -55,7 +58,7 @@ exports.logout = (req, res) => { res.redirect("/"); }); }; - +// Handle GET signup exports.getSignup = (req, res) => { if (req.user) { return res.redirect("/profile"); @@ -64,7 +67,7 @@ exports.getSignup = (req, res) => { title: "Create Account", }); }; - +// Handle POST signup exports.postSignup = (req, res, next) => { const validationErrors = []; if (!validator.isEmail(req.body.email)) @@ -83,13 +86,13 @@ exports.postSignup = (req, res, next) => { req.body.email = validator.normalizeEmail(req.body.email, { gmail_remove_dots: false, }); - + // Create a new User instance const user = new User({ userName: req.body.userName, email: req.body.email, password: req.body.password, }); - + // Check if email || username already exists User.findOne( { $or: [{ email: req.body.email }, { userName: req.body.userName }] }, (err, existingUser) => { diff --git a/controllers/comments.js b/controllers/comments.js new file mode 100644 index 000000000..426d28434 --- /dev/null +++ b/controllers/comments.js @@ -0,0 +1,50 @@ +const Comment = require("../models/Comments"); +// Export functions +module.exports = { + // Create an instance of comment for the given post + createComment: async (req, res) => { + try { + await Comment.create({ + comment: req.body.comment, + likes: 0, + post: req.params.id, + user: req.user.id, + }); + console.log("Comment has been added!"); + res.redirect("/post/"+req.params.id); + } catch (err) { + console.log(err); + } + }, + // increment the like count of the given post + likeComment: async (req, res) => { + try { + const comment = await Comment.findByIdAndUpdate( + req.params.id, + { $inc: { likes: 1 } }, + { new: false } // we only need comment.post to redirect + ); + console.log("Likes +1"); + res.redirect(`/post/${comment.post}`); + } catch (err) { + console.log(err); + res.redirect('back'); + } + }, + // delete a comment by id from the given post with authorization + deleteComment: async (req, res) => { + try { + // Find Comment by id and authorization + const comment = await Comment.findById({ _id: req.params.id }).populate({ path: 'post', select: 'user' }).lean(); + const isCommentAuthor = String(comment.user) === String(req.user._id); + const isPostAuthor = String(comment.post.user) === String(req.user._id); + // Delete comment from db + await Comment.deleteOne({ _id: req.params.id }); + console.log("Deleted Comment"); + const postId = comment.post?._id || comment.post; + return res.redirect(`/post/${postId}`); + } catch (err) { + res.redirect('back'); + } + }, +}; diff --git a/controllers/posts.js b/controllers/posts.js index a3e2dab5d..23ffd979e 100644 --- a/controllers/posts.js +++ b/controllers/posts.js @@ -1,7 +1,9 @@ const cloudinary = require("../middleware/cloudinary"); const Post = require("../models/Post"); - +const Comment = require("../models/Comments"); +// Export functions module.exports = { + // render profile for logged in user getProfile: async (req, res) => { try { const posts = await Post.find({ user: req.user.id }); @@ -10,6 +12,7 @@ module.exports = { console.log(err); } }, + // Render feed with all posts getFeed: async (req, res) => { try { const posts = await Post.find().sort({ createdAt: "desc" }).lean(); @@ -18,14 +21,17 @@ module.exports = { console.log(err); } }, + // Render a post with its comments getPost: async (req, res) => { try { const post = await Post.findById(req.params.id); - res.render("post.ejs", { post: post, user: req.user }); + const comments = await Comment.find({post: req.params.id}).sort({ createdAt: "desc" }).populate("user", "userName").lean(); + res.render("post.ejs", { post: post, user: req.user, comments: comments }); } catch (err) { console.log(err); } }, + // Create a new post createPost: async (req, res) => { try { // Upload image to cloudinary @@ -45,6 +51,7 @@ module.exports = { console.log(err); } }, + // increment the post's like count likePost: async (req, res) => { try { await Post.findOneAndUpdate( @@ -59,6 +66,7 @@ module.exports = { console.log(err); } }, + // Delete the post deletePost: async (req, res) => { try { // Find post by id diff --git a/middleware/auth.js b/middleware/auth.js index f646630da..067b0998c 100644 --- a/middleware/auth.js +++ b/middleware/auth.js @@ -1,13 +1,20 @@ +// Export functions for the app module.exports = { + // Middleware to ensure the user is Authenticated. ensureAuth: function (req, res, next) { + // passport to add isAuthenticated() to req object + // protect the pages that require login if (req.isAuthenticated()) { return next(); } else { res.redirect("/"); } }, + // Middleware to ensure the user is not Authenticated(guest) ensureGuest: function (req, res, next) { - if (!req.isAuthenticated()) { + // if user is not authenticated/guest, allow access + // protect the pages that only visible to guests + if (!req.isAuthenticated()) { return next(); } else { res.redirect("/dashboard"); diff --git a/middleware/cloudinary.js b/middleware/cloudinary.js index 0960c5b6f..e54313443 100644 --- a/middleware/cloudinary.js +++ b/middleware/cloudinary.js @@ -1,11 +1,12 @@ const cloudinary = require("cloudinary").v2; require("dotenv").config({ path: "./config/.env" }); - +// cloudinary config using env variables cloudinary.config({ cloud_name: process.env.CLOUD_NAME, api_key: process.env.API_KEY, api_secret: process.env.API_SECRET, }); +// Export functions for the app module.exports = cloudinary; diff --git a/middleware/multer.js b/middleware/multer.js index c012afe58..cc2f809f0 100644 --- a/middleware/multer.js +++ b/middleware/multer.js @@ -1,3 +1,5 @@ +// Multer is a middleware for handling multipart/form-data, for uploading files. +// Multer will not process any form which is not multipart (multipart/form-data). const multer = require("multer"); const path = require("path"); diff --git a/models/Comments.js b/models/Comments.js new file mode 100644 index 000000000..23417c6e1 --- /dev/null +++ b/models/Comments.js @@ -0,0 +1,26 @@ +const mongoose = require("mongoose"); + +const CommentSchema = new mongoose.Schema({ + comment: { + type: String, + required: true, + }, + likes: { + type: Number, + required: true, + }, + post: { + type: mongoose.Schema.Types.ObjectId, + ref: "Post", + }, + user: { + type: mongoose.Schema.Types.ObjectId, + ref: "User", + }, + createdAt: { + type: Date, + default: Date.now, + }, +}); + +module.exports = mongoose.model("Comment", CommentSchema); diff --git a/public/css/style.css b/public/css/style.css index e69de29bb..db5e566b1 100644 --- a/public/css/style.css +++ b/public/css/style.css @@ -0,0 +1,20 @@ +h1>a.text-primary { + color:#62A7D8 !important; +} + +h1>a.text-primary:hover { + color:#563B73 !important; +} + +a { + text-decoration: none; +} +.btn-primary { + background-color: #7861eb !important; + border-color: #7861eb !important; +} + +.btn-primary:hover { + background-color: #563B73 !important; + border-color: #563B73 !important; +} \ No newline at end of file diff --git a/routes/comments.js b/routes/comments.js new file mode 100644 index 000000000..307af4196 --- /dev/null +++ b/routes/comments.js @@ -0,0 +1,13 @@ +const express = require("express"); +const router = express.Router(); +const commentsController = require("../controllers/comments"); +const { ensureAuth, ensureGuest } = require("../middleware/auth"); + +//Comment Routes - simplified for now +router.post("/createComment/:id", commentsController.createComment); + +router.put("/likeComment/:id", commentsController.likeComment); + +router.delete("/deleteComment/:id", commentsController.deleteComment); + +module.exports = router; diff --git a/routes/main.js b/routes/main.js index d6883000e..92838901e 100644 --- a/routes/main.js +++ b/routes/main.js @@ -15,4 +15,5 @@ router.get("/logout", authController.logout); router.get("/signup", authController.getSignup); router.post("/signup", authController.postSignup); +// Export functions for the app module.exports = router; diff --git a/routes/posts.js b/routes/posts.js index aa463ac90..51864be8f 100644 --- a/routes/posts.js +++ b/routes/posts.js @@ -13,4 +13,5 @@ router.put("/likePost/:id", postsController.likePost); router.delete("/deletePost/:id", postsController.deletePost); +// Export functions for the app module.exports = router; diff --git a/server.js b/server.js index 1718db010..a2d4541c5 100644 --- a/server.js +++ b/server.js @@ -10,6 +10,7 @@ const logger = require("morgan"); const connectDB = require("./config/database"); const mainRoutes = require("./routes/main"); const postRoutes = require("./routes/posts"); +const commentRoutes = require("./routes/comments"); //Use .env file in config folder require("dotenv").config({ path: "./config/.env" }); @@ -56,6 +57,7 @@ app.use(flash()); //Setup Routes For Which The Server Is Listening app.use("/", mainRoutes); app.use("/post", postRoutes); +app.use("/comment", commentRoutes); //Server Running app.listen(process.env.PORT, () => { diff --git a/views/feed.ejs b/views/feed.ejs index 0ded94809..e9bc512b4 100644 --- a/views/feed.ejs +++ b/views/feed.ejs @@ -2,13 +2,15 @@
diff --git a/views/partials/header.ejs b/views/partials/header.ejs index 011ff2239..8455d85cc 100644 --- a/views/partials/header.ejs +++ b/views/partials/header.ejs @@ -28,7 +28,7 @@
-

Binary Upload Boom

+

Binary Upload Boom

The #100Devs Social Network
diff --git a/views/post.ejs b/views/post.ejs index c36a1c946..d7edd1eab 100644 --- a/views/post.ejs +++ b/views/post.ejs @@ -27,6 +27,51 @@

<%= post.caption %>

+
+

Add a comment

+
+
+ + +
+ +
+
+ +
+ +
Return to Profile Return to Feed