diff --git a/.gitignore b/.gitignore index a1cbec1c1..43308b733 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,8 @@ +# do not upload the package modules - our file would be too heavy node_modules + +# do not upload secret keys, risk of exposure .env + +# settings for when opening app in vscode .vscode \ No newline at end of file diff --git a/LICENSE b/LICENSE index 0f07f22e5..b549a0e59 100644 --- a/LICENSE +++ b/LICENSE @@ -1,3 +1,5 @@ + + MIT License Copyright (c) 2024 100devs diff --git a/README.md b/README.md index 8fafaec0b..4d7eb8657 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,19 @@ # Install + `npm install` --- # 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) + - DB_STRING = `your database URI` + - CLOUD_NAME = `your cloudinary cloud name` - API_KEY = `your cloudinary api key` - API_SECRET = `your cloudinary api secret` @@ -16,5 +21,8 @@ --- # Run - + `npm start` + + + \ No newline at end of file diff --git a/config/database.js b/config/database.js index 24102b6d5..1a3a44388 100644 --- a/config/database.js +++ b/config/database.js @@ -1,5 +1,6 @@ const mongoose = require("mongoose"); +// configuration to connect to database using mongoose const connectDB = async () => { try { const conn = await mongoose.connect(process.env.DB_STRING, { diff --git a/config/passport.js b/config/passport.js index 6c058d1b8..37b8efaca 100644 --- a/config/passport.js +++ b/config/passport.js @@ -2,6 +2,7 @@ const LocalStrategy = require("passport-local").Strategy; const mongoose = require("mongoose"); const User = require("../models/User"); +// Use passport with local strategy to authenticate users and the User model to talk to the database module.exports = function (passport) { passport.use( new LocalStrategy({ usernameField: "email" }, (email, password, done) => { diff --git a/controllers/auth.js b/controllers/auth.js index 43f893aed..9e6a21435 100644 --- a/controllers/auth.js +++ b/controllers/auth.js @@ -2,6 +2,7 @@ const passport = require("passport"); const validator = require("validator"); const User = require("../models/User"); +//If they are already logged in, take them to their profile when they try to click login in, if render the login page and give title value of Login exports.getLogin = (req, res) => { if (req.user) { return res.redirect("/profile"); @@ -11,6 +12,7 @@ exports.getLogin = (req, res) => { }); }; +// Validators and display error message if not successful login. Checks to see if login info matches a user, if it does successfully login redirects to /profile exports.postLogin = (req, res, next) => { const validationErrors = []; if (!validator.isEmail(req.body.email)) @@ -44,6 +46,7 @@ exports.postLogin = (req, res, next) => { })(req, res, next); }; +// Destroys the session and the client is redirected to the home route exports.logout = (req, res) => { req.logout(() => { console.log('User has logged out.') @@ -56,6 +59,8 @@ exports.logout = (req, res) => { }); }; +// If user is already logged in, redirects to /profile page if user tries to signup +// If not signedup/logged in, renders a signup page exports.getSignup = (req, res) => { if (req.user) { return res.redirect("/profile"); @@ -65,6 +70,9 @@ exports.getSignup = (req, res) => { }); }; +// Validations and errors +// Adds to the Users Collection using User Model and info from the req.body +// Checks to see if user doesn't already exist, if user already exists will redirect to /signup, if not adds a new document and redirects to /profile exports.postSignup = (req, res, next) => { const validationErrors = []; if (!validator.isEmail(req.body.email)) diff --git a/controllers/home.js b/controllers/home.js index 49ba26578..0fcc317b3 100644 --- a/controllers/home.js +++ b/controllers/home.js @@ -1,3 +1,4 @@ +// on / route render index.ejs module.exports = { getIndex: (req, res) => { res.render("index.ejs"); diff --git a/controllers/posts.js b/controllers/posts.js index a3e2dab5d..d29c4a398 100644 --- a/controllers/posts.js +++ b/controllers/posts.js @@ -2,6 +2,8 @@ const cloudinary = require("../middleware/cloudinary"); const Post = require("../models/Post"); module.exports = { + // finds the Posts that matches the userId given by passport and renders profile.ejs with the posts info and the user info + //Only shows posts for that specific user id getProfile: async (req, res) => { try { const posts = await Post.find({ user: req.user.id }); @@ -10,6 +12,8 @@ module.exports = { console.log(err); } }, + //get all the posts from the Posts collection and render in feed.ejs as posts + // Shows all posts by everyone getFeed: async (req, res) => { try { const posts = await Post.find().sort({ createdAt: "desc" }).lean(); @@ -18,6 +22,7 @@ module.exports = { console.log(err); } }, + // Render a post.ejs with posts matching the post id requested getPost: async (req, res) => { try { const post = await Post.findById(req.params.id); @@ -26,11 +31,14 @@ module.exports = { console.log(err); } }, + + // createPost: async (req, res) => { try { // Upload image to cloudinary const result = await cloudinary.uploader.upload(req.file.path); + //Create a new post using Post model with cloudinary ID and redirect to profile await Post.create({ title: req.body.title, image: result.secure_url, @@ -45,6 +53,7 @@ module.exports = { console.log(err); } }, + //Find the post that matches the post id and increase the likes value by 1 then redirect to the post page likePost: async (req, res) => { try { await Post.findOneAndUpdate( @@ -68,6 +77,7 @@ module.exports = { // Delete post from db await Post.remove({ _id: req.params.id }); console.log("Deleted Post"); + // redirect to user profile res.redirect("/profile"); } catch (err) { res.redirect("/profile"); diff --git a/middleware/auth.js b/middleware/auth.js index f646630da..218dcc0c0 100644 --- a/middleware/auth.js +++ b/middleware/auth.js @@ -1,3 +1,5 @@ +// If user is authenticated, do the next task which is go to the controller and do task +// If not go back to home screen module.exports = { ensureAuth: function (req, res, next) { if (req.isAuthenticated()) { @@ -6,6 +8,9 @@ module.exports = { res.redirect("/"); } }, + + // If user is authenticated, do the next task which is go to the controller and do task +// If not, the guest can go back to the dashboard ensureGuest: function (req, res, next) { if (!req.isAuthenticated()) { return next(); diff --git a/middleware/cloudinary.js b/middleware/cloudinary.js index 0960c5b6f..517f7b919 100644 --- a/middleware/cloudinary.js +++ b/middleware/cloudinary.js @@ -2,6 +2,8 @@ const cloudinary = require("cloudinary").v2; require("dotenv").config({ path: "./config/.env" }); +// Cloduinary middle ware to configure to codinary +//does not need to be in config because we are using it as middleware cloudinary.config({ cloud_name: process.env.CLOUD_NAME, api_key: process.env.API_KEY, diff --git a/middleware/multer.js b/middleware/multer.js index c012afe58..07364f70a 100644 --- a/middleware/multer.js +++ b/middleware/multer.js @@ -1,3 +1,4 @@ +// Use muelter for file upload from client const multer = require("multer"); const path = require("path"); @@ -5,6 +6,7 @@ module.exports = multer({ storage: multer.diskStorage({}), fileFilter: (req, file, cb) => { let ext = path.extname(file.originalname); + // if file is not jpg or jpeg or png, run error if (ext !== ".jpg" && ext !== ".jpeg" && ext !== ".png") { cb(new Error("File type is not supported"), false); return; diff --git a/models/Post.js b/models/Post.js index f7d14c981..aa3b68efd 100644 --- a/models/Post.js +++ b/models/Post.js @@ -1,5 +1,5 @@ const mongoose = require("mongoose"); - +// Post schema to talk to data base and includes coludinary id and image link and reference to User model for who created it. const PostSchema = new mongoose.Schema({ title: { type: String, diff --git a/models/User.js b/models/User.js index afe8afc36..90e825225 100644 --- a/models/User.js +++ b/models/User.js @@ -1,6 +1,7 @@ const bcrypt = require("bcrypt"); const mongoose = require("mongoose"); +// Setting up Schema for Model const UserSchema = new mongoose.Schema({ userName: { type: String, unique: true }, email: { type: String, unique: true }, @@ -28,7 +29,7 @@ UserSchema.pre("save", function save(next) { }); }); -// Helper method for validating user's password. +// Helper method for validating user's password for comparison UserSchema.methods.comparePassword = function comparePassword( candidatePassword, @@ -39,4 +40,5 @@ UserSchema.methods.comparePassword = function comparePassword( }); }; +// Exporting out model to be used in controllers module.exports = mongoose.model("User", UserSchema); diff --git a/package-lock.json b/package-lock.json index 80734e98f..cd2d06016 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,3 +1,4 @@ +// List of packages used for the app, dependencies, { "name": "binary-upload-boom", "version": "1.0.0", diff --git a/package.json b/package.json index 8441374f6..7e5307cc7 100644 --- a/package.json +++ b/package.json @@ -1,31 +1,53 @@ +// List of packages used for the app, versions, dependencies, + { - "name": "binary-upload-boom", + "name": "binary-upload", "version": "1.0.0", "description": "100Devs Social Network", + // main file to start the server "main": "server.js", "scripts": { + //npm start = nodemon server.js "start": "nodemon server.js", "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", "license": "MIT", + // can separate into dev dependencies that only runs when developing and not in production ex: nodemon "dependencies": { + // hash our passwords "bcrypt": "^5.0.1", + // cdn for media "cloudinary": "^1.25.1", + // used for storage of the session "connect-mongo": "^3.2.0", + //creates .env file to store sensitve keys "dotenv": "^8.2.0", + // Views templating language "ejs": "^3.1.6", + //framework that prevents having to use basic node (lots of needing to know specifics of each request) "express": "^4.17.1", + // show flash messages during login/signup "express-flash": "^0.0.2", + // creates cookies on our browser so we can save the sessions into data bases "express-session": "^1.17.1", + // allows form requests to be PUT and DELETE req "method-override": "^3.0.0", + // connects to the db "mongodb": "^3.6.5", + // Object data modeling library that abstracts lots of things from mongo client "mongoose": "^5.12.3", + // morgan is the dev logger "morgan": "^1.10.0", + // allows client to upload files "multer": "^1.4.5-lts.1", + // allows the server to refresh without starting and stoping the server "nodemon": "^2.0.7", + // needed for authentication "passport": "^0.6.0", + // package is needed for local authentication "passport-local": "^1.0.0", + // used for POST req for signup/login "validator": "^13.6.0" } } diff --git a/routes/main.js b/routes/main.js index d6883000e..83f3a1937 100644 --- a/routes/main.js +++ b/routes/main.js @@ -1,18 +1,28 @@ +// Use express package const express = require("express"); +// Use express Router method to be able to direct to routers const router = express.Router(); +//Pathway to auth controller const authController = require("../controllers/auth"); +//Pathway to home Controller const homeController = require("../controllers/home"); +//Pathway to posts Controller const postsController = require("../controllers/posts"); +//Require the middleware for ensureAuth that will check to see if a user is logged in const { ensureAuth, ensureGuest } = require("../middleware/auth"); //Main Routes - simplified for now router.get("/", homeController.getIndex); +//ensureAuth makes sure a user is loggid in before the action in the controller router.get("/profile", ensureAuth, postsController.getProfile); router.get("/feed", ensureAuth, postsController.getFeed); + +// simple routers router.get("/login", authController.getLogin); router.post("/login", authController.postLogin); router.get("/logout", authController.logout); router.get("/signup", authController.getSignup); router.post("/signup", authController.postSignup); +//export the router info to the server.js file module.exports = router; diff --git a/routes/posts.js b/routes/posts.js index aa463ac90..e9e1954f5 100644 --- a/routes/posts.js +++ b/routes/posts.js @@ -1,16 +1,25 @@ +// Use express package const express = require("express"); +// Use express Router method to be able to direct to routers const router = express.Router(); +//Use multer to upload media from the client const upload = require("../middleware/multer"); +//Pathway to posts Controller const postsController = require("../controllers/posts"); +//Require the middleware for ensureAuth that will check to see if a user is logged in const { ensureAuth, ensureGuest } = require("../middleware/auth"); //Post Routes - simplified for now +// with posts/:id (of post) make sure they are logged in and get the POST router.get("/:id", ensureAuth, postsController.getPost); +//on posts/createPost upload a file using muelter and go to the postsController and run createPost router.post("/createPost", upload.single("file"), postsController.createPost); +// simple routers router.put("/likePost/:id", postsController.likePost); router.delete("/deletePost/:id", postsController.deletePost); +//export the router info to the server.js file module.exports = router; diff --git a/server.js b/server.js index 1718db010..fd25cdb6b 100644 --- a/server.js +++ b/server.js @@ -1,42 +1,54 @@ +// Use express package const express = require("express"); +// Use app instead of having to say require("express") every time using express const app = express(); +// Use mongoose package for Object data modeling library const mongoose = require("mongoose"); +// Use passport package for auth const passport = require("passport"); +// Use express sesssion package to create sessions const session = require("express-session"); +// Use connect-mongo package to save sessions into mongoDB using mongoStore const MongoStore = require("connect-mongo")(session); +// Use methodOveride package to make PUT and DELETE req with forms const methodOverride = require("method-override"); +// Use express-flash package to display flash content when logging in/signing up const flash = require("express-flash"); +// Use morgan package to log the server and debug const logger = require("morgan"); +// Use the database configuration file to connect to DB const connectDB = require("./config/database"); +//use the routes/main file for mainRoutes const mainRoutes = require("./routes/main"); +//use the routes/posts file for postRoutes const postRoutes = require("./routes/posts"); -//Use .env file in config folder +//Use the .env secret key config require("dotenv").config({ path: "./config/.env" }); -// Passport config +// Use the passport config require("./config/passport")(passport); -//Connect To Database +// connect to the db connectDB(); -//Using EJS for views +// make ejs the views app.set("view engine", "ejs"); -//Static Folder +// Use the static files in the public folder app.use(express.static("public")); -//Body Parsing +// Body parser so only show important parts of the request app.use(express.urlencoded({ extended: true })); app.use(express.json()); -//Logging +//Use morgan to log in console for debubbing app.use(logger("dev")); -//Use forms for put / delete +//Use methodOverrride to change form POST req to PUT or DELETE app.use(methodOverride("_method")); -// Setup Sessions - stored in MongoDB +// Create cookies that are random and save these into the DB as sessions using MongoStore app.use( session({ secret: "keyboard cat", @@ -46,18 +58,20 @@ app.use( }) ); -// Passport middleware +// Start passport and start session app.use(passport.initialize()); app.use(passport.session()); -//Use flash messages for errors, info, ect... +// Use flash for notifications on login/signup app.use(flash()); -//Setup Routes For Which The Server Is Listening +// using express +// on "/", direct to the mainRoutes which the pathway is given above +// on "/post", direct to the postRoutes which the pathway is given above app.use("/", mainRoutes); app.use("/post", postRoutes); -//Server Running +// tell the server to run on the port that the hosting environment is requiring and display the server is running app.listen(process.env.PORT, () => { console.log("Server is running, you better catch it!"); }); diff --git a/views/feed.ejs b/views/feed.ejs index 0ded94809..52e9becc8 100644 --- a/views/feed.ejs +++ b/views/feed.ejs @@ -1,6 +1,7 @@ <%- include('partials/header') -%>