Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added comments and usernames next to comments #777

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions controllers/auth.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
const passport = require("passport");
//PACKAGE TO HANDLE AUTHENTICATION. IT HAS STRATEGIES ( DIFFERENT LOGIN METHODS)TO DO DIFFERENT STUFF. IF WE WANTED TO, WE CAN USE LOCAL STRATEGIES OR GOOGLEAUTH ETC.
const validator = require("validator");
//PACKAGE TO VALIDATE THE USER
const User = require("../models/User");
//GET THE USER FROM MODELS FOLDER AND USER FILE (THE BCRYPT FILE)

exports.getLogin = (req, res) => {
//IF THE USER TAKEN FROM THE REQUEST INFORMATION, RETURN A RESPOND TO REDIRECT THE USER TO THE /TODO PATH AND RENDER LOGIN
if (req.user) {
return res.redirect("/profile");
}
Expand All @@ -12,20 +16,25 @@ exports.getLogin = (req, res) => {
};

exports.postLogin = (req, res, next) => {
//Checks whether the email provided in the request body is a valid email using validator.isEmail(). If it’s invalid, it pushes an error message into the validationErrors array.
const validationErrors = [];
if (!validator.isEmail(req.body.email))
validationErrors.push({ msg: "Please enter a valid email address." });
// checks if the password is empty using validator.isEmpty(). If the password field is blank, it adds an error message to the validationErrors array.
if (validator.isEmpty(req.body.password))
validationErrors.push({ msg: "Password cannot be blank." });
// If any validation errors are found (i.e., the validationErrors array has items), it flashes the errors to the session using req.flash('errors', validationErrors) and redirects the user back to the login page using res.redirect('/login'). This allows the user to see the error messages and correct their input.

if (validationErrors.length) {
req.flash("errors", validationErrors);
return res.redirect("/login");
}
// If the input passes the validation, the email is normalized using validator.normalizeEmail().
req.body.email = validator.normalizeEmail(req.body.email, {
gmail_remove_dots: false,
});

//AUTH THE USER USING PASSPORT LOCAL STRAEGY, IF ERROR PROCEED TO ERROR, IF NOT USER, USING FLASH, SAY USER AND PROVIDE INFO
passport.authenticate("local", (err, user, info) => {
if (err) {
return next(err);
Expand All @@ -34,20 +43,24 @@ exports.postLogin = (req, res, next) => {
req.flash("errors", info);
return res.redirect("/login");
}
//IF THE REQUEST INFO PASSED TO LOGIN FUNCTION IS SUCCESSFUL, USEFLASH TO NOTIFY A MESSAGE
req.logIn(user, (err) => {
if (err) {
return next(err);
}
req.flash("success", { msg: "Success! You are logged in." });
//REDIRECT THE USER TO THE TODO PATH
res.redirect(req.session.returnTo || "/profile");
});
})(req, res, next);
};

//CHECK THE INFO PASSED IN THE REQ BODY AND USE THE LOGOUT FUNC TO CONSOLE.LOG A MESSAGE
exports.logout = (req, res) => {
req.logout(() => {
console.log('User has logged out.')
})
//IN THE REQ BODE, DESTROY THE SESSION AND REDIRECT THE USER TO MAIN PAGE TO LOG BACK IN
req.session.destroy((err) => {
if (err)
console.log("Error : Failed to destroy the session during logout.", err);
Expand All @@ -56,25 +69,31 @@ exports.logout = (req, res) => {
});
};

//IN THE REQ BODY OF THE USER RES WITH REDIRECT TO TODOS
exports.getSignup = (req, res) => {
if (req.user) {
return res.redirect("/profile");
}
//RENDER SIGNUP AND TITLE
res.render("signup", {
title: "Create Account",
});
};

//Checks whether the email provided in the request body is a valid email using validator.isEmail(). If it’s invalid, it pushes an error message into the validationErrors array.
exports.postSignup = (req, res, next) => {
const validationErrors = [];
if (!validator.isEmail(req.body.email))
validationErrors.push({ msg: "Please enter a valid email address." });
//CHECK IF THE LEGTH OF THE PASSWORK FROM THE REQ BODY IS MIN 8 CHARACTER LONG, IF ITS NOT RETURN A MESSAGE
if (!validator.isLength(req.body.password, { min: 8 }))
validationErrors.push({
msg: "Password must be at least 8 characters long",
});
//IF THE PASSWORDS DONT MATCH, RETURN A MESSAGE
if (req.body.password !== req.body.confirmPassword)
validationErrors.push({ msg: "Passwords do not match" });
// IF THE LENGTH ISNT VALIDATED, USE FLASH TO SHOW ERRORS AND REDIRECT TO SIGNUP ROUTE.

if (validationErrors.length) {
req.flash("errors", validationErrors);
Expand All @@ -84,12 +103,14 @@ exports.postSignup = (req, res, next) => {
gmail_remove_dots: false,
});

//CREATE A NEW USER USING THE REQ BODY SUBMITTED FROM THE FORM OF THE USER AND SAVE IT AS A VAR OF USER.
const user = new User({
userName: req.body.userName,
email: req.body.email,
password: req.body.password,
});

// FIND THE USER, USING THE EMAIL AND USERNAME FROM THE REQU BODY AND IF THE USER ALREADY EXIST, USE FLASH TO INSERT AN ERROR
User.findOne(
{ $or: [{ email: req.body.email }, { userName: req.body.userName }] },
(err, existingUser) => {
Expand Down
51 changes: 51 additions & 0 deletions controllers/comments.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
const Comment = require("../models/Comment");
//LINK THE COMMENT MODEL FROM THE MODEL FOLDER AND COMMENT FILE TO USE IN THE FOLLOWING CODE

module.exports = {
//FIND THE POST FROM THE SPECIFIC USERID FROM THE REQ BODY AND RENDER THE POST AND USER IN THE PROFILE.EJS

createComment: async (req, res) => {
try {
await Comment.create({
comment: req.body.comment,
post: req.params.id,
likes: 0,
user: req.user.id,
userName: req.body.userName,
});
console.log("Comment has been added!");
res.redirect("/post/"+req.params.id);
} catch (err) {
console.log(err);
}
},
//FIND AND UPDATE THE LIKES ON A POST IN THE POST COLLECTION BY USING THE ID FROM THE REQ.PARAMS.ID AND THEN INCREMENT THE LIKES BY 1. AND THE REDIRECT THE USER TO THE POST FROM THE SPECIFIED ID
likeComment: async (req, res) => {
try {
await Comment.findOneAndUpdate(
{ _id: req.params.id },
{
$inc: { likes: 1 },
}
);
console.log("Likes +1");
res.redirect(`/post/${req.params.id}`);
} catch (err) {
console.log(err);
}
},
//FIND THE IDFROM THE REQ PARAMS ID. DELETE THE IMAGE FROM THE CLOUDINARY USING THE CLOUDINARY ID, DELETE THE POST FROM THE DB USING THE PARAMS ID, THEN REDIRECT THE USER TO THE PROFILE PAGE
deleteComment: async (req, res) => {
try {
// Find post by id
let comment = await Comment.findById({ _id: req.params.id });

// Delete post from db
await Comment.remove({ _id: req.params.id });
console.log("Deleted Comment");
res.redirect("/post/"+req.params.id);
} catch (err) {
res.redirect("/post/"+req.params.id);
}
},
};
18 changes: 17 additions & 1 deletion controllers/posts.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
const cloudinary = require("../middleware/cloudinary");
//TO SHOW THE IMAGES IN THE POST, LINK THE CLOUDINARY FROM THE MIDDLEWARE FOLDER
const Post = require("../models/Post");
//LINK THE POST MODEL FROM THE MODEL FOLDER AND POST FILE TO USE IN THE FOLLOWING CODE
const Comment = require("../models/Comment");
//LINK THE COMMENT MODEL FROM THE MODEL FOLDER AND COMMENT FILE TO USE IN THE FOLLOWING CODE


module.exports = {
//FIND THE POST FROM THE SPECIFIC USERID FROM THE REQ BODY AND RENDER THE POST AND USER IN THE PROFILE.EJS
getProfile: async (req, res) => {
try {
const posts = await Post.find({ user: req.user.id });
Expand All @@ -10,6 +16,7 @@ module.exports = {
console.log(err);
}
},
//FIND THE POST FROM THE DB AND SORT IT FROM DESCINDING ORDER AND ONLY GET THE SHORT VERSION OF THE REQ INSTEAD OF THE FULL REQ. THEN RENDER THE POST AND USER IN THE FEED.EJS
getFeed: async (req, res) => {
try {
const posts = await Post.find().sort({ createdAt: "desc" }).lean();
Expand All @@ -18,14 +25,21 @@ module.exports = {
console.log(err);
}
},
//FIND THE id IN THE DB FROM THE PARAMS ID THEN RENDER THE POST AND USER IN THE POST.EJS
//use the comment model to goto the comment collection to find all the post associated with the current post that you are actual on, sort them in desc order and shorten the info sent back.
//then render the comment to ejs
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" }).lean();

res.render("post.ejs", { post: post, user: req.user, comments: comments });
} catch (err) {
console.log(err);
}
},
//CREATE A POST TO THE DB BY ADDING THE IMAGE TO CLOUDNAIRY FROM THE FILE.PATH THEN CREATE A POST IN THE DB BY SAVING THE TITLE(FROM THE REQ BODY) , IMAGE(A URL FROM CLOUDINARY), CLOUDINARY ID(FROM CLOUDINARY AND USEFUL FOR DELTETING THE IMAGE) CAPTION(FROM THE REQ BODY) AND LIKES(SET TO 0) AND USER (FROM THE REQ BODY) AND REDIRECT THE USER TO THE PROFILE PAGE
createPost: async (req, res) => {
try {
// Upload image to cloudinary
Expand All @@ -45,6 +59,7 @@ module.exports = {
console.log(err);
}
},
//FIND AND UPDATE THE LIKES ON A POST IN THE POST COLLECTION BY USING THE ID FROM THE REQ.PARAMS.ID AND THEN INCREMENT THE LIKES BY 1. AND THE REDIRECT THE USER TO THE POST FROM THE SPECIFIED ID
likePost: async (req, res) => {
try {
await Post.findOneAndUpdate(
Expand All @@ -59,6 +74,7 @@ module.exports = {
console.log(err);
}
},
//FIND THE IDFROM THE REQ PARAMS ID. DELETE THE IMAGE FROM THE CLOUDINARY USING THE CLOUDINARY ID, DELETE THE POST FROM THE DB USING THE PARAMS ID, THEN REDIRECT THE USER TO THE PROFILE PAGE
deletePost: async (req, res) => {
try {
// Find post by id
Expand Down
2 changes: 2 additions & 0 deletions middleware/auth.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// THE ENSUREAUTH FUNCTION WILL AUTH THE REQUEST AND AALLOW YOU TO GO NEXT ELSE IF NOT AUTH RESUDIRECT TO MAIN ROUTE. If the user is not authenticated , ET THEM GO ON TO THE NEXT ROUTE. IF THEY ARE AUTHENTICAATED, REDIRECT THEM TO THE DASHBOARD

module.exports = {
ensureAuth: function (req, res, next) {
if (req.isAuthenticated()) {
Expand Down
2 changes: 2 additions & 0 deletions middleware/cloudinary.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
const cloudinary = require("cloudinary").v2;
//USE CLOUDINARE V2

require("dotenv").config({ path: "./config/.env" });
//ALLOW EXPRESS TO USE ENV TO GET ACCES TO CLOUDINARY SECRETS

cloudinary.config({
cloud_name: process.env.CLOUD_NAME,
Expand Down
11 changes: 8 additions & 3 deletions middleware/multer.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
const multer = require("multer");
//USED FOR UPLOADING IMAGES
const path = require("path");
//Imports the path module, which provides utilities for working with file and directory paths.

//Exports a configured instance of multer so it can be used elsewhere in your app.

module.exports = multer({
storage: multer.diskStorage({}),
storage: multer.diskStorage({}),//Specifies the storage engine for uploaded files.
fileFilter: (req, file, cb) => {
let ext = path.extname(file.originalname);
if (ext !== ".jpg" && ext !== ".jpeg" && ext !== ".png") {
let ext = path.extname(file.originalname);//Extracts the file extension (e.g., .jpg, .png) from the original file name of the uploaded file using path.extname.
if (ext !== ".jpg" && ext !== ".jpeg" && ext !== ".png")//Checks if the file extension is not one of the supported types (.jpg, .jpeg, .png).
{
cb(new Error("File type is not supported"), false);
return;
}
Expand Down
34 changes: 34 additions & 0 deletions models/Comment.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
const mongoose = require("mongoose");
////USE MONGOOSE TO TALK TO MONGODB DB AND GIVE US A SCHEMA TO USE

const CommentSchema = new mongoose.Schema({
//HOW TO TAKE THE DATA FROM FORM TO PUT INTO DB
comment: {// ADD comment TO THE DB AS A STRING/SENTENCE AND ITS REQUIRED
type: String,
required: true,
},
likes: {
// ADD LIKES TO THE DB AS A NUMBER AND ITS REQUIRED
type: Number,
required: true,
},
user: {
//MONGO WILL CREATE A UNIQUE USERID
type: mongoose.Schema.Types.ObjectId,
ref: "User",
},
userName: {
type: String, unique: true },
post: {
//MONGO WILL CREATE A UNIQUE PostID
type: mongoose.Schema.Types.ObjectId,
ref: "UserName",
},
createdAt: {
////MONGO WILL CREATE A UNIQUE DATE WITH THE TIME (UTC)
type: Date,
default: Date.now,
},
});

module.exports = mongoose.model("Comment", CommentSchema);
11 changes: 9 additions & 2 deletions models/Post.js
Original file line number Diff line number Diff line change
@@ -1,31 +1,38 @@
const mongoose = require("mongoose");
////USE MONGOOSE TO TALK TO MONGODB DB AND GIVE US A SCHEMA TO USE

const PostSchema = new mongoose.Schema({
title: {
//HOW TO TAKE THE DATA FROM FORM TO PUT INTO DB
title: {// ADD TITLE TO THE DB AS A STRING/SENTENCE AND ITS REQUIRED
type: String,
required: true,
},
image: {
image: {// ADD IMAGE URL TO THE DB AS A STRING AND ITS REQUIRED
type: String,
require: true,
},
cloudinaryId: {
// ADD CLOUDINARYID TO THE DB AS A STRING/SENTENCE AND ITS REQUIRED
type: String,
require: true,
},
caption: {
// ADD CAPTION TO THE DB AS A STRING/SENTENCE AND ITS REQUIRED
type: String,
required: true,
},
likes: {
// ADD LIKES TO THE DB AS A NUMBER AND ITS REQUIRED
type: Number,
required: true,
},
user: {
//MONGO WILL CREATE A UNIQUE USERID
type: mongoose.Schema.Types.ObjectId,
ref: "User",
},
createdAt: {
////MONGO WILL CREATE A UNIQUE DATE WITH THE TIME (UTC)
type: Date,
default: Date.now,
},
Expand Down
4 changes: 4 additions & 0 deletions models/User.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
const bcrypt = require("bcrypt");
//// USE THE BCRYPT MODULE TO ENCRYPT YOUR PASWWORD
const mongoose = require("mongoose");
//USE MONGOOSE TO INTERACT WITH MONGO DB AND ALLOW US TO USE SCHEMA

const UserSchema = new mongoose.Schema({
//CREATE A SCHEMA USER WHERE USER,EMAIL AND PASSWORD IS A STRING AND UNQIUE
userName: { type: String, unique: true },
email: { type: String, unique: true },
password: String,
Expand Down Expand Up @@ -40,3 +43,4 @@ UserSchema.methods.comparePassword = function comparePassword(
};

module.exports = mongoose.model("User", UserSchema);
//EXPORT THE USER AND USERSCHEMA
23 changes: 23 additions & 0 deletions routes/comments.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//USE EXPRESS TO MAKE NODEJS EASIER
const express = require("express");
//get a new router instance AND USE APP.USE LATER TO CALL THE ROUTER VAR
const router = express.Router();
//import the configuration in the multer.js located in the middleware folder. The multer allows you to upload images

const commentsController = require("../controllers/comments");
//CONNECT ENSUREAUTH FROM FOLDER OF MIDDLEWARE AND FILE OF AUTH AND ITS USED TO MAKE SURE THE USE IS ALWAYS LOGGED IN. WE USE THE CURLY BRACKETS AS DESTRUCTORS BUT IT COULD BE CHANGED TO GUEST ACCTS OR OTHERS USERS.
const { ensureAuth, ensureGuest } = require("../middleware/auth");

//Comments Routes - simplified for now


router.post("/createComment/:id", commentsController.createComment);
////IF THERES A POST REQUEST ON THE CreatePOST ROUTE, THE ROUTER WILL USE THE MULTER.JS FUNC TO UPLOAD A FILE AND THE POSTCONTROLLER WILL USE CREATEPOST FUNCTION

router.put("/likeComment/:id", commentsController.likeComment);
//IF THERES A PUT REQUEST ON THE LIKEpost ROUTE WITH THE PARAM OF /:ID FOR A SPECIFIC POST, THE ROUTER WILL USE THE POSTCONTROLLER WILL USE LIKEPOST FUNCTION

router.delete("/deleteComment/:id", commentsController.deleteComment);
//IF THERES A DELETE REQUEST ON THE DELETEPOST ROUTE WITH THE PARAM OF /:ID FOR A SPECIFIC POST, THE ROUTER WILL USE THE POSTCONTROLLER WILL USE DELETEPOST FUNCTION

module.exports = router;
Loading