Skip to content

added comments to each line of code #785

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

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
11 changes: 11 additions & 0 deletions config/database.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,30 @@
// Import the mongoose library to interact with MongoDB
const mongoose = require("mongoose");

// Define an asynchronous function to connect to MongoDB
const connectDB = async () => {
try {
// Attempt to connect to the database using the connection string from the .env file
const conn = await mongoose.connect(process.env.DB_STRING, {
// Use the new URL parser to handle MongoDB connection strings
useNewUrlParser: true,
// Use the new unified topology engine for better server discovery and monitoring
useUnifiedTopology: true,
// Avoid deprecation warning for findOneAndUpdate() and similar methods
useFindAndModify: false,
// Avoid deprecation warning for ensureIndex() by using createIndex() instead
useCreateIndex: true,
});

// Log a message if the connection is successful
console.log(`MongoDB Connected: ${conn.connection.host}`);
} catch (err) {
// Log any connection errors
console.error(err);
// Exit the process with failure code if connection fails
process.exit(1);
}
};

// Export the connectDB function so it can be used in server.js or other files
module.exports = connectDB;
62 changes: 40 additions & 22 deletions config/passport.js
Original file line number Diff line number Diff line change
@@ -1,41 +1,59 @@
// Import the LocalStrategy constructor from the passport-local module
const LocalStrategy = require("passport-local").Strategy;
// Import mongoose to interact with the MongoDB database
const mongoose = require("mongoose");
// Import the User model which contains schema and methods for authentication
const User = require("../models/User");

