diff --git a/.prettierignore b/.prettierignore
new file mode 100644
index 0000000..56ffea5
--- /dev/null
+++ b/.prettierignore
@@ -0,0 +1,16 @@
+*.env
+.env
+.env.*
+
+node_modules
+node_modules/**
+
+./dist
+dist
+
+./coverage
+coverage
+
+./test
+test
+
diff --git a/.prettierrc b/.prettierrc
new file mode 100644
index 0000000..fff6d9f
--- /dev/null
+++ b/.prettierrc
@@ -0,0 +1,7 @@
+{
+ "singleQuote": false,
+ "trailingComma": "es5",
+ "tabWidth": 2,
+ "semi": true,
+ "endOfLine": "crlf"
+}
\ No newline at end of file
diff --git a/README.md b/README.md
index 4cf3de2..af9ce83 100644
--- a/README.md
+++ b/README.md
@@ -6,54 +6,79 @@
-**v1.0.2**
+**v1.0.3**
-This app is build using Backend Development tools like
-> **bcrypt, body-parser, dotenv, express, jsonwebtoken, mongoose, node-rest-client, path, moment-timezone**
+The Social App is built using the following backend development tools:
-### API's available:-
+- bcrypt
+- body-parser
+- dotenv
+- express
+- jsonwebtoken
+- mongoose
+- node-rest-client
+- path
+- moment-timezone
+- prettier
+- helmet
+- express-rate-limit
-NOTE:-
-**1ī¸âŖ** Use {your_URL} or {localhost}
-**2ī¸âŖ** Request Format - (Method) (Type) (URL)
+## APIs Available
-1. **[POST]** - Signup - http://{URL}/api/v1/auth/signup
-2. **[POST]** - Signin - http://{URL}/api/v1/auth/signin
-3. **[POST]** - Add one Post/many Posts (one Post at a time) - http://{URL}/api/v1/addPost
-4. **[GET]** - Fetch all Posts specifying user - http://{URL}/api/v1/posts
-5. **[DELETE]** - Delete one Post specifying Post Id - http://{URL}/api/v1/posts
-6. **[DELETE]** - Delete All Post specifying userId - http://{URL}/api/v1/allposts
+The Social App provides the following APIs:
+
+1. **[POST]** - Signup - `http://{URL}/api/v1/auth/signup`
+2. **[POST]** - Signin - `http://{URL}/api/v1/auth/signin`
+3. **[POST]** - Add one Post/many Posts (one Post at a time) - `http://{URL}/api/v1/addPost`
+4. **[GET]** - Fetch all Posts specifying user - `http://{URL}/api/v1/posts`
+5. **[DELETE]** - Delete one Post specifying Post Id - `http://{URL}/api/v1/posts`
+6. **[DELETE]** - Delete All Post specifying userId - `http://{URL}/api/v1/allposts`
+
+Note: Replace `{URL}` with your actual URL.
### Installation:-
- Run this command to install dependencies
- ### ```npm install```
+ ### `npm install`
+
+ **NOTE:** If above command doesn't install all dependencies, run below command
- **NOTE:** If above command doesn't install all dependencies, run below command
- ### ```npm run build```
+ ### `npm run build`
- Start the server by running this command
- ### ```npm run start```
-- To check if all is working good! đ - http://{URL}:{PORT}
-- **N.B.** - **PORT** can be defined in **".env"** file, otherwise it will default to **3001**
+ ### `npm run start`
+- To check if all is working good! đ - http://{URL}:{SERVER_PORT}
+- **N.B.** - **SERVER_PORT** can be defined in **".env"** file, otherwise it will default to **3001**
+
### Note to Developers:-
+
- I have added my MongoDB database URL to my Environment Variable **(.env)** file and all other secrets, so I insist to create a **(.env)** file and add necessary configuration data which is needed to be hidden from end-user/other developers.
- All the API's mentioned above will work best in Postman (Preferred, becoz I use it!) for testing and development, but other apps may also be used.
### Update
-***30-05-2023***
+
+**_30-05-2023_**
+
- Testing Backend API's and other features to optimise it. Need a Front-end part to be build.
-***01-06-2023***
-- Added some functionalities.
+**_01-06-2023_**
+
+- Added some functionalities.
+
+**_27-09-2023_**
+
+- Added some functionalities.
+- Updated README
+
+**_12-01-2024_**
-***27-09-2023***
-- Added some functionalities.
-- Updated README
-
+- Added helmet for secured headers
+- Added api rate limit for security
+- Added CodeQl, Node.js Vulnerability Check for security
+- Implemented Prettier for uniform code formatting and readability throughout the project
diff --git a/assets/home_page.jpg b/assets/home_page.jpg
index 99aa21d..f2c2c79 100644
Binary files a/assets/home_page.jpg and b/assets/home_page.jpg differ
diff --git a/configs/server.config.js b/configs/server.config.js
index f177a58..0251e0e 100644
--- a/configs/server.config.js
+++ b/configs/server.config.js
@@ -1,6 +1,6 @@
-require('dotenv').config()
+require("dotenv").config();
module.exports = {
- PORT : process.env.PORT || 3001,
- SECRET : process.env.SECRET_KEY
-}
\ No newline at end of file
+ SERVER_PORT: process.env.SERVER_PORT || 3001,
+ JWT_SECRET_KEY: process.env.JWT_SECRET_KEY,
+};
diff --git a/controllers/auth.controller.js b/controllers/auth.controller.js
index 1dfff94..140427c 100644
--- a/controllers/auth.controller.js
+++ b/controllers/auth.controller.js
@@ -3,197 +3,199 @@ const bcrypt = require("bcrypt");
const User = require("../models/user.model.js");
const Post = require("../models/post.model.js");
const jwt = require("jsonwebtoken");
-const { SECRET } = require("../configs/server.config.js");
+const { JWT_SECRET_KEY } = require("../configs/server.config.js");
const { formatDate } = require("../utils/formatDate.js"); //to convert & view UTC date to Indian Time format (doesn't modify in MongoDB database)
/************** SIGNUP/REGISTER API ********************/
exports.signup = async (req, res) => {
-
try {
- // get the data from request body
- let { name, userId, email, password } = req.body;
-
- /************ GENERATE HASH FOR PASSWORD *******************/
- const hash = await bcrypt.hash(password, 10);
-
- /************* STORE USER DATA TO DB *************/
- const userCreated = await User.create({
- name,
- userId,
- email,
- password:hash
- });
- //CONVERT JSON DATE TO INDIA DATE FORMAT
- const IndiaDateCreatedAt = formatDate(userCreated.createdAt);
- //Displays the user created
- console.log(userCreated);
-
- /************* SEND RESPONSE TO USER ************/
- res.status(201).send(['Message: User created Successfully',
- { Name: userCreated.name,
- UserID: userCreated.userId,
- Email: userCreated.email,
- Created_At: IndiaDateCreatedAt
- }]);
-
- } catch (err) {
- console.log("Error Occured!", err.message);
- res.status(500).send("Internal Server Error:");
- }
+ // get the data from request body
+ let { name, userId, email, password } = req.body;
+
+ /************ GENERATE HASH FOR PASSWORD *******************/
+ const hash = await bcrypt.hash(password, 10);
+
+ /************* STORE USER DATA TO DB *************/
+ const userCreated = await User.create({
+ name,
+ userId,
+ email,
+ password: hash,
+ });
+ //CONVERT JSON DATE TO INDIA DATE FORMAT
+ const IndiaDateCreatedAt = formatDate(userCreated.createdAt);
+ //Displays the user created
+ console.log(userCreated);
+
+ /************* SEND RESPONSE TO USER ************/
+ res
+ .status(201)
+ .send([
+ "Message: User created Successfully",
+ {
+ Name: userCreated.name,
+ UserID: userCreated.userId,
+ Email: userCreated.email,
+ Created_At: IndiaDateCreatedAt,
+ },
+ ]);
+ } catch (err) {
+ console.log("Error Occured!", err.message);
+ res.status(500).send("Internal Server Error:");
+ }
};
/************** SIGNIN API ****************************/
exports.signin = async (req, res) => {
-
try {
- // GET DATA FROM REQUEST BODY
- const { userId, password } = req.body;
-
- /*** CHECK FOR USERID IS PROVIDED IN REQUEST BODY OR NOT ********************/
- if (!userId) {
- console.log("\nuserId is not provided");
- return res.status(400).send("Bad Request! UserId not provided");
- }
-
- //FETCH REQUESTED USER FROM DB
- const user = await User.findOne({ userId: userId});
-
- /**** CHECK FOR USER EXISTS IN DB OR NOT *********** */
- if (!user) {
- console.log("UserID doesn't exists");
- return res.status(400).send("UserId doesn't exist in our server");
- }
-
- /*** CHECK FOR PASSWORD IS PROVIDED IN REQUEST BODY OR NOT ********* */
- if (!password) {
- console.log("Password not entered");
- return res.status(400).send("Please enter the password");
- }
-
- /******* CHECK WHETHER PASSWORD IS MATCHING IN DB OR NOT ***********/
- let isCorrectPassword = await bcrypt.compare(password,user.password);
- /******* CHECK WHETHER PASSWORD IS CORRECT OR NOT ***********/
- if (!isCorrectPassword) {
- console.log("Invalid Password");
- return res.status(401).send("Invalid Password");
- }
-
- /***** CREATE A ACCESS TOKEN FOR THE USER ************ */
- let token = jwt.sign({ userId: userId }, SECRET, {
- expiresIn: process.env.JWT_EXPIRY,
- });
-
- /**** CONVERT JSON DATE IN DB TO INDIA DATE FORMAT ***********/
- const IndiaDateCreatedAt = formatDate(user.createdAt);
- const IndiaDateUpdatedAt = formatDate(user.updatedAt);
- /**** CREATE A USER OBJECT TO STRUCTURE THE DATA TO SEND TH RESPONSE TO USER *****/
- const userData = {
- name: user.name,
- userId: user.userId
- };
-
- /***** SEND USER DATA IN RESPONSE FOR SIGNIN REQUESTED ********/
- console.log("SignIn Req for:", userData);
- /***** SEND ACCESS TOKEN TO USER FOR SIGNIN *******/
- res.status(200).send({
- Message: "Signed in Successfully!",
- AccessToken: token,
- });
- } catch (error) {
- console.log("Error occured", error);
- res.status(500).send("Internal Server Error");
- }
+ // GET DATA FROM REQUEST BODY
+ const { userId, password } = req.body;
+
+ /*** CHECK FOR USERID IS PROVIDED IN REQUEST BODY OR NOT ********************/
+ if (!userId) {
+ console.log("\nuserId is not provided");
+ return res.status(400).send("Bad Request! UserId not provided");
+ }
+
+ //FETCH REQUESTED USER FROM DB
+ const user = await User.findOne({ userId: userId });
+
+ /**** CHECK FOR USER EXISTS IN DB OR NOT *********** */
+ if (!user) {
+ console.log("UserID doesn't exists");
+ return res.status(400).send("UserId doesn't exist in our server");
+ }
+
+ /*** CHECK FOR PASSWORD IS PROVIDED IN REQUEST BODY OR NOT ********* */
+ if (!password) {
+ console.log("Password not entered");
+ return res.status(400).send("Please enter the password");
+ }
+
+ /******* CHECK WHETHER PASSWORD IS MATCHING IN DB OR NOT ***********/
+ let isCorrectPassword = await bcrypt.compare(password, user.password);
+ /******* CHECK WHETHER PASSWORD IS CORRECT OR NOT ***********/
+ if (!isCorrectPassword) {
+ console.log("Invalid Password");
+ return res.status(401).send("Invalid Password");
+ }
+
+ /***** CREATE A ACCESS TOKEN FOR THE USER ************ */
+ let token = jwt.sign({ userId: userId }, JWT_SECRET_KEY, {
+ expiresIn: process.env.JWT_EXPIRY,
+ });
+
+ /**** CONVERT JSON DATE IN DB TO INDIA DATE FORMAT ***********/
+ const IndiaDateCreatedAt = formatDate(user.createdAt);
+ const IndiaDateUpdatedAt = formatDate(user.updatedAt);
+ /**** CREATE A USER OBJECT TO STRUCTURE THE DATA TO SEND TH RESPONSE TO USER *****/
+ const userData = {
+ // name: user.name,
+ userId: user.userId,
+ };
+
+ /***** SEND USER DATA IN RESPONSE FOR SIGNIN REQUESTED ********/
+ console.log("SignIn Req for:", userData);
+ /***** SEND ACCESS TOKEN TO USER FOR SIGNIN *******/
+ res.status(200).send({
+ Message: "Signed in Successfully!",
+ AccessToken: token,
+ });
+ } catch (error) {
+ console.log("Error occured", error);
+ res.status(500).send("Internal Server Error");
+ }
};
/************** CHANGE PASSWORD API*********************/
exports.changePassword = async (req, res) => {
try {
- // GET DATA FROM REQUEST BODY
- const { userId, password } = req.body;
-
- //FETCH REQUESTED USER FROM DB
- const userCheck = await User.findOne({
- userId: userId,
- });
-
- /**** CHECK FOR USER EXISTS IN DB OR NOT *********** */
- if (!userCheck) {
- console.log("UserID doesn't exists");
- return res.status(400).send("UserId doesn't exist in our server");
- }
-
- /*** CHECK FOR PASSWORD TO CHANGE, IS PROVIDED IN REQUEST BODY OR NOT ********* */
- if (!password) {
- console.log("Password not entered");
- return res.status(400).send("Please enter the password");
- }
-
- /************ GENERATE HASH FOR PASSWORD *******************/
- const hash = await bcrypt.hash(password, 10);
-
- /************ FIND THE USER, CHANGE PASSWORD, & UPDATE IN DB *******************/
- await User.findOneAndUpdate(
- {
- userId: userId,
- },
- {
- password: hash,
- updatedAt: Date.now(),
- }
- ).exec();
-
- /*** LOG FOR SUCCESSFULL PASSWORD CHANGE ****/
- console.log(`Password for user '${userId}' updated!`);
- /*** SEND RESPONSE TO USER FOR SUCCESSFULL PASSWORD CHANGE ****/
- res.status(200).send(`Password for user '${userId}' updated!`);
-
- } catch (err) {
- console.log("Error while updating the password", err.message);
- res.status(500).send("Some internal error occured");
+ // GET DATA FROM REQUEST BODY
+ const { userId, password } = req.body;
+
+ //FETCH REQUESTED USER FROM DB
+ const userCheck = await User.findOne({
+ userId: userId,
+ });
+
+ /**** CHECK FOR USER EXISTS IN DB OR NOT *********** */
+ if (!userCheck) {
+ console.log("UserID doesn't exists");
+ return res.status(400).send("UserId doesn't exist in our server");
+ }
+
+ /*** CHECK FOR PASSWORD TO CHANGE, IS PROVIDED IN REQUEST BODY OR NOT ********* */
+ if (!password) {
+ console.log("Password not entered");
+ return res.status(400).send("Please enter the password");
+ }
+
+ /************ GENERATE HASH FOR PASSWORD *******************/
+ const hash = await bcrypt.hash(password, 10);
+
+ /************ FIND THE USER, CHANGE PASSWORD, & UPDATE IN DB *******************/
+ await User.findOneAndUpdate(
+ {
+ userId: userId,
+ },
+ {
+ password: hash,
+ updatedAt: Date.now(),
}
+ ).exec();
+
+ /*** LOG FOR SUCCESSFULL PASSWORD CHANGE ****/
+ console.log(`Password for user '${userId}' updated!`);
+ /*** SEND RESPONSE TO USER FOR SUCCESSFULL PASSWORD CHANGE ****/
+ res.status(200).send(`Password for user '${userId}' updated!`);
+ } catch (err) {
+ console.log("Error while updating the password", err.message);
+ res.status(500).send("Some internal error occured");
+ }
};
/************** DELETE USER API *********************/
exports.deleteUser = async (req, res) => {
try {
- /**** GET USERID & PASSWORD FROM REQUEST BODY ********/
- const {userId, password} = req.body;
- /**** CHECK FOR USERID & PASSWORD PROVIDED BY USER OR NOT ********/
- if (!userId || !password) {
- console.log("UserID/Password not provided");
- throw new Error("UserID/Password not provided");
- }
-
- //find the User in DB from requested userId
- const user = await User.findOne({
- userId: userId,
- });
-
- /******** CHECK WHETHER USER IN OUR DB OR NOT ***************/
- if(user==null) {
- console.log('User not found in DB/server');
- return res.status(400).send('Requested user is not in our Server!')
- }
-
- //check whether password provided is correct or not
- let isPasswordValid = await bcrypt.compare(password, user.password);
- if (isPasswordValid != true) throw new Error("Password not correct");
-
- //check posts with userId provided in DB
- await Post.find({ user: userId });
-
- //deletes all posts of provided userId
- Post.deleteMany({ user: userId }).exec();
-
- //deletes the user with provided userId
- User.findOneAndDelete({ userId: userId }).exec();
-
- console.log(`User with userId '${userId}' and all it's data are deleted`);
- /******** SEND RESPONSE TO USER ABOUT DELETION **************/
- res.status(200).send(`User with userId '${userId}' and all it's data are deleted`);
-
- } catch (err) {
- console.log("Error: ", err.message);
- res.status(400).send(err.message);
- }
+ /**** GET USERID & PASSWORD FROM REQUEST BODY ********/
+ const { userId, password } = req.body;
+ /**** CHECK FOR USERID & PASSWORD PROVIDED BY USER OR NOT ********/
+ if (!userId || !password) {
+ console.log("UserID/Password not provided");
+ throw new Error("UserID/Password not provided");
+ }
+
+ //find the User in DB from requested userId
+ const user = await User.findOne({
+ userId: userId,
+ });
+
+ /******** CHECK WHETHER USER IN OUR DB OR NOT ***************/
+ if (user == null) {
+ console.log("User not found in DB/server");
+ return res.status(400).send("Requested user is not in our Server!");
+ }
+
+ //check whether password provided is correct or not
+ let isPasswordValid = await bcrypt.compare(password, user.password);
+ if (isPasswordValid != true) throw new Error("Password not correct");
+
+ //check posts with userId provided in DB
+ await Post.find({ user: userId });
+
+ //deletes all posts of provided userId
+ Post.deleteMany({ user: userId }).exec();
+
+ //deletes the user with provided userId
+ User.findOneAndDelete({ userId: userId }).exec();
+
+ console.log(`User with userId '${userId}' and all it's data are deleted`);
+ /******** SEND RESPONSE TO USER ABOUT DELETION **************/
+ res
+ .status(200)
+ .send(`User with userId '${userId}' and all it's data are deleted`);
+ } catch (err) {
+ console.log("Error: ", err.message);
+ res.status(400).send(err.message);
+ }
};
diff --git a/controllers/post.controllers.js b/controllers/post.controllers.js
index e1b0395..6f6fc4f 100644
--- a/controllers/post.controllers.js
+++ b/controllers/post.controllers.js
@@ -1,94 +1,93 @@
-require('dotenv').config()
-const Post = require('../models/post.model');
-const User = require('../models/user.model')
-const jwt = require('jsonwebtoken');
-const { SECRET } = require('../configs/server.config.js')
-const { formatDate } = require("../utils/formatDate") //to convert & view UTC date to Indian Time format (doesn't modify in MongoDB database)
+require("dotenv").config();
+const Post = require("../models/post.model");
+const User = require("../models/user.model");
+const jwt = require("jsonwebtoken");
+const { JWT_SECRET_KEY } = require("../configs/server.config.js");
+const { formatDate } = require("../utils/formatDate"); //to convert & view UTC date to Indian Time format (doesn't modify in MongoDB database)
/********* STORE POST TO DB REQUESTED BY USER *********/
exports.addPost = async (req, res) => {
- try {
- // Extract the post data from the request body
- const { title, content } = req.body;
- // Verify that the user is authenticated by checking the JWT token in the Authorization header
- let token = req.headers['x-access-token'];
-
- const decoded = jwt.verify(token, SECRET);
- // console.log(decoded);
- const user = await User.findOne({ userId: decoded.userId });
-
- //check if title of post is already in DB or not
- const titleCheck = await Post.findOne({ title: title })
-
- if(titleCheck) {
- console.log("Title already in DB, create unique title");
- return res.status(409).send("Title already in DB, create unique title")
- }
-
- // Create a new post with the user ID included
- const createPost = new Post({
- title,
- content
- });
-
- // Save the post to the database
- await createPost.save();
- console.log("Post created successfully", createPost);
- res.status(201).send({Message: "Post created successfully",createPost});
-
- } catch (error) {
- console.log("Error at post.controller:", error.message);
- res.status(500).send('Internal Server Error');
+ try {
+ // Extract the post data from the request body
+ const { title, content } = req.body;
+ // Verify that the user is authenticated by checking the JWT token in the Authorization header
+ let token = req.headers["x-access-token"];
+
+ const decoded = jwt.verify(token, JWT_SECRET_KEY);
+ // console.log(decoded);
+ const user = await User.findOne({ userId: decoded.userId });
+
+ //check if title of post is already in DB or not
+ const titleCheck = await Post.findOne({ title: title });
+
+ if (titleCheck) {
+ console.log("Title already in DB, create unique title");
+ return res.status(409).send("Title already in DB, create unique title");
}
+
+ // Create a new post with the user ID included
+ const createPost = new Post({
+ title,
+ content,
+ });
+
+ // Save the post to the database
+ await createPost.save();
+ console.log("Post created successfully", createPost);
+ res.status(201).send({ Message: "Post created successfully", createPost });
+ } catch (error) {
+ console.log("Error at post.controller:", error.message);
+ res.status(500).send("Internal Server Error");
+ }
};
exports.getPostByUserId = async (req, res) => {
- try {
- //Verify that the user is authenticated
- //by checking the JWT token in the Authorization header
- const token = req.headers['x-access-token'];
-
- const decoded = jwt.verify(token, SECRET);
- // console.log(decoded);
- // console.log("decoded.id", decoded.userId);
-
- if(!req.body.userId) throw new Error //if no id is provided
-
- if(decoded.userId != req.body.userId){
- console.log("Unauthorized User/User not in our Server");
- return res.status(401).send('Unauthorized User!');
- }
-
- const user = await User.findOne({ userId: decoded.userId });
-
- const posts = await Post.find({
- user: user._id
- });
-
- // Check if any posts were found
- if (posts.length === 0) {
- console.log("No posts found");
- return res.status(404).send('Post/Posts not found!');
- }
-
- // Return the found posts
- let allPostData = [];
- posts.forEach(element => {
- const IndiaDateCreatedAt = formatDate(element.createdAt)
- const IndiaDateUpdatedAt = formatDate(element.updatedAt)
- let postData = {
- id: element._id,
- title: element.title,
- content: element.content,
- createdAt: IndiaDateCreatedAt,
- updatedAt: IndiaDateUpdatedAt
- }
- allPostData.push(postData);
- });
- console.log(allPostData);
- res.status(200).send(allPostData);
- // const IndiaDateCreatedAt = formatDate(posts.createdAt)
- // const IndiaDateUpdatedAt = formatDate(posts.updatedAt)
+ try {
+ //Verify that the user is authenticated
+ //by checking the JWT token in the Authorization header
+ const token = req.headers["x-access-token"];
+
+ const decoded = jwt.verify(token, JWT_SECRET_KEY);
+ // console.log(decoded);
+ // console.log("decoded.id", decoded.userId);
+
+ if (!req.body.userId) throw new Error(); //if no id is provided
+
+ if (decoded.userId != req.body.userId) {
+ console.log("Unauthorized User/User not in our Server");
+ return res.status(401).send("Unauthorized User!");
+ }
+
+ const user = await User.findOne({ userId: decoded.userId });
+
+ const posts = await Post.find({
+ user: user._id,
+ });
+
+ // Check if any posts were found
+ if (posts.length === 0) {
+ console.log("No posts found");
+ return res.status(404).send("Post/Posts not found!");
+ }
+
+ // Return the found posts
+ let allPostData = [];
+ posts.forEach((element) => {
+ const IndiaDateCreatedAt = formatDate(element.createdAt);
+ const IndiaDateUpdatedAt = formatDate(element.updatedAt);
+ let postData = {
+ id: element._id,
+ title: element.title,
+ content: element.content,
+ createdAt: IndiaDateCreatedAt,
+ updatedAt: IndiaDateUpdatedAt,
+ };
+ allPostData.push(postData);
+ });
+ console.log(allPostData);
+ res.status(200).send(allPostData);
+ // const IndiaDateCreatedAt = formatDate(posts.createdAt)
+ // const IndiaDateUpdatedAt = formatDate(posts.updatedAt)
// const postData = {
// id: posts._id,
// title: posts.title,
@@ -98,64 +97,60 @@ exports.getPostByUserId = async (req, res) => {
// // updatedAt: IndiaDateUpdatedAt
// }
// // res.status(200).send(postData);
-
- } catch (err) {
- console.error("Internal Error:",err.message);
- res.status(500).send("Internal Server Error");
- }
+ } catch (err) {
+ console.error("Internal Error:", err.message);
+ res.status(500).send("Internal Server Error");
+ }
};
exports.deletePostByPostTitle = async (req, res) => {
- const titleReq = req.query.title
- try {
- if(!titleReq) throw new Error("Title of post not provided");
+ const titleReq = req.query.title;
+ try {
+ if (!titleReq) throw new Error("Title of post not provided");
+
+ const post = await Post.findOneAndDelete({ title: titleReq }).exec();
+
+ if (post == null) throw new Error("Post is null/Post is not in server");
+
+ console.log(`Post Deleted for Title: "${titleReq}"`);
+ return res
+ .status(200)
+ .send(`Post Deleted Successfully! for Title: "${titleReq}"`);
+ } catch (err) {
+ console.log("Error deleting post: ", err.message);
+ return res.status(500).send("Internal Server Error");
+ }
+};
- const post = await Post.findOneAndDelete({ title : titleReq }).exec()
+exports.deleteAllPostsByUserId = async (req, res) => {
+ try {
+ //Verify that the user is authenticated by checking the
+ //JWT token in the Authorization header
+ const token = req.headers["x-access-token"];
+ const decoded = jwt.verify(token, JWT_SECRET_KEY);
- if(post == null) throw new Error("Post is null/Post is not in server");
+ const user = await User.findOne({
+ userId: decoded.id,
+ });
- console.log(`Post Deleted for Title: "${titleReq}"`);
- return res.status(200).send(`Post Deleted Successfully! for Title: "${titleReq}"`)
-
- }
- catch (err)
- {
- console.log("Error deleting post: ", err.message);
- return res.status(500).send("Internal Server Error");
- }
+ const userId = user._id.toString(); //convert ObjectId to String
-}
+ const posts = await Post.find({
+ user: userId,
+ });
-exports.deleteAllPostsByUserId = async (req, res) => {
- try {
- //Verify that the user is authenticated by checking the
- //JWT token in the Authorization header
- const token = req.headers['x-access-token'];
- const decoded = jwt.verify(token, SECRET);
-
- const user = await User.findOne({
- userId: decoded.id
- })
-
- const userId = user._id.toString() //convert ObjectId to String
-
- const posts = await Post.find({
- user: userId
- })
-
- if(posts.length === 0) {
- console.log(`No posts with user: ${user.name}`);
- return res.status(200).send(`No posts with user: ${user.name}`)
- }
- Post.deleteMany({ user : userId}).exec()
- console.log(`All Posts of '${user.name}' with Id: ${userId} are deleted`);
- return res.status(200).send(`All Posts of '${user.name}' are deleted`)
- } catch(err) {
- console.log("Error deleting posts", err);
- return res.status(500).send("Internal Server Error")
+ if (posts.length === 0) {
+ console.log(`No posts with user: ${user.name}`);
+ return res.status(200).send(`No posts with user: ${user.name}`);
}
-}
-
+ Post.deleteMany({ user: userId }).exec();
+ console.log(`All Posts of '${user.name}' with Id: ${userId} are deleted`);
+ return res.status(200).send(`All Posts of '${user.name}' are deleted`);
+ } catch (err) {
+ console.log("Error deleting posts", err);
+ return res.status(500).send("Internal Server Error");
+ }
+};
/*** TEST DATA USING AXIOS CONTROLLER**** */
// exports.getData = async (req, res) => {
@@ -167,4 +162,4 @@ exports.deleteAllPostsByUserId = async (req, res) => {
// console.log('Error:', error);
// res.status(500).send('Internal Error')
// }
-// }
\ No newline at end of file
+// }
diff --git a/index.js b/index.js
index 1147304..55f12c0 100644
--- a/index.js
+++ b/index.js
@@ -1,49 +1,50 @@
-require('dotenv').config() //needed to fetch data from .env file
-const express = require('express')
-const mongoose = require('mongoose')
-const userIP = require('user-ip');
-const cookieParser = require('cookie-parser');
-const securedHeaders = require('helmet');
-
-const { PORT } = require('./configs/server.config.js')
-const app = express()
-const db_url = process.env.DB_URL || `mongodb://127.0.0.1:27017/${process.env.DB_NAME}`
-
-app.use(express.urlencoded({extended:true}));
+require("dotenv").config(); //needed to fetch data from .env file
+const express = require("express");
+const mongoose = require("mongoose");
+const userIP = require("user-ip");
+const cookieParser = require("cookie-parser");
+const securedHeaders = require("helmet");
+const { limiter } = require("./utils/api-rate-limiter.js");
+
+const { SERVER_PORT } = require("./configs/server.config.js");
+const app = express();
+const db_url =
+ process.env.DB_URL || `mongodb://127.0.0.1:27017/${process.env.DB_NAME}`;
+
+app.use(express.urlencoded({ extended: true }));
app.use(express.json());
app.use(cookieParser());
app.use(securedHeaders());
+app.use(limiter);
// connect to MongoDB
// Event handlers for successful connection and connection error
const connectDB = async () => {
- try { console.time('Mongodb connection time:');
- const connect = await mongoose.connect(db_url,{
- // useNewUrlParser: true, // DEPRECATED
- // useUnifiedTopology: true // DEPRECATED
- });
- console.timeEnd('Mongodb connection time:');
- console.log(`MongoDB Connected to Host: ${connect.connection.host}`);
- } catch (error) {
- console.timeEnd();
- console.log("Can't connect to DB:", error.message);
- }
-}
+ console.time("Mongodb connection time:");
+ const connect = await mongoose.connect(db_url, {
+ // useNewUrlParser: true, // DEPRECATED
+ // useUnifiedTopology: true // DEPRECATED
+ });
+ console.timeEnd("Mongodb connection time:");
+ console.log(`MongoDB Connected to Host: ${connect.connection.host}`);
+};
// FIRST CONNECT TO MONGODB THEN START LISTENING TO REQUESTS
-connectDB().then(() => {
- app.listen(PORT, () => {
- console.log(`Listening all requests on port ${PORT}`);
+connectDB()
+ .then(() => {
+ app.listen(SERVER_PORT, () => {
+ console.log(`Listening all requests on port ${SERVER_PORT}`);
+ });
})
-}).catch((e)=>console.log(e)) // IF DB CONNECT FAILED, CATCH ERROR
-
+ .catch((e) => {
+ console.log("Can't connect to DB:", e.message); // IF DB CONNECT FAILED, CATCH ERROR
+ });
/**************************HOME PAGE**************************** */
-app.get('/', (req, res) => {
- res.status(200).send(`Backend Running! đ
`)
+app.get("/", (req, res) => {
+ res.status(200).send(`Backend Running! đ
`);
});
/************** IMPORT API's ********** */
require("./routes/auth.routes")(app);
require("./routes/post.routes")(app);
-
diff --git a/middlewares/AuthJWT.js b/middlewares/AuthJWT.js
index f334a05..0902988 100644
--- a/middlewares/AuthJWT.js
+++ b/middlewares/AuthJWT.js
@@ -1,33 +1,30 @@
const jwt = require("jsonwebtoken");
-const { SECRET } = require('../configs/server.config.js')
+const { JWT_SECRET_KEY } = require("../configs/server.config.js");
/***** VERIFY ACCESS TOKEN TO CHECK IF TOKEN IS PRESENT AND VALID ********/
let verifyToken = (req, res, next) => {
+ /*** GET THE ACCESS TOKEN FORM HEADER *****/
+ let token = req.headers["x-access-token"];
- /*** GET THE ACCESS TOKEN FORM HEADER *****/
- let token = req.headers['x-access-token'];
-
- /*** CHECK WHETHER ACCESS TOKEN PROVIDED OR NOT *****/
- if (!token) {
- console.log("No token provided đ")
- return res.status(403).send("No Token Provided");
+ /*** CHECK WHETHER ACCESS TOKEN PROVIDED OR NOT *****/
+ if (!token) {
+ console.log("No token provided đ");
+ return res.status(403).send("No Token Provided");
+ }
+ /*** VERIFY ACCESS TOKEN FOR THE USER *****/
+ jwt.verify(token, JWT_SECRET_KEY, (err, decoded) => {
+ // Checks for any error in JWT verification
+ if (err) {
+ console.log("Error --> Token Expired", err.name);
+ return res.status(401).send("Token Expired!");
+ } else {
+ /****** USER FETCHED FROM TOKEN ASSIGNED TO REQ.USERID ********/
+ req.userId = decoded.userId;
+ next();
}
- /*** VERIFY ACCESS TOKEN FOR THE USER *****/
- jwt.verify(token, SECRET, (err, decoded) => {
- // Checks for any error in JWT verification
- if (err) {
- console.log("Error:", err.message);
- return res.status(401).send("Token Expired!");
- }
-
- else {
- /****** ASSIGN USER FETCHED FROM TOKEN TO REQ.USER ********/
- req.userId = decoded.userId
- next();
- }
- })
-}
+ });
+};
module.exports = {
- verifyToken
-}
\ No newline at end of file
+ verifyToken,
+};
diff --git a/middlewares/validateUserRequestBody.js b/middlewares/validateUserRequestBody.js
index acb38d9..9a46144 100644
--- a/middlewares/validateUserRequestBody.js
+++ b/middlewares/validateUserRequestBody.js
@@ -7,7 +7,6 @@ const { formatDate } = require("../utils/formatDate");
/*** VALIDATE DETAILS PROVIDED BY USER ******/
let validateUserRequestBody = async (req, res, next) => {
try {
-
let { name, userId, email, password } = req.body;
/************** NAME VALIDATION ****************** */
if (!name) {
@@ -35,7 +34,9 @@ let validateUserRequestBody = async (req, res, next) => {
updatedAt: IndiaDateUpdatedAt,
};
console.log("\nuserId with this data already exists!", userData);
- return res.status(400).send(`Failed!, userId '${userData.userId}' already exists!`);
+ return res
+ .status(400)
+ .send(`Failed!, userId '${userData.userId}' already exists!`);
}
/************** EMAIL VALIDATION ****************** */
@@ -49,8 +50,10 @@ let validateUserRequestBody = async (req, res, next) => {
const emailReq = await User.findOne({ email: email });
if (emailReq != null) {
- console.log("\nEmail \'"+email+ "\' already exists");
- return res.status(400).send(`Failed! Email '${emailReq.email}' already exists`);
+ console.log("\nEmail '" + email + "' already exists");
+ return res
+ .status(400)
+ .send(`Failed! Email '${emailReq.email}' already exists`);
}
/************* PASSWORD VALIDATION ***************** */
@@ -60,7 +63,6 @@ let validateUserRequestBody = async (req, res, next) => {
}
next();
-
} catch (error) {
console.log("Error at validateUserRequestBody:", error.message);
res.status(400).send("Internal Error Occured!");
diff --git a/models/post.model.js b/models/post.model.js
index 935d351..8c7bbc0 100644
--- a/models/post.model.js
+++ b/models/post.model.js
@@ -1,34 +1,33 @@
-
//import mongoose
const mongoose = require("mongoose");
-const User = require('./user.model')
+const User = require("./user.model");
//route handler
const postSchema = new mongoose.Schema({
- title: {
- type: String,
- required: [true,`NOT PROVIDED đ`],
- unique:true
- },
- content: {
- type: String,
- required: [true,`NOT PROVIDED âšī¸`]
- },
- user: {
- type: mongoose.Schema.Types.ObjectId,
- required:true,
- ref: "User"
- },
- createdAt: {
- type: Date,
- immutable:true,
- default: Date.now()
- },
- updatedAt: {
- type: Date,
- default: Date.now()
- }
-})
+ title: {
+ type: String,
+ required: [true, `NOT PROVIDED đ`],
+ unique: true,
+ },
+ content: {
+ type: String,
+ required: [true, `NOT PROVIDED âšī¸`],
+ },
+ user: {
+ type: mongoose.Schema.Types.ObjectId,
+ required: true,
+ ref: "User",
+ },
+ createdAt: {
+ type: Date,
+ immutable: true,
+ default: Date.now(),
+ },
+ updatedAt: {
+ type: Date,
+ default: Date.now(),
+ },
+});
//export
-module.exports = mongoose.model("Post", postSchema);
\ No newline at end of file
+module.exports = mongoose.model("Post", postSchema);
diff --git a/models/user.model.js b/models/user.model.js
index c045280..513b175 100644
--- a/models/user.model.js
+++ b/models/user.model.js
@@ -1,39 +1,38 @@
-
//import mongoose
const mongoose = require("mongoose");
//route handler
const userSchema = new mongoose.Schema({
- name:{
- type:String,
- required:[true,`NOT PROVIDED đ`]
- },
- userId:{
- type:String,
- required:[true,`NOT PROVIDED đ`],
- unique:true,
- lowercase:true
- },
- password:{
- type:String,
- required:[true,`NOT PROVIDED đ`]
- },
- email:{
- type:String,
- required:[true,`NOT PROVIDED đ `],
- lowercase:true,
- unique:true
- },
- createdAt: {
- type: Date,
- immutable:true,
- default: Date.now()
- },
- updatedAt: {
- type: Date,
- default: Date.now()
- }
-})
+ name: {
+ type: String,
+ required: [true, `NOT PROVIDED đ`],
+ },
+ userId: {
+ type: String,
+ required: [true, `NOT PROVIDED đ`],
+ unique: true,
+ lowercase: true,
+ },
+ password: {
+ type: String,
+ required: [true, `NOT PROVIDED đ`],
+ },
+ email: {
+ type: String,
+ required: [true, `NOT PROVIDED đ `],
+ lowercase: true,
+ unique: true,
+ },
+ createdAt: {
+ type: Date,
+ immutable: true,
+ default: Date.now(),
+ },
+ updatedAt: {
+ type: Date,
+ default: Date.now(),
+ },
+});
//export
-module.exports = mongoose.model("User", userSchema);
\ No newline at end of file
+module.exports = mongoose.model("User", userSchema);
diff --git a/package-lock.json b/package-lock.json
index e00dbef..9b40e2d 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -15,6 +15,7 @@
"cookie-parser": "^1.4.6",
"dotenv": "^16.3.1",
"express": "^4.18.2",
+ "express-rate-limit": "^7.1.5",
"helmet": "^7.1.0",
"jsonwebtoken": "^9.0.2",
"moment-timezone": "^0.5.44",
@@ -24,7 +25,8 @@
"user-ip": "^1.1.0"
},
"devDependencies": {
- "nodemon": "^3.0.2"
+ "nodemon": "^3.0.2",
+ "prettier": "^3.1.1"
},
"engines": {
"node": ">=18.19.0"
@@ -589,6 +591,20 @@
"node": ">= 0.10.0"
}
},
+ "node_modules/express-rate-limit": {
+ "version": "7.1.5",
+ "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.1.5.tgz",
+ "integrity": "sha512-/iVogxu7ueadrepw1bS0X0kaRC/U0afwiYRSLg68Ts+p4Dc85Q5QKsOnPS/QUjPMHvOJQtBDrZgvkOzf8ejUYw==",
+ "engines": {
+ "node": ">= 16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/express-rate-limit"
+ },
+ "peerDependencies": {
+ "express": "4 || 5 || ^5.0.0-beta.1"
+ }
+ },
"node_modules/express/node_modules/body-parser": {
"version": "1.20.1",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz",
@@ -1647,6 +1663,21 @@
"url": "https://github.com/sponsors/jonschlinkert"
}
},
+ "node_modules/prettier": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.1.1.tgz",
+ "integrity": "sha512-22UbSzg8luF4UuZtzgiUOfcGM8s4tjBv6dJRT7j275NXsy2jb4aJa4NNveul5x4eqlF1wuhuR2RElK71RvmVaw==",
+ "dev": true,
+ "bin": {
+ "prettier": "bin/prettier.cjs"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/prettier/prettier?sponsor=1"
+ }
+ },
"node_modules/proxy-addr": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
diff --git a/package.json b/package.json
index fdce14c..2784250 100644
--- a/package.json
+++ b/package.json
@@ -7,7 +7,9 @@
"scripts": {
"build": "npm install bcrypt body-parser dotenv express jsonwebtoken mongoose node-rest-client moment-timezone user-ip axios node-fetch --save",
"start": "node ./index.js",
- "dev": "nodemon ./index.js"
+ "dev": "nodemon ./index.js",
+ "prettier": "prettier --write \"./**/*.{js,jsx,ts,tsx,json,css,scss,md}\"",
+ "prettier-check": "prettier --check \"./**/*.{js,jsx,ts,tsx,json,css,scss,md}\""
},
"author": "Nilanjan",
"license": "ISC",
@@ -18,6 +20,7 @@
"cookie-parser": "^1.4.6",
"dotenv": "^16.3.1",
"express": "^4.18.2",
+ "express-rate-limit": "^7.1.5",
"helmet": "^7.1.0",
"jsonwebtoken": "^9.0.2",
"moment-timezone": "^0.5.44",
@@ -27,7 +30,8 @@
"user-ip": "^1.1.0"
},
"devDependencies": {
- "nodemon": "^3.0.2"
+ "nodemon": "^3.0.2",
+ "prettier": "^3.1.1"
},
"keywords": [
"mongodb",
diff --git a/routes/auth.routes.js b/routes/auth.routes.js
index 9020113..ae4d3ca 100644
--- a/routes/auth.routes.js
+++ b/routes/auth.routes.js
@@ -1,15 +1,17 @@
-const {signup, signin, changePassword, deleteUser} = require("../controllers/auth.controller");
+const {
+ signup,
+ signin,
+ changePassword,
+ deleteUser,
+} = require("../controllers/auth.controller");
const { verifyToken } = require("../middlewares/AuthJWT");
-const {validateUserRequestBody} = require("../middlewares/validateUserRequestBody");
+const {
+ validateUserRequestBody,
+} = require("../middlewares/validateUserRequestBody");
-module.exports = function(app){
- app.post("/api/v1/auth/signup",
- [validateUserRequestBody] , signup);
- app.post("/api/v1/auth/signin", signin)
- app.put("/changepassword",
- verifyToken,
- changePassword)
- app.delete("/deleteuser",
- verifyToken,
- deleteUser)
-}
\ No newline at end of file
+module.exports = function (app) {
+ app.post("/api/v1/auth/signup", [validateUserRequestBody], signup);
+ app.post("/api/v1/auth/signin", signin);
+ app.put("/changepassword", verifyToken, changePassword);
+ app.delete("/deleteuser", verifyToken, deleteUser);
+};
diff --git a/routes/post.routes.js b/routes/post.routes.js
index 94329fb..ab2623c 100644
--- a/routes/post.routes.js
+++ b/routes/post.routes.js
@@ -1,13 +1,19 @@
-'use strict';
-const { addPost, getPostByUserId, deletePostByPostTitle, deleteAllPostsByUserId, getData} = require('../controllers/post.controllers')
-const { verifyToken } = require('../middlewares/AuthJWT')
+"use strict";
+const {
+ addPost,
+ getPostByUserId,
+ deletePostByPostTitle,
+ deleteAllPostsByUserId,
+ getData,
+} = require("../controllers/post.controllers");
+const { verifyToken } = require("../middlewares/AuthJWT");
module.exports = function (app) {
- app.post("/api/v1/addPost", verifyToken, addPost)
- app.get("/api/v1/posts", verifyToken, getPostByUserId)
- app.delete("/api/v1/posts", verifyToken, deletePostByPostTitle)
- app.delete("/api/v1/allposts", verifyToken, deleteAllPostsByUserId)
+ app.post("/api/v1/addPost", verifyToken, addPost);
+ app.get("/api/v1/posts", verifyToken, getPostByUserId);
+ app.delete("/api/v1/posts", verifyToken, deletePostByPostTitle);
+ app.delete("/api/v1/allposts", verifyToken, deleteAllPostsByUserId);
- /*** TEST DATA USING AXIOS API **** */
- // app.get("/api/getdata", getData)
-}
\ No newline at end of file
+ /*** TEST DATA USING AXIOS API **** */
+ // app.get("/api/getdata", getData)
+};
diff --git a/utils/api-rate-limiter.js b/utils/api-rate-limiter.js
new file mode 100644
index 0000000..934084e
--- /dev/null
+++ b/utils/api-rate-limiter.js
@@ -0,0 +1,25 @@
+require("dotenv").config({
+ path: `./.env`,
+});
+const { rateLimit } = require("express-rate-limit");
+
+const limiter = rateLimit({
+ windowMs: process.env.RATE_LIMIT_TIME * 60 * 1000 || 15 * 60 * 1000, // default is 15 minutes
+ max: process.env.MAX_REQUESTS || 10, // Limit each IP to 5 requests per `window` (here, per 15 minutes)
+ skipFailedRequests: true, // If any request not failed that will not count
+ standardHeaders: true, // Return rate limit info in the `RateLimit-*` headers
+ legacyHeaders: false, // Disable the `X-RateLimit-*` headers
+ keyGenerator: (req) => `${req.protocol}://${req.hostname}${req.originalUrl}`,
+ message: async (req, res) => {
+ console.log(
+ `\n${req.protocol}://${req.hostname}${req.originalUrl} [${req.method}] -> API is Rate-limited`
+ );
+ return res.status(429).json({
+ message: "Too many requests, please try again later.",
+ });
+ },
+});
+
+module.exports = {
+ limiter
+};