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
57 changes: 53 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,34 @@
# Install

![BUB-social-media-demo](https://github.com/user-attachments/assets/f7174646-b75e-45c8-9185-94b35598431d)

# BINARY UPLOAD BOOM

## Introduction

This is forked from a Simple Social Media App built using the MVC Architecture with multiple related schemas, implementing "authorization" so folx can sign up, customize & personalize the app, and cloudinary to store and retrieve uploaded media. The theme is based on the class inside joke from Independence Day "Binary Upload Boom". Because the original had started using Bootstrap for base styles, I used this project to learn and implement more Bootstrap.

---

## Features

- User sessions: sign up, log in, personalized profile page and delete permissions
- Posts: adding titles, images, captions, counts of likes
- Comments: comment on each post, like the comments, only post creator and comment writer can delete
- Packages have been updated and deprecated code adjusted from the original fork

---

## Local Setup

Install all the dependencies or node packages used for development via Terminal
`npm install`

Run
`npm start`

---

# Things to add
## Things to add

- Create a `.env` file in config folder and add the following as `key = value`
- PORT = 2121 (can be any port example: 3000)
Expand All @@ -15,6 +39,31 @@

---

# Run
## Packages/Dependencies used

- bcrypt - hashing/salting/encrypting in order to not have plain text passwords stored in the database
- cloudinary - upload and serve media (used for storing post images)
- connect-mongo - helps with session storage in the database
- dotenv - to use environment variable files
- ejs - templating for rendering dynamic data to html
- express - node framework for easier setup
- express-flash - flash messages without request redirection (error messaging for forms)
- express-session - using cookies along with the db to keep track of logged in users
- method-override - override the default browser form methods of GET and POST to do be able to use DELETE and PUT
- moment - format dates to be more readable to users
- mongodb - to connect to mongo database
- mongoose - easily set up schemas for data being sent and stored in mongodb
- morgan - logging activity in the console
- multer - handles file uploads in forms and makes them accessible in routes
- nodemon - auto restart server after changes
- passport - strategies for authentication
- passport-local - allow user to make an account with sign in data instead of a different strategy
- validator - checks validity of strings to make sure user will enter the required data

---






`npm start`
8 changes: 1 addition & 7 deletions config/database.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,7 @@ const mongoose = require("mongoose");

const connectDB = async () => {
try {
const conn = await mongoose.connect(process.env.DB_STRING, {
useNewUrlParser: true,
useUnifiedTopology: true,
useFindAndModify: false,
useCreateIndex: true,
});

const conn = await mongoose.connect(process.env.DB_STRING);
console.log(`MongoDB Connected: ${conn.connection.host}`);
} catch (err) {
console.error(err);
Expand Down
37 changes: 20 additions & 17 deletions config/passport.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,9 @@ const User = require("../models/User");

module.exports = function (passport) {
passport.use(
new LocalStrategy({ usernameField: "email" }, (email, password, done) => {
User.findOne({ email: email.toLowerCase() }, (err, user) => {
if (err) {
return done(err);
}
new LocalStrategy({ usernameField: "email" }, async (email, password, done) => {
try{
const user = await User.findOne({ email: email.toLowerCase() });
if (!user) {
return done(null, false, { msg: `Email ${email} not found.` });
}
Expand All @@ -18,24 +16,29 @@ 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.",
});
}
user.comparePassword(password, (err, isMatch) => {
if (err) {
return done(err);
}
if (isMatch) {
return done(null, user);
}
return done(null, false, { msg: "Invalid email or password." });
});
});
const isMatch = await user.comparePassword(password)
if (isMatch) {
return done(null, user);
}
return done(null, false, { msg: "Invalid email or password." });

}catch (err){
return done(err);
}

})
);

passport.serializeUser((user, done) => {
done(null, user.id);
});

passport.deserializeUser((id, done) => {
User.findById(id, (err, user) => done(err, user));
passport.deserializeUser(async (id, done) => {
try{
const user = await User.findById(id);
done(null, user);
} catch (err) {
done(err);
}
});
};
120 changes: 72 additions & 48 deletions controllers/auth.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ exports.getLogin = (req, res) => {
});
};

exports.postLogin = (req, res, next) => {
exports.postLogin = async (req, res, next) => {
const validationErrors = [];
if (!validator.isEmail(req.body.email))
validationErrors.push({ msg: "Please enter a valid email address." });
Expand All @@ -26,34 +26,54 @@ exports.postLogin = (req, res, next) => {
gmail_remove_dots: false,
});

passport.authenticate("local", (err, user, info) => {
if (err) {
return next(err);
}
if (!user) {
req.flash("errors", info);
return res.redirect("/login");
}
req.logIn(user, (err) => {
if (err) {
return next(err);
try{
const { user, info } = await new Promise((resolve, reject) => {
passport.authenticate("local", (err, user, info) => {
if (err) return reject(err);
resolve({ user, info });
})(req, res, next);
});
if (!user) {
req.flash("errors", info);
return res.redirect("/login");
}

await new Promise ((resolve, reject) => {
req.logIn(user, (err) => {
if (err) reject(err);
else resolve();
});
});

req.flash("success", { msg: "Success! You are logged in." });
res.redirect(req.session.returnTo || "/profile");
});
})(req, res, next);
res.redirect(req.session.returnTo || "/profile");
} catch (err){
return next(err);
};
};

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);
exports.logout = async (req, res) => {
try{
await new Promise((resolve, reject) => {
req.logout((err) =>{
if (err) reject(err);
else resolve();
});
});

console.log('User has logged out.');

await new Promise ((resolve, reject) =>{
req.session.destroy((err)=> {
if (err) reject(err);
else resolve();
});
});
req.user = null;
res.redirect("/");
});
} catch (err) {
console.log("Error : Failed to destroy the session during logout.", err);
}
};