// Export a function to configure Passport.js
module.exports = function (passport) {
// Configure the local strategy for username/password authentication
passport.use(
new LocalStrategy({ usernameField: "email" }, (email, password, done) => {
User.findOne({ email: email.toLowerCase() }, (err, user) => {
if (err) {
return done(err);
}
if (!user) {
return done(null, false, { msg: `Email ${email} not found.` });
}
if (!user.password) {
return done(null, false, {
msg:
"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.",
});
}
user.comparePassword(password, (err, isMatch) => {
new LocalStrategy(
{ usernameField: "email" }, // Use "email" instead of default "username"
(email, password, done) => {
// Find the user in the database by email (converted to lowercase)
User.findOne({ email: email.toLowerCase() }, (err, user) => {
if (err) {
return done(err);
return done(err); // Return any DB error
}
if (!user) {
// No user found with that email
return done(null, false, { msg: `Email ${email} not found.` });
}
if (isMatch) {
return done(null, user);
if (!user.password) {
// User exists but doesn't have a local password set (used a third-party login)
return done(null, false, {
msg:
"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.",
});
}
return done(null, false, { msg: "Invalid email or password." });

// If user found, compare the entered password with the hashed one in DB
user.comparePassword(password, (err, isMatch) => {
if (err) {
return done(err); // Return error if comparison fails
}
if (isMatch) {
// Password matches, login successful
return done(null, user);
}
// Password does not match
return done(null, false, { msg: "Invalid email or password." });
});
});
});
})
}
)
);

// Serialize the user ID to store in session cookie
passport.serializeUser((user, done) => {
done(null, user.id);
done(null, user.id); // Store only the user's ID in the session
});

// Deserialize the user based on ID stored in the session
passport.deserializeUser((id, done) => {
// Look up the user in the database by their ID
User.findById(id, (err, user) => done(err, user));
});
};
38 changes: 34 additions & 4 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");
const passport = require("passport"); // Passport.js for authentication
const validator = require("validator"); // To validate user input like email, password
const User = require("../models/User"); // Your User model for database interaction

// Render login page if user is not logged in, else redirect to profile
exports.getLogin = (req, res) => {
if (req.user) {
return res.redirect("/profile");
Expand All @@ -11,43 +12,56 @@ exports.getLogin = (req, res) => {
});
};

// Handle login POST request with validation and passport authentication
exports.postLogin = (req, res, next) => {
const validationErrors = [];

// Validate email format
if (!validator.isEmail(req.body.email))
validationErrors.push({ msg: "Please enter a valid email address." });

// Ensure password is not blank
if (validator.isEmpty(req.body.password))
validationErrors.push({ msg: "Password cannot be blank." });

// If validation errors exist, flash errors and redirect to login page
if (validationErrors.length) {
req.flash("errors", validationErrors);
return res.redirect("/login");
}

// Normalize the email to a standard format
req.body.email = validator.normalizeEmail(req.body.email, {
gmail_remove_dots: false,
});

// Authenticate using Passport local strategy
passport.authenticate("local", (err, user, info) => {
if (err) {
return next(err);
}
if (!user) {
// If authentication fails, flash error messages and redirect
req.flash("errors", info);
return res.redirect("/login");
}
// Log in the user if authentication is successful
req.logIn(user, (err) => {
if (err) {
return next(err);
}
// Flash success message and redirect to originally requested page or profile
req.flash("success", { msg: "Success! You are logged in." });
res.redirect(req.session.returnTo || "/profile");
});
})(req, res, next);
};

// Log out the user, destroy session and redirect to homepage
exports.logout = (req, res) => {
req.logout(() => {
console.log('User has logged out.')
})
});
req.session.destroy((err) => {
if (err)
console.log("Error : Failed to destroy the session during logout.", err);
Expand All @@ -56,6 +70,7 @@ exports.logout = (req, res) => {
});
};

// Render signup page if user is not logged in, else redirect to profile
exports.getSignup = (req, res) => {
if (req.user) {
return res.redirect("/profile");
Expand All @@ -65,47 +80,62 @@ exports.getSignup = (req, res) => {
});
};

// Handle signup POST request with validation, user creation, and login
exports.postSignup = (req, res, next) => {
const validationErrors = [];

// Validate email format
if (!validator.isEmail(req.body.email))
validationErrors.push({ msg: "Please enter a valid email address." });

// Validate password length (min 8 characters)
if (!validator.isLength(req.body.password, { min: 8 }))
validationErrors.push({
msg: "Password must be at least 8 characters long",
});

// Confirm password and confirmPassword match
if (req.body.password !== req.body.confirmPassword)
validationErrors.push({ msg: "Passwords do not match" });

// If validation errors exist, flash errors and redirect to signup page
if (validationErrors.length) {
req.flash("errors", validationErrors);
return res.redirect("../signup");
}

// Normalize email
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 user with same email or username already exists
User.findOne(
{ $or: [{ email: req.body.email }, { userName: req.body.userName }] },
(err, existingUser) => {
if (err) {
return next(err);
}
if (existingUser) {
// If user exists, flash error and redirect to signup page
req.flash("errors", {
msg: "Account with that email address or username already exists.",
});
return res.redirect("../signup");
}
// Save the new user to database
user.save((err) => {
if (err) {
return next(err);
}
// Automatically log in the newly registered user
req.logIn(user, (err) => {
if (err) {
return next(err);
Expand Down
59 changes: 38 additions & 21 deletions controllers/posts.js
Original file line number Diff line number Diff line change
@@ -1,75 +1,92 @@
const cloudinary = require("../middleware/cloudinary");
const Post = require("../models/Post");
const cloudinary = require("../middleware/cloudinary"); // Cloudinary middleware for image upload & deletion
const Post = require("../models/Post"); // Post model to interact with posts collection in DB

module.exports = {
// Get all posts created by the logged-in user and render profile page
getProfile: async (req, res) => {
try {
const posts = await Post.find({ user: req.user.id });
res.render("profile.ejs", { posts: posts, user: req.user });
const posts = await Post.find({ user: req.user.id }); // Find posts by user ID
res.render("profile.ejs", { posts: posts, user: req.user }); // Render profile with posts and user data
} catch (err) {
console.log(err);
}
},

// Get all posts for the global feed, sorted by newest first, and render feed page
getFeed: async (req, res) => {
try {
const posts = await Post.find().sort({ createdAt: "desc" }).lean();
res.render("feed.ejs", { posts: posts });
const posts = await Post.find().sort({ createdAt: "desc" }).lean(); // Find all posts, sorted descending
res.render("feed.ejs", { posts: posts }); // Render feed with posts
} catch (err) {
console.log(err);
}
},

// Get a single post by its ID and render the post details page
getPost: async (req, res) => {
try {
const post = await Post.findById(req.params.id);
res.render("post.ejs", { post: post, user: req.user });
const post = await Post.findById(req.params.id); // Find post by ID from URL param
res.render("post.ejs", { post: post, user: req.user }); // Render post page with post and user info
} catch (err) {
console.log(err);
}
},

// Create a new post with image upload via Cloudinary and save to database
createPost: async (req, res) => {
try {
// Upload image to cloudinary
// Upload image to Cloudinary using the file path from multer
const result = await cloudinary.uploader.upload(req.file.path);

// Create new post document in MongoDB with Cloudinary image details
await Post.create({
title: req.body.title,
image: result.secure_url,
cloudinaryId: result.public_id,
image: result.secure_url, // Cloudinary URL for the uploaded image
cloudinaryId: result.public_id, // Cloudinary's public ID (needed for deletion)
caption: req.body.caption,
likes: 0,
user: req.user.id,
likes: 0, // Initialize likes count to zero
user: req.user.id, // Associate post with logged-in user
});

console.log("Post has been added!");
res.redirect("/profile");
res.redirect("/profile"); // Redirect back to profile page after post creation
} catch (err) {
console.log(err);
}
},

// Increment the like count of a post by 1
likePost: async (req, res) => {
try {
await Post.findOneAndUpdate(
{ _id: req.params.id },
{ _id: req.params.id }, // Find the post by ID from URL param
{
$inc: { likes: 1 },
$inc: { likes: 1 }, // Increment likes field by 1
}
);
console.log("Likes +1");
res.redirect(`/post/${req.params.id}`);
res.redirect(`/post/${req.params.id}`); // Redirect back to the post page
} catch (err) {
console.log(err);
}
},

// Delete a post and its associated image from Cloudinary and database
deletePost: async (req, res) => {
try {
// Find post by id
// Find the post by ID
let post = await Post.findById({ _id: req.params.id });
// Delete image from cloudinary

// Delete the image from Cloudinary using its public ID
await cloudinary.uploader.destroy(post.cloudinaryId);
// Delete post from db

// Remove the post from MongoDB
await Post.remove({ _id: req.params.id });

console.log("Deleted Post");
res.redirect("/profile");
res.redirect("/profile"); // Redirect back to profile after deletion
} catch (err) {
// On error, still redirect back to profile
res.redirect("/profile");
}
},
Expand Down