[
  {
    "path": ".gitignore",
    "content": "node_modules/\n.env\n"
  },
  {
    "path": "README.md",
    "content": "# Instaclone Backend\n\n**NOTE: As of 10-06-2022 19:52 IST, I am archiving this repository. It was fun while it lasted.**\n\nInstagram clone using MERN stack\n\nThis is the backend repo built with Express and MongoDB. If you looking for the frontend repo, [click here](https://github.com/manikandanraji/instaclone-frontend)\n\n## Running Locally\n\nAt the root of the project, you should have a .env with the following contents\n\n```js\nJWT_SECRET=<yoursupersecret>\nJWT_EXPIRE=30d // or anything you prefer\nMONGOURI=<your_mongodb_connection_uri>\n```\n\nThen run <code>npm i && npm run dev</code> to start the development server\n\n## Deploying the backend to heroku\n\nFirst create an heroku account and install the heroku cli globally and login\n\n```bash\nnpm i -g heroku\nheroku login\n```\n\nOnce logged in, create a new heroku application and push it to the remote 'heroku'\n\n```bash\nheroku create\ngit push heroku master\n```\nThen we need to manually setup the environmental variables using the heroku dashboard\n\n## UI\n\n### Home \n![Home](screenshots/home_new.png)\n\n### Explore\n![Explore](screenshots/explore_new.png)\n\n\n### Followers\n![Followers](screenshots/followers_new.png)\n\n\n### Profile\n![Profile](screenshots/profile_new.png)\n\n### Edit Profile\n![Edit Profile](screenshots/edit_profile_new.png)\n\n### New Post\n![New Post](screenshots/new_post_new.png)\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"instaclone-backend\",\n  \"version\": \"1.0.0\",\n  \"description\": \"Instaclone backend using Express, MongoDB\",\n  \"main\": \"src/server.js\",\n  \"scripts\": {\n    \"start\": \"NODE_ENV=production node src/server\",\n    \"dev\": \"nodemon src/server\"\n  },\n  \"author\": \"Manikandan Raji\",\n  \"license\": \"MIT\",\n  \"dependencies\": {\n    \"bcryptjs\": \"^2.4.3\",\n    \"cors\": \"^2.8.5\",\n    \"dotenv\": \"^8.2.0\",\n    \"express\": \"^4.17.1\",\n    \"jsonwebtoken\": \"^8.5.1\",\n    \"mongoose\": \"^5.9.19\",\n    \"nodemon\": \"^2.0.4\"\n  }\n}\n"
  },
  {
    "path": "src/controllers/auth.js",
    "content": "const User = require(\"../models/User\");\nconst asyncHandler = require(\"../middlewares/asyncHandler\");\n\nexports.login = asyncHandler(async (req, res, next) => {\n  const { email, password } = req.body;\n\n  // make sure the email, pw is not empty\n  if (!email || !password) {\n    return next({\n      message: \"Please provide email and password\",\n      statusCode: 400,\n    });\n  }\n\n  // check if the user exists\n  const user = await User.findOne({ email });\n\n  if (!user) {\n    return next({\n      message: \"The email is not yet registered to an accout\",\n      statusCode: 400,\n    });\n  }\n\n  // if exists, make sure the password matches\n  const match = await user.checkPassword(password);\n\n  if (!match) {\n    return next({ message: \"The password does not match\", statusCode: 400 });\n  }\n  const token = user.getJwtToken();\n\n  // then send json web token as response\n  res.status(200).json({ success: true, token });\n});\n\nexports.signup = asyncHandler(async (req, res, next) => {\n  const { fullname, username, email, password } = req.body;\n\n  const user = await User.create({ fullname, username, email, password });\n\n  const token = user.getJwtToken();\n\n  res.status(200).json({ success: true, token });\n});\n\nexports.me = asyncHandler(async (req, res, next) => {\n  const { avatar, username, fullname, email, _id, website, bio } = req.user;\n\n  res\n    .status(200)\n    .json({\n      success: true,\n      data: { avatar, username, fullname, email, _id, website, bio },\n    });\n});\n"
  },
  {
    "path": "src/controllers/post.js",
    "content": "const mongoose = require(\"mongoose\");\nconst Post = require(\"../models/Post\");\nconst User = require(\"../models/User\");\nconst Comment = require(\"../models/Comment\");\nconst asyncHandler = require(\"../middlewares/asyncHandler\");\n\nexports.getPosts = asyncHandler(async (req, res, next) => {\n  const posts = await Post.find();\n\n  res.status(200).json({ success: true, data: posts });\n});\n\nexports.getPost = asyncHandler(async (req, res, next) => {\n  const post = await Post.findById(req.params.id)\n    .populate({\n      path: \"comments\",\n      select: \"text\",\n      populate: {\n        path: \"user\",\n        select: \"username avatar\",\n      },\n    })\n    .populate({\n      path: \"user\",\n      select: \"username avatar\",\n    })\n    .lean()\n    .exec();\n\n  if (!post) {\n    return next({\n      message: `No post found for id ${req.params.id}`,\n      statusCode: 404,\n    });\n  }\n\n  // is the post belongs to loggedin user?\n  post.isMine = req.user.id === post.user._id.toString();\n\n  // is the loggedin user liked the post??\n  const likes = post.likes.map((like) => like.toString());\n  post.isLiked = likes.includes(req.user.id);\n\n  // is the loggedin user liked the post??\n  const savedPosts = req.user.savedPosts.map((post) => post.toString());\n  post.isSaved = savedPosts.includes(req.params.id);\n\n  // is the comment on the post belongs to the logged in user?\n  post.comments.forEach((comment) => {\n    comment.isCommentMine = false;\n\n    const userStr = comment.user._id.toString();\n    if (userStr === req.user.id) {\n      comment.isCommentMine = true;\n    }\n  });\n\n  res.status(200).json({ success: true, data: post });\n});\n\nexports.deletePost = asyncHandler(async (req, res, next) => {\n  const post = await Post.findById(req.params.id);\n\n  if (!post) {\n    return next({\n      message: `No post found for id ${req.params.id}`,\n      statusCode: 404,\n    });\n  }\n\n  if (post.user.toString() !== req.user.id) {\n    return next({\n      message: \"You are not authorized to delete this post\",\n      statusCode: 401,\n    });\n  }\n\n  await User.findByIdAndUpdate(req.user.id, {\n    $pull: { posts: req.params.id },\n    $inc: { postCount: -1 },\n  });\n\n  await post.remove();\n\n  res.status(200).json({ success: true, data: {} });\n});\n\nexports.addPost = asyncHandler(async (req, res, next) => {\n  const { caption, files, tags } = req.body;\n  const user = req.user.id;\n\n  let post = await Post.create({ caption, files, tags, user });\n\n  await User.findByIdAndUpdate(req.user.id, {\n    $push: { posts: post._id },\n    $inc: { postCount: 1 },\n  });\n\n  post = await post\n    .populate({ path: \"user\", select: \"avatar username fullname\" })\n    .execPopulate();\n\n  res.status(200).json({ success: true, data: post });\n});\n\nexports.toggleLike = asyncHandler(async (req, res, next) => {\n  // make sure that the post exists\n  const post = await Post.findById(req.params.id);\n\n  if (!post) {\n    return next({\n      message: `No post found for id ${req.params.id}`,\n      statusCode: 404,\n    });\n  }\n\n  if (post.likes.includes(req.user.id)) {\n    const index = post.likes.indexOf(req.user.id);\n    post.likes.splice(index, 1);\n    post.likesCount = post.likesCount - 1;\n    await post.save();\n  } else {\n    post.likes.push(req.user.id);\n    post.likesCount = post.likesCount + 1;\n    await post.save();\n  }\n\n  res.status(200).json({ success: true, data: {} });\n});\n\nexports.addComment = asyncHandler(async (req, res, next) => {\n  const post = await Post.findById(req.params.id);\n\n  if (!post) {\n    return next({\n      message: `No post found for id ${req.params.id}`,\n      statusCode: 404,\n    });\n  }\n\n  let comment = await Comment.create({\n    user: req.user.id,\n    post: req.params.id,\n    text: req.body.text,\n  });\n\n  post.comments.push(comment._id);\n  post.commentsCount = post.commentsCount + 1;\n  await post.save();\n\n  comment = await comment\n    .populate({ path: \"user\", select: \"avatar username fullname\" })\n    .execPopulate();\n\n  res.status(200).json({ success: true, data: comment });\n});\n\nexports.deleteComment = asyncHandler(async (req, res, next) => {\n  const post = await Post.findById(req.params.id);\n\n  if (!post) {\n    return next({\n      message: `No post found for id ${req.params.id}`,\n      statusCode: 404,\n    });\n  }\n\n  const comment = await Comment.findOne({\n    _id: req.params.commentId,\n    post: req.params.id,\n  });\n\n  if (!comment) {\n    return next({\n      message: `No comment found for id ${req.params.id}`,\n      statusCode: 404,\n    });\n  }\n\n  if (comment.user.toString() !== req.user.id) {\n    return next({\n      message: \"You are not authorized to delete this comment\",\n      statusCode: 401,\n    });\n  }\n\n  // remove the comment from the post\n  const index = post.comments.indexOf(comment._id);\n  post.comments.splice(index, 1);\n  post.commentsCount = post.commentsCount - 1;\n  await post.save();\n\n  await comment.remove();\n\n  res.status(200).json({ success: true, data: {} });\n});\n\nexports.searchPost = asyncHandler(async (req, res, next) => {\n  if (!req.query.caption && !req.query.tag) {\n    return next({\n      message: \"Please enter either caption or tag to search for\",\n      statusCode: 400,\n    });\n  }\n\n  let posts = [];\n\n  if (req.query.caption) {\n    const regex = new RegExp(req.query.caption, \"i\");\n    posts = await Post.find({ caption: regex });\n  }\n\n  if (req.query.tag) {\n    posts = posts.concat([await Post.find({ tags: req.query.tag })]);\n  }\n\n  res.status(200).json({ success: true, data: posts });\n});\n\nexports.toggleSave = asyncHandler(async (req, res, next) => {\n  // make sure that the post exists\n  const post = await Post.findById(req.params.id);\n\n  if (!post) {\n    return next({\n      message: `No post found for id ${req.params.id}`,\n      statusCode: 404,\n    });\n  }\n\n  const { user } = req;\n\n  if (user.savedPosts.includes(req.params.id)) {\n    console.log(\"removing saved post\");\n    await User.findByIdAndUpdate(user.id, {\n      $pull: { savedPosts: req.params.id },\n    });\n  } else {\n    console.log(\"saving post\");\n    await User.findByIdAndUpdate(user.id, {\n      $push: { savedPosts: req.params.id },\n    });\n  }\n\n  res.status(200).json({ success: true, data: {} });\n});\n"
  },
  {
    "path": "src/controllers/user.js",
    "content": "const User = require(\"../models/User\");\nconst Post = require(\"../models/Post\");\nconst asyncHandler = require(\"../middlewares/asyncHandler\");\n\nexports.getUsers = asyncHandler(async (req, res, next) => {\n  let users = await User.find().select(\"-password\").lean().exec();\n\n  users.forEach((user) => {\n    user.isFollowing = false;\n    const followers = user.followers.map((follower) => follower._id.toString());\n    if (followers.includes(req.user.id)) {\n      user.isFollowing = true;\n    }\n  });\n\n  users = users.filter((user) => user._id.toString() !== req.user.id);\n\n  res.status(200).json({ success: true, data: users });\n});\n\nexports.getUser = asyncHandler(async (req, res, next) => {\n  const user = await User.findOne({ username: req.params.username })\n    .select(\"-password\")\n    .populate({ path: \"posts\", select: \"files commentsCount likesCount\" })\n    .populate({ path: \"savedPosts\", select: \"files commentsCount likesCount\" })\n    .populate({ path: \"followers\", select: \"avatar username fullname\" })\n    .populate({ path: \"following\", select: \"avatar username fullname\" })\n    .lean()\n    .exec();\n\n  if (!user) {\n    return next({\n      message: `The user ${req.params.username} is not found`,\n      statusCode: 404,\n    });\n  }\n\n  user.isFollowing = false;\n  const followers = user.followers.map((follower) => follower._id.toString());\n\n  user.followers.forEach((follower) => {\n    follower.isFollowing = false;\n    if (req.user.following.includes(follower._id.toString())) {\n      follower.isFollowing = true;\n    }\n  });\n\n  user.following.forEach((user) => {\n    user.isFollowing = false;\n    if (req.user.following.includes(user._id.toString())) {\n      user.isFollowing = true;\n    }\n  });\n\n  if (followers.includes(req.user.id)) {\n    user.isFollowing = true;\n  }\n\n  user.isMe = req.user.id === user._id.toString();\n\n  res.status(200).json({ success: true, data: user });\n});\n\nexports.follow = asyncHandler(async (req, res, next) => {\n  // make sure the user exists\n  const user = await User.findById(req.params.id);\n\n  if (!user) {\n    return next({\n      message: `No user found for id ${req.params.id}`,\n      statusCode: 404,\n    });\n  }\n\n  // make the sure the user is not the logged in user\n  if (req.params.id === req.user.id) {\n    return next({ message: \"You can't unfollow/follow yourself\", status: 400 });\n  }\n\n  // only follow if the user is not following already\n  if (user.followers.includes(req.user.id)) {\n    return next({ message: \"You are already following him\", status: 400 });\n  }\n\n  await User.findByIdAndUpdate(req.params.id, {\n    $push: { followers: req.user.id },\n    $inc: { followersCount: 1 },\n  });\n  await User.findByIdAndUpdate(req.user.id, {\n    $push: { following: req.params.id },\n    $inc: { followingCount: 1 },\n  });\n\n  res.status(200).json({ success: true, data: {} });\n});\n\nexports.unfollow = asyncHandler(async (req, res, next) => {\n  const user = await User.findById(req.params.id);\n\n  if (!user) {\n    return next({\n      message: `No user found for ID ${req.params.id}`,\n      statusCode: 404,\n    });\n  }\n\n  // make the sure the user is not the logged in user\n  if (req.params.id === req.user.id) {\n    return next({ message: \"You can't follow/unfollow yourself\", status: 400 });\n  }\n\n  await User.findByIdAndUpdate(req.params.id, {\n    $pull: { followers: req.user.id },\n    $inc: { followersCount: -1 },\n  });\n  await User.findByIdAndUpdate(req.user.id, {\n    $pull: { following: req.params.id },\n    $inc: { followingCount: -1 },\n  });\n\n  res.status(200).json({ success: true, data: {} });\n});\n\nexports.feed = asyncHandler(async (req, res, next) => {\n  const following = req.user.following;\n\n  const users = await User.find()\n    .where(\"_id\")\n    .in(following.concat([req.user.id]))\n    .exec();\n\n  const postIds = users.map((user) => user.posts).flat();\n\n  const posts = await Post.find()\n    .populate({\n      path: \"comments\",\n      select: \"text\",\n      populate: { path: \"user\", select: \"avatar fullname username\" },\n    })\n    .populate({ path: \"user\", select: \"avatar fullname username\" })\n    .sort(\"-createdAt\")\n    .where(\"_id\")\n    .in(postIds)\n    .lean()\n    .exec();\n\n  posts.forEach((post) => {\n    // is the loggedin user liked the post\n    post.isLiked = false;\n    const likes = post.likes.map((like) => like.toString());\n    if (likes.includes(req.user.id)) {\n      post.isLiked = true;\n    }\n\n    // is the loggedin saved this post\n    post.isSaved = false;\n    const savedPosts = req.user.savedPosts.map((post) => post.toString());\n    if (savedPosts.includes(post._id)) {\n      post.isSaved = true;\n    }\n\n    // is the post belongs to the loggedin user\n    post.isMine = false;\n    if (post.user._id.toString() === req.user.id) {\n      post.isMine = true;\n    }\n\n    // is the comment belongs to the loggedin user\n    post.comments.map((comment) => {\n      comment.isCommentMine = false;\n      if (comment.user._id.toString() === req.user.id) {\n        comment.isCommentMine = true;\n      }\n    });\n  });\n\n  res.status(200).json({ success: true, data: posts });\n});\n\nexports.searchUser = asyncHandler(async (req, res, next) => {\n  if (!req.query.username) {\n    return next({ message: \"The username cannot be empty\", statusCode: 400 });\n  }\n\n  const regex = new RegExp(req.query.username, \"i\");\n  const users = await User.find({ username: regex });\n\n  res.status(200).json({ success: true, data: users });\n});\n\nexports.editUser = asyncHandler(async (req, res, next) => {\n  const { avatar, username, fullname, website, bio, email } = req.body;\n\n  const fieldsToUpdate = {};\n  if (avatar) fieldsToUpdate.avatar = avatar;\n  if (username) fieldsToUpdate.username = username;\n  if (fullname) fieldsToUpdate.fullname = fullname;\n  if (email) fieldsToUpdate.email = email;\n\n  const user = await User.findByIdAndUpdate(\n    req.user.id,\n    {\n      $set: { ...fieldsToUpdate, website, bio },\n    },\n    {\n      new: true,\n      runValidators: true,\n    }\n  ).select(\"avatar username fullname email bio website\");\n\n  res.status(200).json({ success: true, data: user });\n});\n"
  },
  {
    "path": "src/middlewares/asyncHandler.js",
    "content": "const asyncHandler = (fn) => (req, res, next) =>\n  Promise.resolve(fn(req, res, next)).catch(next);\n\nmodule.exports = asyncHandler;\n"
  },
  {
    "path": "src/middlewares/auth.js",
    "content": "const jwt = require(\"jsonwebtoken\");\nconst User = require(\"../models/User\");\n\nexports.protect = async (req, res, next) => {\n  let token;\n\n  if (\n    req.headers.authorization &&\n    req.headers.authorization.startsWith(\"Bearer\")\n  ) {\n    token = req.headers.authorization.split(\" \")[1];\n  }\n\n  if (!token) {\n    return next({\n      message: \"You need to be logged in to visit this route\",\n      statusCode: 403,\n    });\n  }\n\n  try {\n    const decoded = jwt.verify(token, process.env.JWT_SECRET);\n\n    const user = await User.findById(decoded.id).select(\"-password\");\n\n    if (!user) {\n      return next({ message: `No user found for ID ${decoded.id}` });\n    }\n\n    req.user = user;\n    next();\n  } catch (err) {\n    next({\n      message: \"You need to be logged in to visit this route\",\n      statusCode: 403,\n    });\n  }\n};\n"
  },
  {
    "path": "src/middlewares/errorHandler.js",
    "content": "const errorHandler = (err, req, res, next) => {\n  console.log(err);\n\n  let message = err.message || \"Internal server error\";\n  let statusCode = err.statusCode || 500;\n\n  if (err.code === 11000) {\n    message = \"Duplicate key\";\n\n    if (err.keyValue.email) {\n      message = \"The email is already taken\";\n    }\n\n    if (err.keyValue.username) {\n      message = \"The username is already taken\";\n    }\n\n    statusCode = 400;\n  }\n\n  if (err.name === \"ValidationError\") {\n    const fields = Object.keys(err.errors);\n\n    fields.map((field) => {\n      if (err.errors[field].kind === \"maxlength\") {\n        message = \"Password should be maximum of 12 characters\";\n      } else {\n        message = \"Password should be minimum of 6 characters\";\n      }\n    });\n\n    statusCode = 400;\n  }\n\n  if (err.name === \"CastError\") {\n    message = \"The ObjectID is malformed\";\n    statusCode = 400;\n  }\n\n  res.status(statusCode).json({ success: false, message });\n};\n\nmodule.exports = errorHandler;\n"
  },
  {
    "path": "src/models/Comment.js",
    "content": "const mongoose = require(\"mongoose\");\n\nconst CommentSchema = new mongoose.Schema({\n  user: {\n    type: mongoose.Schema.ObjectId,\n    ref: \"User\",\n    required: true,\n  },\n  post: {\n    type: mongoose.Schema.ObjectId,\n    ref: \"Post\",\n    required: true,\n  },\n  text: {\n    type: String,\n    required: [true, \"Please enter the comment\"],\n    trim: true,\n  },\n  createdAt: {\n    type: Date,\n    default: Date.now,\n  },\n});\n\nmodule.exports = mongoose.model(\"Comment\", CommentSchema);\n"
  },
  {
    "path": "src/models/Post.js",
    "content": "const mongoose = require(\"mongoose\");\n\nconst PostSchema = new mongoose.Schema({\n  user: {\n    type: mongoose.Schema.ObjectId,\n    ref: \"User\",\n    required: true,\n  },\n  caption: {\n    type: String,\n    required: [true, \"Please enter the caption\"],\n    trim: true,\n  },\n  tags: {\n    type: [String],\n  },\n  files: {\n    type: [String],\n    validate: (v) => v === null || v.length > 0,\n  },\n  likes: [{ type: mongoose.Schema.ObjectId, ref: \"User\" }],\n  likesCount: {\n    type: Number,\n    default: 0,\n  },\n  comments: [{ type: mongoose.Schema.ObjectId, ref: \"Comment\" }],\n  commentsCount: {\n    type: Number,\n    default: 0,\n  },\n  createdAt: {\n    type: Date,\n    default: Date.now,\n  },\n});\n\nmodule.exports = mongoose.model(\"Post\", PostSchema);\n"
  },
  {
    "path": "src/models/User.js",
    "content": "const mongoose = require(\"mongoose\");\nconst jwt = require(\"jsonwebtoken\");\nconst bcrypt = require(\"bcryptjs\");\n\nconst UserSchema = new mongoose.Schema({\n  fullname: {\n    type: String,\n    required: [true, \"Please enter your fullname\"],\n    trim: true,\n  },\n  username: {\n    type: String,\n    required: [true, \"Please enter your username\"],\n    trim: true,\n    unique: true,\n  },\n  email: {\n    type: String,\n    required: [true, \"Please enter your email\"],\n    trim: true,\n    lowercase: true,\n    unique: true,\n  },\n  password: {\n    type: String,\n    required: [true, \"Please enter your password\"],\n    minlength: [6, \"Password should be atleast minimum of 6 characters\"],\n    maxlength: [12, \"Password should be maximum of 12 characters\"],\n  },\n  avatar: {\n    type: String,\n    default:\n      \"https://res.cloudinary.com/tylerdurden/image/upload/v1602657481/random/pngfind.com-default-image-png-6764065_krremh.png\",\n  },\n  bio: String,\n  website: String,\n  followers: [{ type: mongoose.Schema.ObjectId, ref: \"User\" }],\n  followersCount: {\n    type: Number,\n    default: 0,\n  },\n  followingCount: {\n    type: Number,\n    default: 0,\n  },\n  following: [{ type: mongoose.Schema.ObjectId, ref: \"User\" }],\n  posts: [{ type: mongoose.Schema.ObjectId, ref: \"Post\" }],\n  postCount: {\n    type: Number,\n    default: 0,\n  },\n  savedPosts: [{ type: mongoose.Schema.ObjectId, ref: \"Post\" }],\n  createdAt: {\n    type: Date,\n    default: Date.now,\n  },\n});\n\nUserSchema.pre(\"save\", async function (next) {\n  const salt = await bcrypt.genSalt(10);\n  this.password = await bcrypt.hash(this.password, salt);\n  next();\n});\n\nUserSchema.methods.getJwtToken = function () {\n  return jwt.sign({ id: this._id }, process.env.JWT_SECRET, {\n    expiresIn: process.env.JWT_EXPIRE,\n  });\n};\n\nUserSchema.methods.checkPassword = async function (password) {\n  return await bcrypt.compare(password, this.password);\n};\n\nmodule.exports = mongoose.model(\"User\", UserSchema);\n"
  },
  {
    "path": "src/routes/auth.js",
    "content": "const express = require(\"express\");\nconst router = express.Router();\nconst { login, signup, me } = require(\"../controllers/auth\");\nconst { protect } = require(\"../middlewares/auth\");\n\nrouter.route(\"/signup\").post(signup);\nrouter.route(\"/login\").post(login);\nrouter.route(\"/me\").get(protect, me);\n\nmodule.exports = router;\n"
  },
  {
    "path": "src/routes/post.js",
    "content": "const express = require(\"express\");\nconst router = express.Router();\nconst {\n  getPosts,\n  getPost,\n  addPost,\n  deletePost,\n  toggleLike,\n  toggleSave,\n  addComment,\n  deleteComment,\n  searchPost,\n} = require(\"../controllers/post\");\nconst { protect } = require(\"../middlewares/auth\");\n\nrouter.route(\"/\").get(getPosts).post(protect, addPost);\nrouter.route(\"/search\").get(searchPost);\nrouter.route(\"/:id\").get(protect, getPost).delete(protect, deletePost);\nrouter.route(\"/:id/togglelike\").get(protect, toggleLike);\nrouter.route(\"/:id/togglesave\").get(protect, toggleSave);\nrouter.route(\"/:id/comments\").post(protect, addComment);\nrouter.route(\"/:id/comments/:commentId\").delete(protect, deleteComment);\n\nmodule.exports = router;\n"
  },
  {
    "path": "src/routes/user.js",
    "content": "const express = require(\"express\");\nconst router = express.Router();\nconst {\n  getUsers,\n  getUser,\n  follow,\n  unfollow,\n  feed,\n  searchUser,\n  editUser,\n} = require(\"../controllers/user\");\nconst { protect } = require(\"../middlewares/auth\");\n\nrouter.route(\"/\").get(protect, getUsers);\nrouter.route(\"/\").put(protect, editUser);\nrouter.route(\"/feed\").get(protect, feed);\nrouter.route(\"/search\").get(searchUser);\nrouter.route(\"/:username\").get(protect, getUser);\nrouter.route(\"/:id/follow\").get(protect, follow);\nrouter.route(\"/:id/unfollow\").get(protect, unfollow);\n\nmodule.exports = router;\n"
  },
  {
    "path": "src/seeder.js",
    "content": "require(\"dotenv\").config();\nconst mongoose = require(\"mongoose\");\nconst User = require(\"./models/User\");\nconst Comment = require(\"./models/Comment\");\nconst Post = require(\"./models/Post\");\n\nmongoose.connect(process.env.MONGOURI, {\n  useCreateIndex: true,\n  useNewUrlParser: true,\n  useUnifiedTopology: true,\n  useFindAndModify: false,\n});\n\nconst deleteData = async () => {\n  try {\n    await User.deleteMany();\n    await Comment.deleteMany();\n    await Post.deleteMany();\n    console.log(\"Deleted data...\");\n    process.exit();\n  } catch (err) {\n    console.error(err);\n  }\n};\n\nif (process.argv[2] === \"-d\") {\n  deleteData();\n} else {\n  console.log(\"not enough arguments\");\n  process.exit(1);\n}\n"
  },
  {
    "path": "src/server.js",
    "content": "require(\"dotenv\").config();\nconst express = require(\"express\");\nconst cors = require(\"cors\");\nconst auth = require(\"./routes/auth\");\nconst user = require(\"./routes/user\");\nconst post = require(\"./routes/post\");\nconst connectToDb = require(\"./utils/db\");\nconst errorHandler = require(\"./middlewares/errorHandler\");\n\nconst app = express();\n\nconnectToDb();\napp.use(express.json());\napp.use(cors());\n\napp.use(\"/api/v1/auth\", auth);\napp.use(\"/api/v1/users\", user);\napp.use(\"/api/v1/posts\", post);\n\napp.use(errorHandler);\n\nconst PORT = process.env.PORT || 5000;\napp.listen(\n  PORT,\n  console.log(`server started in ${process.env.NODE_ENV} mode at port ${PORT}`)\n);\n"
  },
  {
    "path": "src/utils/db.js",
    "content": "const mongoose = require(\"mongoose\");\n\nconst connectToDb = async () => {\n  try {\n    const connection = await mongoose.connect(process.env.MONGOURI, {\n      useCreateIndex: true,\n      useNewUrlParser: true,\n      useUnifiedTopology: true,\n      useFindAndModify: false,\n    });\n\n    console.log(`Connected to database ${connection.connections[0].name}`);\n  } catch (err) {\n    console.error(err);\n  }\n};\n\nmodule.exports = connectToDb;\n"
  }
]