Skip to content
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
2 changes: 2 additions & 0 deletions config/database.js
Original file line number Diff line number Diff line change
@@ -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, {
Expand All @@ -16,4 +17,5 @@ const connectDB = async () => {
}
};

// Export the function so it can be used in our app
module.exports = connectDB;
10 changes: 8 additions & 2 deletions config/passport.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.` });
}
Expand All @@ -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);
Expand All @@ -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));
});
Expand Down
19 changes: 11 additions & 8 deletions controllers/auth.js
Original file line number Diff line number Diff line change
@@ -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");
Expand All @@ -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." });
Expand All @@ -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);
Expand All @@ -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.')
Expand All @@ -55,7 +58,7 @@ exports.logout = (req, res) => {
res.redirect("/");
});
};

// Handle GET signup
exports.getSignup = (req, res) => {
if (req.user) {
return res.redirect("/profile");
Expand All @@ -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))
Expand All @@ -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) => {
Expand Down
50 changes: 50 additions & 0 deletions controllers/comments.js
Original file line number Diff line number Diff line change
@@ -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');
}
},
};
12 changes: 10 additions & 2 deletions controllers/posts.js
Original file line number Diff line number Diff line change
@@ -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 });
Expand All @@ -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();
Expand All @@ -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
Expand All @@ -45,6 +51,7 @@ module.exports = {
console.log(err);
}
},
// increment the post's like count
likePost: async (req, res) => {
try {
await Post.findOneAndUpdate(
Expand All @@ -59,6 +66,7 @@ module.exports = {
console.log(err);
}
},
// Delete the post
deletePost: async (req, res) => {
try {
// Find post by id
Expand Down
9 changes: 8 additions & 1 deletion middleware/auth.js
Original file line number Diff line number Diff line change
@@ -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");
Expand Down
3 changes: 2 additions & 1 deletion middleware/cloudinary.js
Original file line number Diff line number Diff line change
@@ -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;
2 changes: 2 additions & 0 deletions middleware/multer.js
Original file line number Diff line number Diff line change
@@ -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");

Expand Down
26 changes: 26 additions & 0 deletions models/Comments.js
Original file line number Diff line number Diff line change
@@ -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);
20 changes: 20 additions & 0 deletions public/css/style.css
Original file line number Diff line number Diff line change
@@ -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;
}
13 changes: 13 additions & 0 deletions routes/comments.js
Original file line number Diff line number Diff line change
@@ -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;
1 change: 1 addition & 0 deletions routes/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
1 change: 1 addition & 0 deletions routes/posts.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@ router.put("/likePost/:id", postsController.likePost);

router.delete("/deletePost/:id", postsController.deletePost);

// Export functions for the app
module.exports = router;
2 changes: 2 additions & 0 deletions server.js
Original file line number Diff line number Diff line change
Expand Up @@ -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" });
Expand Down Expand Up @@ -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, () => {
Expand Down
10 changes: 6 additions & 4 deletions views/feed.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@
<div class="container">
<div class="row justify-content-center mt-5">
<ul class="row list-unstyled">
<% for(var i=0; i<posts.length; i++) {%>
<% posts.forEach(post => {%>
<li class="col-6 justify-content-between mt-5">
<a href="/post/<%= posts[i]._id%>">
<img class="img-fluid" src="<%= posts[i].image%>">
<a href="/post/<%= post._id%>">
<div class="ratio ratio-1x1">
<img src="<%= post.image %>" class="w-100 h-100 rounded" style="object-fit: contain;">
</div>
</a>
</li>
<% } %>
<% }) %>
</ul>
</div>
</div>
Expand Down
2 changes: 1 addition & 1 deletion views/partials/header.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
<body>
<header class="container">
<div class="text-center">
<h1 class=""><a href="/profile">Binary Upload Boom</a></h1>
<h1 ><a href="/profile" class="text-primary">Binary Upload Boom</a></h1>
<span>The #100Devs Social Network</span>
</div>
</header>
Expand Down
Loading