exports.getSignup = (req, res) => {
Expand All @@ -65,7 +85,7 @@ exports.getSignup = (req, res) => {
});
};

exports.postSignup = (req, res, next) => {
exports.postSignup = async (req, res, next) => {
const validationErrors = [];
if (!validator.isEmail(req.body.email))
validationErrors.push({ msg: "Please enter a valid email address." });
Expand All @@ -90,29 +110,33 @@ exports.postSignup = (req, res, next) => {
password: req.body.password,
});

User.findOne(
{ $or: [{ email: req.body.email }, { userName: req.body.userName }] },
(err, existingUser) => {
if (err) {
return next(err);
}
if (existingUser) {
req.flash("errors", {
msg: "Account with that email address or username already exists.",
});
return res.redirect("../signup");
}
user.save((err) => {
if (err) {
return next(err);
}
req.logIn(user, (err) => {
if (err) {
return next(err);
}
res.redirect("/profile");
});
try {
const existingUser = await User.findOne(
{ $or: [{ email: req.body.email }, { userName: req.body.userName }] }
);

if (existingUser) {
req.flash("errors", {
msg: "Account with that email address or username already exists.",
});
return res.redirect("../signup");
}
);
};

await user.save();

await new Promise((resolve, reject) => {
req.logIn(user, (err) => {
if (err) reject(err);
else resolve();
});
});

res.redirect("/profile");


} catch (err) {
return next(err);
}
}


59 changes: 59 additions & 0 deletions controllers/comments.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
const Comment = require("../models/Comment");

module.exports = {
createComment: async (req, res) => {
try {
await Comment.create({
comment: req.body.comment,
likes: 0,
user: req.user.id,
post: req.params.post
});
console.log("Comment has been added!");
res.redirect(`/post/${req.params.post}`);
} catch (err) {
console.log(err);
}
},
likeComment: async (req, res) => {
try {
const comment = await Comment.findById(req.params.id);

const alreadyLiked = comment.likedBy.includes(req.user.id);

if(alreadyLiked) {
await Comment.findOneAndUpdate(
{ _id: req.params.id},
{
$pull: { likedBy: req.user.id },
$inc: { likes: -1 }
}
);
console.log("Likes -1")
} else {
await Comment.findOneAndUpdate(
{ _id: req.params.id },
{
$addToSet: { likedBy: req.user.id },
$inc: { likes: 1 },
}
);
console.log("Likes +1");
}

res.redirect(req.headers.referer || `/post/${comment.post}`);
} catch (err) {
console.log(err);
res.redirect(req.headers.referer || `/post/${comment.post}`);
}
},
deleteComment: async (req, res) => {
try {
await Comment.deleteOne({ _id: req.params.id });
console.log("Deleted Post");
res.redirect(req.headers.referer || `/post/${comment.post}`);
} catch (err) {
res.redirect(req.headers.referer || `/post/${comment.post}`);
}
},
};
Loading