├── .gitignore ├── README.MD ├── controllers ├── authController.js ├── orderController.js ├── productController.js ├── reviewController.js └── userController.js ├── db └── connect.js ├── errors ├── bad-request.js ├── custom-api.js ├── index.js ├── not-found.js ├── unauthenticated.js └── unauthorized.js ├── middleware ├── authentication.js ├── error-handler.js ├── full-auth.js └── not-found.js ├── mockData ├── orders.json └── products.json ├── models ├── orderModel.js ├── productModel.js ├── reviewModel.js └── userModel.js ├── package-lock.json ├── package.json ├── public └── uploads │ ├── 5103Dl2NOvL._SL1500_.jpg │ ├── dd.png │ └── example.jpeg.png ├── routes ├── authRoutes.js ├── orderRoutes.js ├── productRoutes.js ├── reviewRoutes.js └── userRoutes.js ├── server.js └── utils ├── checkPermissions.js ├── createTokenUser.js ├── index.js └── jwt.js /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | .env -------------------------------------------------------------------------------- /README.MD: -------------------------------------------------------------------------------- 1 | 2 | # eCommerce REST API 3 | This is a REST API built using Node.js and Express.js for eCommerce. It provides endpoints for user authentication, product management, review management, and order management. 4 | 5 | 6 | ## Features 7 | 8 | - User registration 9 | - User login and logout 10 | - Product creation, update, deletion, and retrieval 11 | - Review creation, update, deletion, and retrieval 12 | - Order creation, update, deletion, and retrieval 13 | - Image upload for products 14 | - JWT-based authentication 15 | 16 | ## Tech Stack 17 | **Backend:** 18 | - Node.js 19 | - Express.js 20 | - MongoDB 21 | - Mongoose 22 | - JWT 23 | 24 | **Image Upload:** 25 | - Cloudinary API 26 | 27 | **Data Storage:** 28 | - MongoDB 29 | 30 | **User Authentication:** 31 | - JSON Web Tokens (JWT) 32 | ## API Reference 33 | 34 | #### User Authentication 35 | - `POST /api/v1/auth/register` - Register a new user. 36 | - `POST /api/v1/auth/login` - Login with an existing user. 37 | - `GET /api/v1/auth/logout` - Logout the current user. 38 | 39 | #### User 40 | - `GET /api/v1/users` - Get all users. 41 | - `GET /api/v1/users/:id` - Get single user. 42 | - `GET /api/v1/users/showMe` - Show current user. 43 | - `PATCH /api/v1/users/updateUser` - Update user profile. 44 | - `PATCH /api/v1/users/updateUserPassword` - Update User Password. 45 | 46 | #### Products 47 | - `GET /api/v1/products` - Get all products. 48 | - `POST /api/v1/products` - Create a new product. 49 | - `POST /api/v1/products/uploadImage` - Upload an image for a product.. 50 | - `GET /api/v1/products/:id` - Get a single product by ID . 51 | - `PATCH /api/v1/products/:id` - Update a product by ID . 52 | - `DELETE /api/v1/products/:id` - Delete a product by ID . 53 | - `GET /api/v1/products/:id/reviews` - Get a single product review. 54 | 55 | #### Product Reviews 56 | - `GET /api/v1/reviews` - Get all reviews for a product. 57 | - `POST /api/v1/reviews` - Create a new review for a product. 58 | - `GET /api/v1/reviews/:id` - Get a single review by ID. 59 | - `PATCH /api/v1/reviews/:id` - Update a review by ID. 60 | - `DELETE /api/v1/reviews/:id` - Delete a review by ID. 61 | 62 | #### Order 63 | - `GET /api/v1/orders` - Get all orders. 64 | - `POST /api/v1/orders` - Create a order . 65 | - `GET /api/v1/orders/:id` - Get a single order. 66 | - `PATCH /api/v1/orders/:id` - Update a order. 67 | - `GET /api/v1/orders/showAllMyOrder` - Get a current user orders. 68 | 69 | 70 | ## Installation 71 | 72 | 73 | 1. Clone the repository. 74 | 75 | 76 | ``` 77 | git clone https://github.com/themodernmonk7/E-commerce-API.git 78 | ``` 79 | 80 | 2. Navigate to the project directory. 81 | 82 | ``` 83 | cd E-commerce-API 84 | ``` 85 | 86 | 3. Install the dependencies. 87 | 88 | ``` 89 | npm install 90 | ``` 91 | 92 | 4. Set the environment variables in a .env file in the root directory of the project. 93 | Example: 94 | ``` 95 | PORT=5000 96 | MONGO_URL=mongodb://localhost/ecommerce 97 | JWT_SECRET=your_secret_key_here 98 | JWT_LIFETIME=1d 99 | 100 | ``` 101 | 102 | 5. Start the application. 103 | 104 | ``` 105 | npm run dev 106 | 107 | ``` 108 | ## License 109 | Indago is licensed under the MIT License. See the LICENSE file for more information. 110 | [MIT](https://choosealicense.com/licenses/mit/) 111 | 112 | 113 | ## Feedback 114 | 115 | Please let us know your thoughts on my app by sending any suggestions or feedback to kumaravishek2015@gmail.com. 116 | 117 | 118 | ## 🚀 About Me 119 | - Full-stack developer with experience in modern web development 120 | - Proficient in React and JavaScript for frontend development 121 | - Skilled in Redux Toolkit for state management in frontend applications 122 | - Experienced in Node.js and Express.js for backend development 123 | - Ability to create efficient and high-performance server-side applications 124 | - Proficient in using TailwindCSS for creating visually appealing and responsive user interfaces 125 | - Passionate about building user-friendly and efficient applications using cutting-edge technologies 126 | - Always eager to learn new technologies and improve skills to provide the best solutions to clients. 127 | 128 | 129 | ## 🔗 Links 130 | [](https://themodernmonk7.netlify.app) 131 | [](https://www.linkedin.com/in/themodernmonk7) 132 | [](https://twitter.com/themodernmonk7) 133 | [](https://peerlist.io/themodernmonk7) 134 | 135 | -------------------------------------------------------------------------------- /controllers/authController.js: -------------------------------------------------------------------------------- 1 | const User = require("../models/userModel") 2 | const { StatusCodes } = require("http-status-codes") 3 | const CustomError = require("../errors") 4 | const { createTokenUser, attachCookiesToResponse } = require("../utils") 5 | 6 | // Register User 7 | const register = async (req, res) => { 8 | const { name, email, password } = req.body 9 | const emailAlreadyExists = await User.findOne({ email }) 10 | if (emailAlreadyExists) { 11 | throw new CustomError.BadRequestError("Email already exists") 12 | } 13 | // Add first registered user as admin 14 | const isFirstAccount = (await User.countDocuments({})) === 0 15 | const role = isFirstAccount ? "admin" : "user" 16 | const user = await User.create({ name, email, password, role }) 17 | // Create token user 18 | const tokenUser = createTokenUser(user) 19 | attachCookiesToResponse({ res, user: tokenUser }) 20 | res.status(StatusCodes.CREATED).json({ user: tokenUser }) 21 | } 22 | 23 | // Login User 24 | const login = async (req, res) => { 25 | const { email, password } = req.body 26 | if (!email || !password) { 27 | throw new CustomError.BadRequestError("Please provide email and password") 28 | } 29 | const user = await User.findOne({ email }) 30 | if (!user) { 31 | throw CustomError.UnauthorizedError("No user found") 32 | } 33 | const isPasswordCorrect = await user.comparePassword(password) 34 | if (!isPasswordCorrect) { 35 | throw new CustomError.UnauthenticatedError("Invalid Credentials") 36 | } 37 | const tokenUser = createTokenUser(user) 38 | attachCookiesToResponse({ res, user: tokenUser }) 39 | res.status(StatusCodes.OK).json({ user: tokenUser, msg: "Login successful!" }) 40 | } 41 | 42 | // Logout User 43 | const logout = async (req, res) => { 44 | res.cookie("token", null, { 45 | httpOnly: true, 46 | expires: new Date(Date.now()), 47 | }) 48 | // res.send() ==== this is for production 49 | res.status(StatusCodes.OK).json({ msg: "user logged out!" }) // this is for testing during development 50 | } 51 | 52 | // const logout = async (req, res) => { 53 | // res.cookie("token", "no token", { 54 | // httpOnly: true, 55 | // expires: new Date(Date.now()), 56 | // }) 57 | // res.send() 58 | // } 59 | 60 | module.exports = { 61 | register, 62 | login, 63 | logout, 64 | } 65 | -------------------------------------------------------------------------------- /controllers/orderController.js: -------------------------------------------------------------------------------- 1 | // ** =================== GET ALL ORDERS =================== 2 | const getAllOrders = async (req, res) => { 3 | res.send("Get all orders") 4 | } 5 | 6 | // ** =================== GET SINGLE ORDERS =================== 7 | const getSingleOrder = async (req, res) => { 8 | res.send("Get single order") 9 | } 10 | 11 | // ** =================== GET CURRENT USER ORDERS ORDERS =================== 12 | const getCurrentUserOrder = async (req, res) => { 13 | res.send("Get current user order") 14 | } 15 | 16 | // ** =================== CREATE ORDER =================== 17 | const createOrder = async (req, res) => { 18 | res.send("Create order") 19 | } 20 | 21 | // ** =================== UPDATE ORDER =================== 22 | const updateOrder = async (req, res) => { 23 | res.send("Update order") 24 | } 25 | 26 | module.exports = { 27 | getAllOrders, 28 | getSingleOrder, 29 | getCurrentUserOrder, 30 | createOrder, 31 | updateOrder, 32 | } 33 | -------------------------------------------------------------------------------- /controllers/productController.js: -------------------------------------------------------------------------------- 1 | const Product = require("../models/productModel") 2 | const CustomError = require("../errors") 3 | const { StatusCodes } = require("http-status-codes") 4 | const path = require("path") 5 | 6 | // ** =================== CREATE PRODUCT =================== 7 | const createProduct = async (req, res) => { 8 | req.body.user = req.user.userId 9 | const product = await Product.create(req.body) 10 | res.status(StatusCodes.CREATED).json({ product }) 11 | } 12 | 13 | // ** =================== GET ALL PRODUCTS =================== 14 | const getAllProducts = async (req, res) => { 15 | const product = await Product.find({}) 16 | res.status(StatusCodes.OK).json({ total_products: product.length, product }) 17 | } 18 | 19 | // ** =================== GET SINGLE PRODUCT =================== 20 | const getSingleProduct = async (req, res) => { 21 | const { id: productId } = req.params 22 | const product = await Product.findOne({ _id: productId }).populate("reviews") 23 | if (!product) { 24 | throw new CustomError.BadRequestError(`No product with the id ${productId}`) 25 | } 26 | res.status(StatusCodes.OK).json({ product }) 27 | } 28 | 29 | // ** =================== UPDATE PRODUCT =================== 30 | const updateProduct = async (req, res) => { 31 | const { id: productId } = req.params 32 | const product = await Product.findOneAndUpdate({ _id: productId }, req.body, { 33 | new: true, 34 | runValidators: true, 35 | }) 36 | if (!product) { 37 | throw new CustomError.BadRequestError(`No product with the id ${productId}`) 38 | } 39 | res.status(StatusCodes.OK).json({ product }) 40 | } 41 | 42 | // ** =================== DELETE PRODUCT =================== 43 | const deleteProduct = async (req, res) => { 44 | const { id: productId } = req.params 45 | const product = await Product.findOneAndDelete({ _id: productId }) 46 | if (!product) { 47 | throw new CustomError.BadRequestError(`No product with the id ${productId}`) 48 | } 49 | await product.remove() // this will trigger the pre remove hook 50 | res.status(StatusCodes.OK).json({ msg: "Success! Product removed" }) 51 | } 52 | 53 | // ** =================== UPLOAD IMAGE PRODUCT =================== 54 | const uploadImage = async (req, res) => { 55 | //console.log(req.files) 56 | if (!req.files) { 57 | throw new CustomError.BadRequestError("No File Uploaded") 58 | } 59 | const productImage = req.files.image 60 | if (!productImage.mimetype.startsWith("image")) { 61 | throw new CustomError.BadRequestError("Please Upload Image") 62 | } 63 | const maxSize = 1024 * 1024 64 | if (productImage.size > maxSize) { 65 | throw new CustomError.BadRequestError("Please upload image smaller 1MB") 66 | } 67 | const imagePath = path.join( 68 | __dirname, 69 | "../public/uploads/" + `${productImage.name}` 70 | ) 71 | await productImage.mv(imagePath) 72 | res.status(StatusCodes.OK).json({ image: `/uploads/${productImage.name}` }) 73 | } 74 | 75 | module.exports = { 76 | createProduct, 77 | getAllProducts, 78 | getSingleProduct, 79 | updateProduct, 80 | deleteProduct, 81 | uploadImage, 82 | } 83 | -------------------------------------------------------------------------------- /controllers/reviewController.js: -------------------------------------------------------------------------------- 1 | const Review = require("../models/reviewModel") 2 | const Product = require("../models/productModel") 3 | const CustomError = require("../errors") 4 | const { StatusCodes } = require("http-status-codes") 5 | const { checkPermissions } = require("../utils") 6 | 7 | // ** =================== CREATE REVIEW =================== 8 | const createReview = async (req, res) => { 9 | const { product: productId } = req.body 10 | 11 | // check if product is valid or not 12 | const isValidProduct = await Product.findOne({ _id: productId }) 13 | if (!isValidProduct) { 14 | throw new CustomError.NotFoundError(`No product with id: ${productId}`) 15 | } 16 | // check if user is already submitted the review for this product or not 17 | const alreadySubmittedReview = await Review.findOne({ 18 | product: productId, 19 | user: req.user.userId, 20 | }) 21 | if (alreadySubmittedReview) { 22 | throw new CustomError.BadRequestError( 23 | "Already submitted review for this product" 24 | ) 25 | } 26 | req.body.user = req.user.userId 27 | const review = await Review.create(req.body) 28 | res.status(StatusCodes.CREATED).json({ review }) 29 | } 30 | 31 | // ** =================== GET ALL REVIEWS =================== 32 | const getAllReviews = async (req, res) => { 33 | const review = await Review.find({}).populate({ 34 | path: "product", 35 | select: "name company, price", 36 | }) 37 | res.status(StatusCodes.OK).json({ total_review: review.length, review }) 38 | } 39 | 40 | // ** =================== GET SINGLE REVIEW =================== 41 | const getSingleReview = async (req, res) => { 42 | const { id: reviewId } = req.params 43 | const review = await Review.findOne({ _id: reviewId }) 44 | if (!review) { 45 | throw new CustomError.NotFoundError(`No review with the the id ${reviewId}`) 46 | } 47 | res.status(StatusCodes.OK).json({ review }) 48 | } 49 | 50 | // ** =================== UPDATE REVIEW =================== 51 | const updateReview = async (req, res) => { 52 | const { id: reviewId } = req.params 53 | const { rating, title, comment } = req.body 54 | // Check if review exists or not 55 | const review = await Review.findOne({ _id: reviewId }) 56 | if (!review) { 57 | throw new CustomError.NotFoundError(`No review with the the id ${reviewId}`) 58 | } 59 | checkPermissions(req.user, review.user) 60 | 61 | review.rating = rating 62 | review.title = title 63 | review.comment = comment 64 | 65 | await review.save() 66 | res.status(StatusCodes.OK).json({ msg: "Success! Review has been updated" }) 67 | } 68 | 69 | // ** =================== DELETE REVIEW =================== 70 | const deleteReview = async (req, res) => { 71 | const { id: reviewId } = req.params 72 | const review = await Review.findOne({ _id: reviewId }) 73 | if (!review) { 74 | throw new CustomError.NotFoundError(`No review with the the id ${reviewId}`) 75 | } 76 | checkPermissions(req.user, review.user) 77 | await review.remove() 78 | res.status(StatusCodes.OK).json({ msg: "Success! Review has been deleted" }) 79 | } 80 | 81 | // ** =================== GET SINGLE PRODUCT REVIEW =================== 82 | const getSingleProductReviews = async (req, res) => { 83 | const { id: productId } = req.params 84 | const reviews = await Review.find({ product: productId }) 85 | res.status(StatusCodes.OK).json({ total_reviews: reviews.length, reviews }) 86 | } 87 | 88 | module.exports = { 89 | createReview, 90 | getAllReviews, 91 | getSingleReview, 92 | updateReview, 93 | deleteReview, 94 | getSingleProductReviews, 95 | } 96 | -------------------------------------------------------------------------------- /controllers/userController.js: -------------------------------------------------------------------------------- 1 | const User = require("../models/userModel") 2 | const { StatusCodes } = require("http-status-codes") 3 | const CustomError = require("../errors") 4 | const { 5 | createTokenUser, 6 | attachCookiesToResponse, 7 | checkPermissions, 8 | } = require("../utils") 9 | 10 | //** ======================== Get all users ======================== 11 | const getAllUsers = async (req, res) => { 12 | // console.log(req.user) //check if I am getting req.user from authentication.js or not 13 | const user = await User.find({ role: "user" }).select("-password") 14 | res.status(StatusCodes.OK).json({ total_users: user.length, user }) 15 | } 16 | 17 | //** ======================== Get single user ======================== 18 | const getSingleUser = async (req, res) => { 19 | const { id: userId } = req.params 20 | const user = await User.findOne({ _id: userId }).select("-password") 21 | if (!user) { 22 | throw CustomError.NotFoundError("User does not exist") 23 | } 24 | checkPermissions(req.user, user._id) 25 | res.status(StatusCodes.OK).json({ user }) 26 | } 27 | 28 | //** ======================== Show current user ======================== 29 | const showCurrentUser = async (req, res) => { 30 | res.status(StatusCodes.OK).json({ user: req.user }) 31 | } 32 | 33 | //** ======================== Update user ======================== 34 | const updateUser = async (req, res) => { 35 | const { name, email } = req.body 36 | if (!name || !email) { 37 | throw new CustomError.BadRequestError("Please provide value") 38 | } 39 | const user = await User.findOne({ _id: req.user.userId }) 40 | 41 | user.name = name 42 | user.email = email 43 | await user.save() 44 | const tokenUser = createTokenUser(user) 45 | attachCookiesToResponse({ res, user: tokenUser }) 46 | res.status(StatusCodes.OK).json({ user: tokenUser }) 47 | } 48 | 49 | //** ======================== Update user password ======================== 50 | const updateUserPassword = async (req, res) => { 51 | const { oldPassword, newPassword } = req.body 52 | if (!oldPassword || !newPassword) { 53 | throw new CustomError.BadRequestError("Please provide both values") 54 | } 55 | const user = await User.findOne({ _id: req.user.userId }) 56 | const isPasswordCorrect = await user.comparePassword(oldPassword) 57 | if (!isPasswordCorrect) { 58 | throw new CustomError.UnauthenticatedError("Wrong password provided") 59 | } 60 | user.password = newPassword 61 | await user.save() 62 | res.status(StatusCodes.OK).json({ msg: "Success! Password Updated" }) 63 | 64 | } 65 | 66 | module.exports = { 67 | getAllUsers, 68 | getSingleUser, 69 | showCurrentUser, 70 | updateUser, 71 | updateUserPassword, 72 | } 73 | -------------------------------------------------------------------------------- /db/connect.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose") 2 | 3 | const connectDB = (url) => { 4 | return mongoose.connect(url) 5 | } 6 | 7 | module.exports = connectDB 8 | -------------------------------------------------------------------------------- /errors/bad-request.js: -------------------------------------------------------------------------------- 1 | const { StatusCodes } = require('http-status-codes'); 2 | const CustomAPIError = require('./custom-api'); 3 | 4 | class BadRequestError extends CustomAPIError { 5 | constructor(message) { 6 | super(message); 7 | this.statusCode = StatusCodes.BAD_REQUEST; 8 | } 9 | } 10 | 11 | module.exports = BadRequestError; 12 | -------------------------------------------------------------------------------- /errors/custom-api.js: -------------------------------------------------------------------------------- 1 | class CustomAPIError extends Error { 2 | constructor(message) { 3 | super(message) 4 | } 5 | } 6 | 7 | module.exports = CustomAPIError 8 | -------------------------------------------------------------------------------- /errors/index.js: -------------------------------------------------------------------------------- 1 | const CustomAPIError = require("./custom-api") 2 | const UnauthenticatedError = require("./unauthenticated") 3 | const NotFoundError = require("./not-found") 4 | const BadRequestError = require("./bad-request") 5 | const UnauthorizedError = require("./unauthorized") 6 | module.exports = { 7 | CustomAPIError, 8 | UnauthenticatedError, 9 | NotFoundError, 10 | BadRequestError, 11 | UnauthorizedError, 12 | } 13 | -------------------------------------------------------------------------------- /errors/not-found.js: -------------------------------------------------------------------------------- 1 | const { StatusCodes } = require('http-status-codes'); 2 | const CustomAPIError = require('./custom-api'); 3 | 4 | class NotFoundError extends CustomAPIError { 5 | constructor(message) { 6 | super(message); 7 | this.statusCode = StatusCodes.NOT_FOUND; 8 | } 9 | } 10 | 11 | module.exports = NotFoundError; 12 | -------------------------------------------------------------------------------- /errors/unauthenticated.js: -------------------------------------------------------------------------------- 1 | const { StatusCodes } = require("http-status-codes") 2 | const CustomAPIError = require("./custom-api") 3 | 4 | class UnauthenticatedError extends CustomAPIError { 5 | constructor(message) { 6 | super(message) 7 | this.statusCode = StatusCodes.UNAUTHORIZED 8 | } 9 | } 10 | 11 | module.exports = UnauthenticatedError 12 | -------------------------------------------------------------------------------- /errors/unauthorized.js: -------------------------------------------------------------------------------- 1 | const { StatusCodes } = require("http-status-codes") 2 | const CustomAPIError = require("./custom-api") 3 | 4 | class UnauthorizedError extends CustomAPIError { 5 | constructor(message) { 6 | super(message) 7 | this.statusCode = StatusCodes.FORBIDDEN 8 | } 9 | } 10 | 11 | module.exports = UnauthorizedError 12 | -------------------------------------------------------------------------------- /middleware/authentication.js: -------------------------------------------------------------------------------- 1 | const CustomError = require("../errors") 2 | const { isTokenValid } = require("../utils") 3 | 4 | const authenticateUser = async (req, res, next) => { 5 | const token = req.signedCookies.token 6 | if (!token) { 7 | throw new CustomError.UnauthenticatedError("Authentication invalid") 8 | } 9 | 10 | try { 11 | const { name, userId, role } = isTokenValid({ token }) 12 | req.user = { name, userId, role } 13 | next() 14 | } catch (error) { 15 | throw new CustomError.UnauthenticatedError("Authentication Invalid") 16 | } 17 | } 18 | 19 | const authorizePermissions = (...roles) => { 20 | return (req, res, next) => { 21 | if (!roles.includes(req.user.role)) { 22 | throw new CustomError.UnauthorizedError( 23 | "Unauthorized to access to this route" 24 | ) 25 | } 26 | next() 27 | } 28 | } 29 | 30 | module.exports = { authenticateUser, authorizePermissions } -------------------------------------------------------------------------------- /middleware/error-handler.js: -------------------------------------------------------------------------------- 1 | const { StatusCodes } = require("http-status-codes") 2 | const errorHandlerMiddleware = (err, req, res, next) => { 3 | let customError = { 4 | // set default 5 | statusCode: err.statusCode || StatusCodes.INTERNAL_SERVER_ERROR, 6 | msg: err.message || "Something went wrong try again later", 7 | } 8 | if (err.name === "ValidationError") { 9 | customError.msg = Object.values(err.errors) 10 | .map((item) => item.message) 11 | .join(",") 12 | customError.statusCode = 400 13 | } 14 | if (err.code && err.code === 11000) { 15 | customError.msg = `Duplicate value entered for ${Object.keys( 16 | err.keyValue 17 | )} field, please choose another value` 18 | customError.statusCode = 400 19 | } 20 | if (err.name === "CastError") { 21 | customError.msg = `No item found with id : ${err.value}` 22 | customError.statusCode = 404 23 | } 24 | 25 | return res.status(customError.statusCode).json({ msg: customError.msg }) 26 | } 27 | 28 | module.exports = errorHandlerMiddleware 29 | -------------------------------------------------------------------------------- /middleware/full-auth.js: -------------------------------------------------------------------------------- 1 | const CustomError = require('../errors'); 2 | const { isTokenValid } = require('../utils/jwt'); 3 | 4 | const authenticateUser = async (req, res, next) => { 5 | let token; 6 | // check header 7 | const authHeader = req.headers.authorization; 8 | if (authHeader && authHeader.startsWith('Bearer')) { 9 | token = authHeader.split(' ')[1]; 10 | } 11 | // check cookies 12 | else if (req.cookies.token) { 13 | token = req.cookies.token; 14 | } 15 | 16 | if (!token) { 17 | throw new CustomError.UnauthenticatedError('Authentication invalid'); 18 | } 19 | try { 20 | const payload = isTokenValid(token); 21 | 22 | // Attach the user and his permissions to the req object 23 | req.user = { 24 | userId: payload.user.userId, 25 | role: payload.user.role, 26 | }; 27 | 28 | next(); 29 | } catch (error) { 30 | throw new CustomError.UnauthenticatedError('Authentication invalid'); 31 | } 32 | }; 33 | 34 | const authorizeRoles = (...roles) => { 35 | return (req, res, next) => { 36 | if (!roles.includes(req.user.role)) { 37 | throw new CustomError.UnauthorizedError( 38 | 'Unauthorized to access this route' 39 | ); 40 | } 41 | next(); 42 | }; 43 | }; 44 | 45 | module.exports = { authenticateUser, authorizeRoles }; 46 | -------------------------------------------------------------------------------- /middleware/not-found.js: -------------------------------------------------------------------------------- 1 | const notFound = (req, res) => res.status(404).send('Route does not exist') 2 | 3 | module.exports = notFound 4 | -------------------------------------------------------------------------------- /mockData/orders.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "tax": 399, 4 | "shippingFee": 499, 5 | "items": [ 6 | { 7 | "name": "accent chair", 8 | "price": 2599, 9 | "image": "https://dl.airtable.com/.attachmentThumbnails/e8bc3791196535af65f40e36993b9e1f/438bd160", 10 | "amount": 34, 11 | "product": "6126ad3424d2bd09165a68c8" 12 | } 13 | ] 14 | }, 15 | { 16 | "tax": 499, 17 | "shippingFee": 799, 18 | "items": [ 19 | { 20 | "name": "bed", 21 | "price": 2699, 22 | "image": "https://dl.airtable.com/.attachmentThumbnails/e8bc3791196535af65f40e36993b9e1f/438bd160", 23 | "amount": 3, 24 | "product": "6126ad3424d2bd09165a68c7" 25 | }, 26 | { 27 | "name": "chair", 28 | "price": 2999, 29 | "image": "https://dl.airtable.com/.attachmentThumbnails/e8bc3791196535af65f40e36993b9e1f/438bd160", 30 | "amount": 2, 31 | "product": "6126ad3424d2bd09165a68c4" 32 | } 33 | ] 34 | } 35 | ] 36 | -------------------------------------------------------------------------------- /mockData/products.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "accent chair", 4 | "price": 25999, 5 | "image": "https://dl.airtable.com/.attachmentThumbnails/e8bc3791196535af65f40e36993b9e1f/438bd160", 6 | "colors": ["#ff0000", "#00ff00", "#0000ff"], 7 | "company": "marcos", 8 | "description": "Cloud bread VHS hell of banjo bicycle rights jianbing umami mumblecore etsy 8-bit pok pok +1 wolf. Vexillologist yr dreamcatcher waistcoat, authentic chillwave trust fund. Viral typewriter fingerstache pinterest pork belly narwhal. Schlitz venmo everyday carry kitsch pitchfork chillwave iPhone taiyaki trust fund hashtag kinfolk microdosing gochujang live-edge", 9 | "category": "office" 10 | }, 11 | { 12 | "name": "albany sectional", 13 | "price": 109999, 14 | "image": "https://dl.airtable.com/.attachmentThumbnails/0be1af59cf889899b5c9abb1e4db38a4/d631ac52", 15 | "colors": ["#000", "#ffb900"], 16 | "company": "liddy", 17 | "description": "Cloud bread VHS hell of banjo bicycle rights jianbing umami mumblecore etsy 8-bit pok pok +1 wolf. Vexillologist yr dreamcatcher waistcoat, authentic chillwave trust fund. Viral typewriter fingerstache pinterest pork belly narwhal. Schlitz venmo everyday carry kitsch pitchfork chillwave iPhone taiyaki trust fund hashtag kinfolk microdosing gochujang live-edge", 18 | "category": "kitchen" 19 | }, 20 | { 21 | "name": "armchair", 22 | "price": 12599, 23 | "image": "https://dl.airtable.com/.attachmentThumbnails/530c07c5ade5acd9934c8dd334458b86/cf91397f", 24 | "colors": ["#000", "#00ff00", "#0000ff"], 25 | "company": "marcos", 26 | "description": "Cloud bread VHS hell of banjo bicycle rights jianbing umami mumblecore etsy 8-bit pok pok +1 wolf. Vexillologist yr dreamcatcher waistcoat, authentic chillwave trust fund. Viral typewriter fingerstache pinterest pork belly narwhal. Schlitz venmo everyday carry kitsch pitchfork chillwave iPhone taiyaki trust fund hashtag kinfolk microdosing gochujang live-edge", 27 | "category": "bedroom" 28 | }, 29 | { 30 | "name": "emperor bed", 31 | "price": 23999, 32 | "image": "https://dl.airtable.com/.attachmentThumbnails/0446e84c5bca9643de3452a61b2d6195/1b32f48b", 33 | "colors": ["#0000ff", "#000"], 34 | "company": "ikea", 35 | "description": "Cloud bread VHS hell of banjo bicycle rights jianbing umami mumblecore etsy 8-bit pok pok +1 wolf. Vexillologist yr dreamcatcher waistcoat, authentic chillwave trust fund. Viral typewriter fingerstache pinterest pork belly narwhal. Schlitz venmo everyday carry kitsch pitchfork chillwave iPhone taiyaki trust fund hashtag kinfolk microdosing gochujang live-edge", 36 | "category": "bedroom" 37 | } 38 | ] 39 | -------------------------------------------------------------------------------- /models/orderModel.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose") 2 | 3 | const OrderSchema = new mongoose.Schema( 4 | { 5 | tax: { 6 | type: Number, 7 | required: true, 8 | }, 9 | 10 | shippingFee: { 11 | type: Number, 12 | required: true, 13 | }, 14 | 15 | subtotal: { 16 | type: Number, 17 | required: true, 18 | }, 19 | 20 | total: { 21 | type: Number, 22 | required: true, 23 | }, 24 | 25 | orderItems: [SingleOrderItemSchema], 26 | 27 | status: { 28 | type: String, 29 | enum: ["pending", "failed", "paid", "delivered", "canceled"], 30 | default: "pending", 31 | }, 32 | 33 | user: { 34 | type: mongoose.Types.ObjectId, 35 | ref: "User", 36 | required: true, 37 | }, 38 | 39 | clientSecret: { 40 | type: String, 41 | required: true, 42 | }, 43 | 44 | paymentIntentID: { 45 | type: String, 46 | }, 47 | }, 48 | { timestamps: true } 49 | ) 50 | 51 | module.exports = mongoose.model("Order", OrderSchema) 52 | -------------------------------------------------------------------------------- /models/productModel.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose") 2 | 3 | const ProductSchema = new mongoose.Schema( 4 | { 5 | name: { 6 | type: String, 7 | required: [true, "Please provide product name"], 8 | trim: true, 9 | maxlength: [100, "Name cannot be more than 100 characters"], 10 | }, 11 | 12 | price: { 13 | type: Number, 14 | required: [true, "Please provide price value"], 15 | default: 0, 16 | }, 17 | 18 | description: { 19 | type: String, 20 | required: [true, "Please provide description"], 21 | maxlength: [1000, "Description can not be more than 1000 characters"], 22 | }, 23 | 24 | image: { 25 | type: String, 26 | default: "/uploads/example.jpeg", 27 | }, 28 | 29 | category: { 30 | type: String, 31 | required: [true, "Please provide category"], 32 | enum: ["office", "kitchen", "bedroom"], 33 | }, 34 | 35 | company: { 36 | type: String, 37 | required: [true, "Please provide company"], 38 | enum: { 39 | values: ["ikea", "liddy", "marcos"], 40 | message: "{VALUE} is not supported", 41 | }, 42 | }, 43 | 44 | colors: { 45 | type: [String], 46 | default: ["#222"], 47 | required: true, 48 | }, 49 | 50 | featured: { 51 | type: Boolean, 52 | default: false, 53 | }, 54 | 55 | freeShipping: { 56 | type: Boolean, 57 | default: false, 58 | }, 59 | 60 | inventory: { 61 | type: Number, 62 | required: true, 63 | default: 15, 64 | }, 65 | 66 | averageRating: { 67 | type: Number, 68 | default: 0, 69 | }, 70 | 71 | numOfReviews: { 72 | type: Number, 73 | default: 0, 74 | }, 75 | 76 | user: { 77 | type: mongoose.Types.ObjectId, 78 | ref: "User", 79 | required: true, 80 | }, 81 | }, 82 | { timestamps: true, toJSON: { virtuals: true }, toObject: { virtuals: true } } 83 | ) 84 | 85 | // If I want to search single product, in tha product I also want to have all reviews associated with that product. 86 | ProductSchema.virtual("reviews", { 87 | ref: "Review", 88 | localField: "_id", 89 | foreignField: "product", 90 | justOne: false, 91 | // match: {rating: 5} // Get the reviews whose rating is only 5. 92 | }) 93 | 94 | ProductSchema.pre("remove", async function (next) { 95 | // Go to 'Reveiw; and delete all the review that are associated with this particular product 96 | await this.model("Review").deleteMany({ product: this._id }) 97 | }) 98 | 99 | module.exports = new mongoose.model("Product", ProductSchema) 100 | -------------------------------------------------------------------------------- /models/reviewModel.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose") 2 | 3 | const ReviewSchema = new mongoose.Schema( 4 | { 5 | rating: { 6 | type: String, 7 | min: 1, 8 | max: 5, 9 | required: [true, "Please provide rating"], 10 | }, 11 | 12 | title: { 13 | type: String, 14 | trim: true, 15 | required: [true, "Please provide review title"], 16 | maxlength: 100, 17 | }, 18 | 19 | comment: { 20 | type: String, 21 | required: [true, "Please provide review text"], 22 | }, 23 | 24 | user: { 25 | type: mongoose.Types.ObjectId, 26 | ref: "User", 27 | required: true, 28 | }, 29 | 30 | product: { 31 | type: mongoose.Types.ObjectId, 32 | ref: "Product", 33 | required: true, 34 | }, 35 | }, 36 | { timestamps: true } 37 | ) 38 | 39 | // User can leave only one review for a product 40 | ReviewSchema.index({product: 1, user: 1}, {unique: true}) 41 | 42 | // Average rating 43 | ReviewSchema.statics.calculateAverageRating = async function (productId) { 44 | const result = await this.aggregate([ 45 | { $match: { product: productId } }, 46 | { 47 | $group: { 48 | _id: null, 49 | averageRating: { $avg: "$rating" }, 50 | numOfReviews: { $sum: 1 }, 51 | }, 52 | }, 53 | //console.log(result) // {_id: null, averageRating: 4, numOfReviews: 2} 54 | ]) 55 | 56 | try { 57 | await this.model("Product").findOneAndUpdate( 58 | { _id: productId }, 59 | { 60 | // 👇 This is optional chaining in JavaScript 61 | averageRating: Math.ceil(result[0]?.averageRating || 0), 62 | numOfReviews: result[0]?.numOfReviews || 0, 63 | } 64 | ) 65 | } catch (error) { 66 | console.log(error) 67 | } 68 | } 69 | 70 | ReviewSchema.post("save", async function () { 71 | await this.constructor.calculateAverageRating(this.product) 72 | }) 73 | ReviewSchema.post("remove", async function () { 74 | await this.constructor.calculateAverageRating(this.product) 75 | }) 76 | 77 | 78 | module.exports = mongoose.model("Review", ReviewSchema) 79 | -------------------------------------------------------------------------------- /models/userModel.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose") 2 | const validator = require("validator") 3 | const bcrypt = require("bcryptjs") 4 | 5 | const UserSchema = new mongoose.Schema({ 6 | name: { 7 | type: String, 8 | required: [true, "Please provide name"], 9 | minlength: 3, 10 | maxlength: 50, 11 | }, 12 | 13 | email: { 14 | type: String, 15 | unique: true, 16 | required: [true, "Please provide email"], 17 | // Custom Validators package 18 | validate: { 19 | // validator package 20 | validator: validator.isEmail, 21 | message: "Please provide valid email", 22 | }, 23 | }, 24 | 25 | password: { 26 | type: String, 27 | required: [true, "Please provide password"], 28 | }, 29 | role: { 30 | type: String, 31 | enum: ["admin", "user"], 32 | default: "user", 33 | }, 34 | }) 35 | 36 | // Hashed the password before saving the user into database 37 | UserSchema.pre("save", async function () { 38 | // console.log(this.modifiedPaths()); 39 | // console.log(this.isModified("name")); 40 | // Only run this 👇 function if password was modified (not on other update functions) 41 | if (!this.isModified("password")) return 42 | const salt = await bcrypt.genSalt(10) 43 | this.password = await bcrypt.hash(this.password, salt) 44 | }) 45 | 46 | // Compare password 47 | UserSchema.methods.comparePassword = async function (candidatePassword) { 48 | const isMatch = await bcrypt.compare(candidatePassword, this.password) 49 | return isMatch 50 | } 51 | 52 | module.exports = mongoose.model("User", UserSchema) 53 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "e-commerce-api-1", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@hapi/hoek": { 8 | "version": "9.3.0", 9 | "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", 10 | "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==" 11 | }, 12 | "@hapi/topo": { 13 | "version": "5.1.0", 14 | "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz", 15 | "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==", 16 | "requires": { 17 | "@hapi/hoek": "^9.0.0" 18 | } 19 | }, 20 | "@sideway/address": { 21 | "version": "4.1.4", 22 | "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.4.tgz", 23 | "integrity": "sha512-7vwq+rOHVWjyXxVlR76Agnvhy8I9rpzjosTESvmhNeXOXdZZB15Fl+TI9x1SiHZH5Jv2wTGduSxFDIaq0m3DUw==", 24 | "requires": { 25 | "@hapi/hoek": "^9.0.0" 26 | } 27 | }, 28 | "@sideway/formula": { 29 | "version": "3.0.0", 30 | "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.0.tgz", 31 | "integrity": "sha512-vHe7wZ4NOXVfkoRb8T5otiENVlT7a3IAiw7H5M2+GO+9CDgcVUUsX1zalAztCmwyOr2RUTGJdgB+ZvSVqmdHmg==" 32 | }, 33 | "@sideway/pinpoint": { 34 | "version": "2.0.0", 35 | "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", 36 | "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==" 37 | }, 38 | "@types/node": { 39 | "version": "18.0.6", 40 | "resolved": "https://registry.npmjs.org/@types/node/-/node-18.0.6.tgz", 41 | "integrity": "sha512-/xUq6H2aQm261exT6iZTMifUySEt4GR5KX8eYyY+C4MSNPqSh9oNIP7tz2GLKTlFaiBbgZNxffoR3CVRG+cljw==" 42 | }, 43 | "@types/webidl-conversions": { 44 | "version": "6.1.1", 45 | "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-6.1.1.tgz", 46 | "integrity": "sha512-XAahCdThVuCFDQLT7R7Pk/vqeObFNL3YqRyFZg+AqAP/W1/w3xHaIxuW7WszQqTbIBOPRcItYJIou3i/mppu3Q==" 47 | }, 48 | "@types/whatwg-url": { 49 | "version": "8.2.2", 50 | "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-8.2.2.tgz", 51 | "integrity": "sha512-FtQu10RWgn3D9U4aazdwIE2yzphmTJREDqNdODHrbrZmmMqI0vMheC/6NE/J1Yveaj8H+ela+YwWTjq5PGmuhA==", 52 | "requires": { 53 | "@types/node": "*", 54 | "@types/webidl-conversions": "*" 55 | } 56 | }, 57 | "abbrev": { 58 | "version": "1.1.1", 59 | "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", 60 | "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", 61 | "dev": true 62 | }, 63 | "accepts": { 64 | "version": "1.3.8", 65 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", 66 | "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", 67 | "requires": { 68 | "mime-types": "~2.1.34", 69 | "negotiator": "0.6.3" 70 | } 71 | }, 72 | "anymatch": { 73 | "version": "3.1.2", 74 | "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", 75 | "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", 76 | "dev": true, 77 | "requires": { 78 | "normalize-path": "^3.0.0", 79 | "picomatch": "^2.0.4" 80 | } 81 | }, 82 | "argparse": { 83 | "version": "1.0.10", 84 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", 85 | "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", 86 | "requires": { 87 | "sprintf-js": "~1.0.2" 88 | } 89 | }, 90 | "array-flatten": { 91 | "version": "1.1.1", 92 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", 93 | "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" 94 | }, 95 | "balanced-match": { 96 | "version": "1.0.2", 97 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", 98 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" 99 | }, 100 | "base64-js": { 101 | "version": "1.5.1", 102 | "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", 103 | "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" 104 | }, 105 | "basic-auth": { 106 | "version": "2.0.1", 107 | "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", 108 | "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", 109 | "requires": { 110 | "safe-buffer": "5.1.2" 111 | }, 112 | "dependencies": { 113 | "safe-buffer": { 114 | "version": "5.1.2", 115 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 116 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" 117 | } 118 | } 119 | }, 120 | "bcryptjs": { 121 | "version": "2.4.3", 122 | "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz", 123 | "integrity": "sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==" 124 | }, 125 | "binary-extensions": { 126 | "version": "2.2.0", 127 | "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", 128 | "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", 129 | "dev": true 130 | }, 131 | "body-parser": { 132 | "version": "1.20.0", 133 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.0.tgz", 134 | "integrity": "sha512-DfJ+q6EPcGKZD1QWUjSpqp+Q7bDQTsQIF4zfUAtZ6qk+H/3/QRhg9CEp39ss+/T2vw0+HaidC0ecJj/DRLIaKg==", 135 | "requires": { 136 | "bytes": "3.1.2", 137 | "content-type": "~1.0.4", 138 | "debug": "2.6.9", 139 | "depd": "2.0.0", 140 | "destroy": "1.2.0", 141 | "http-errors": "2.0.0", 142 | "iconv-lite": "0.4.24", 143 | "on-finished": "2.4.1", 144 | "qs": "6.10.3", 145 | "raw-body": "2.5.1", 146 | "type-is": "~1.6.18", 147 | "unpipe": "1.0.0" 148 | } 149 | }, 150 | "brace-expansion": { 151 | "version": "1.1.11", 152 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 153 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 154 | "requires": { 155 | "balanced-match": "^1.0.0", 156 | "concat-map": "0.0.1" 157 | } 158 | }, 159 | "braces": { 160 | "version": "3.0.2", 161 | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", 162 | "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", 163 | "dev": true, 164 | "requires": { 165 | "fill-range": "^7.0.1" 166 | } 167 | }, 168 | "bson": { 169 | "version": "4.6.5", 170 | "resolved": "https://registry.npmjs.org/bson/-/bson-4.6.5.tgz", 171 | "integrity": "sha512-uqrgcjyOaZsHfz7ea8zLRCLe1u+QGUSzMZmvXqO24CDW7DWoW1qiN9folSwa7hSneTSgM2ykDIzF5kcQQ8cwNw==", 172 | "requires": { 173 | "buffer": "^5.6.0" 174 | } 175 | }, 176 | "buffer": { 177 | "version": "5.7.1", 178 | "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", 179 | "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", 180 | "requires": { 181 | "base64-js": "^1.3.1", 182 | "ieee754": "^1.1.13" 183 | } 184 | }, 185 | "buffer-equal-constant-time": { 186 | "version": "1.0.1", 187 | "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", 188 | "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" 189 | }, 190 | "busboy": { 191 | "version": "1.6.0", 192 | "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", 193 | "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", 194 | "requires": { 195 | "streamsearch": "^1.1.0" 196 | } 197 | }, 198 | "bytes": { 199 | "version": "3.1.2", 200 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", 201 | "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" 202 | }, 203 | "call-bind": { 204 | "version": "1.0.2", 205 | "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", 206 | "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", 207 | "requires": { 208 | "function-bind": "^1.1.1", 209 | "get-intrinsic": "^1.0.2" 210 | } 211 | }, 212 | "chokidar": { 213 | "version": "3.5.3", 214 | "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", 215 | "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", 216 | "dev": true, 217 | "requires": { 218 | "anymatch": "~3.1.2", 219 | "braces": "~3.0.2", 220 | "fsevents": "~2.3.2", 221 | "glob-parent": "~5.1.2", 222 | "is-binary-path": "~2.1.0", 223 | "is-glob": "~4.0.1", 224 | "normalize-path": "~3.0.0", 225 | "readdirp": "~3.6.0" 226 | } 227 | }, 228 | "concat-map": { 229 | "version": "0.0.1", 230 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 231 | "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" 232 | }, 233 | "content-disposition": { 234 | "version": "0.5.4", 235 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", 236 | "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", 237 | "requires": { 238 | "safe-buffer": "5.2.1" 239 | } 240 | }, 241 | "content-type": { 242 | "version": "1.0.4", 243 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", 244 | "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" 245 | }, 246 | "cookie": { 247 | "version": "0.5.0", 248 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", 249 | "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==" 250 | }, 251 | "cookie-parser": { 252 | "version": "1.4.6", 253 | "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.6.tgz", 254 | "integrity": "sha512-z3IzaNjdwUC2olLIB5/ITd0/setiaFMLYiZJle7xg5Fe9KWAceil7xszYfHHBtDFYLSgJduS2Ty0P1uJdPDJeA==", 255 | "requires": { 256 | "cookie": "0.4.1", 257 | "cookie-signature": "1.0.6" 258 | }, 259 | "dependencies": { 260 | "cookie": { 261 | "version": "0.4.1", 262 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", 263 | "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==" 264 | } 265 | } 266 | }, 267 | "cookie-signature": { 268 | "version": "1.0.6", 269 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", 270 | "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" 271 | }, 272 | "cors": { 273 | "version": "2.8.5", 274 | "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", 275 | "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", 276 | "requires": { 277 | "object-assign": "^4", 278 | "vary": "^1" 279 | } 280 | }, 281 | "debug": { 282 | "version": "2.6.9", 283 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 284 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 285 | "requires": { 286 | "ms": "2.0.0" 287 | } 288 | }, 289 | "denque": { 290 | "version": "2.1.0", 291 | "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", 292 | "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==" 293 | }, 294 | "depd": { 295 | "version": "2.0.0", 296 | "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", 297 | "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" 298 | }, 299 | "destroy": { 300 | "version": "1.2.0", 301 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", 302 | "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==" 303 | }, 304 | "dotenv": { 305 | "version": "16.0.1", 306 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.1.tgz", 307 | "integrity": "sha512-1K6hR6wtk2FviQ4kEiSjFiH5rpzEVi8WW0x96aztHVMhEspNpc4DVOUTEHtEva5VThQ8IaBX1Pe4gSzpVVUsKQ==" 308 | }, 309 | "ecdsa-sig-formatter": { 310 | "version": "1.0.11", 311 | "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", 312 | "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", 313 | "requires": { 314 | "safe-buffer": "^5.0.1" 315 | } 316 | }, 317 | "ee-first": { 318 | "version": "1.1.1", 319 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 320 | "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" 321 | }, 322 | "encodeurl": { 323 | "version": "1.0.2", 324 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", 325 | "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==" 326 | }, 327 | "escape-html": { 328 | "version": "1.0.3", 329 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", 330 | "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" 331 | }, 332 | "etag": { 333 | "version": "1.8.1", 334 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", 335 | "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==" 336 | }, 337 | "express": { 338 | "version": "4.18.1", 339 | "resolved": "https://registry.npmjs.org/express/-/express-4.18.1.tgz", 340 | "integrity": "sha512-zZBcOX9TfehHQhtupq57OF8lFZ3UZi08Y97dwFCkD8p9d/d2Y3M+ykKcwaMDEL+4qyUolgBDX6AblpR3fL212Q==", 341 | "requires": { 342 | "accepts": "~1.3.8", 343 | "array-flatten": "1.1.1", 344 | "body-parser": "1.20.0", 345 | "content-disposition": "0.5.4", 346 | "content-type": "~1.0.4", 347 | "cookie": "0.5.0", 348 | "cookie-signature": "1.0.6", 349 | "debug": "2.6.9", 350 | "depd": "2.0.0", 351 | "encodeurl": "~1.0.2", 352 | "escape-html": "~1.0.3", 353 | "etag": "~1.8.1", 354 | "finalhandler": "1.2.0", 355 | "fresh": "0.5.2", 356 | "http-errors": "2.0.0", 357 | "merge-descriptors": "1.0.1", 358 | "methods": "~1.1.2", 359 | "on-finished": "2.4.1", 360 | "parseurl": "~1.3.3", 361 | "path-to-regexp": "0.1.7", 362 | "proxy-addr": "~2.0.7", 363 | "qs": "6.10.3", 364 | "range-parser": "~1.2.1", 365 | "safe-buffer": "5.2.1", 366 | "send": "0.18.0", 367 | "serve-static": "1.15.0", 368 | "setprototypeof": "1.2.0", 369 | "statuses": "2.0.1", 370 | "type-is": "~1.6.18", 371 | "utils-merge": "1.0.1", 372 | "vary": "~1.1.2" 373 | } 374 | }, 375 | "express-async-errors": { 376 | "version": "3.1.1", 377 | "resolved": "https://registry.npmjs.org/express-async-errors/-/express-async-errors-3.1.1.tgz", 378 | "integrity": "sha512-h6aK1da4tpqWSbyCa3FxB/V6Ehd4EEB15zyQq9qe75OZBp0krinNKuH4rAY+S/U/2I36vdLAUFSjQJ+TFmODng==" 379 | }, 380 | "express-fileupload": { 381 | "version": "1.4.0", 382 | "resolved": "https://registry.npmjs.org/express-fileupload/-/express-fileupload-1.4.0.tgz", 383 | "integrity": "sha512-RjzLCHxkv3umDeZKeFeMg8w7qe0V09w3B7oGZprr/oO2H/ISCgNzuqzn7gV3HRWb37GjRk429CCpSLS2KNTqMQ==", 384 | "requires": { 385 | "busboy": "^1.6.0" 386 | } 387 | }, 388 | "express-rate-limit": { 389 | "version": "6.4.0", 390 | "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-6.4.0.tgz", 391 | "integrity": "sha512-lxQRZI4gi3qAWTf0/Uqsyugsz57h8bd7QyllXBgJvd6DJKokzW7C5DTaNvwzvAQzwHGFaItybfYGhC8gpu0V2A==" 392 | }, 393 | "fill-range": { 394 | "version": "7.0.1", 395 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", 396 | "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", 397 | "dev": true, 398 | "requires": { 399 | "to-regex-range": "^5.0.1" 400 | } 401 | }, 402 | "finalhandler": { 403 | "version": "1.2.0", 404 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", 405 | "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", 406 | "requires": { 407 | "debug": "2.6.9", 408 | "encodeurl": "~1.0.2", 409 | "escape-html": "~1.0.3", 410 | "on-finished": "2.4.1", 411 | "parseurl": "~1.3.3", 412 | "statuses": "2.0.1", 413 | "unpipe": "~1.0.0" 414 | } 415 | }, 416 | "forwarded": { 417 | "version": "0.2.0", 418 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", 419 | "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" 420 | }, 421 | "fresh": { 422 | "version": "0.5.2", 423 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", 424 | "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==" 425 | }, 426 | "fs.realpath": { 427 | "version": "1.0.0", 428 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 429 | "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" 430 | }, 431 | "fsevents": { 432 | "version": "2.3.2", 433 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", 434 | "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", 435 | "dev": true, 436 | "optional": true 437 | }, 438 | "function-bind": { 439 | "version": "1.1.1", 440 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", 441 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" 442 | }, 443 | "get-intrinsic": { 444 | "version": "1.1.2", 445 | "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.2.tgz", 446 | "integrity": "sha512-Jfm3OyCxHh9DJyc28qGk+JmfkpO41A4XkneDSujN9MDXrm4oDKdHvndhZ2dN94+ERNfkYJWDclW6k2L/ZGHjXA==", 447 | "requires": { 448 | "function-bind": "^1.1.1", 449 | "has": "^1.0.3", 450 | "has-symbols": "^1.0.3" 451 | } 452 | }, 453 | "glob": { 454 | "version": "7.2.3", 455 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", 456 | "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", 457 | "requires": { 458 | "fs.realpath": "^1.0.0", 459 | "inflight": "^1.0.4", 460 | "inherits": "2", 461 | "minimatch": "^3.1.1", 462 | "once": "^1.3.0", 463 | "path-is-absolute": "^1.0.0" 464 | } 465 | }, 466 | "glob-parent": { 467 | "version": "5.1.2", 468 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", 469 | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", 470 | "dev": true, 471 | "requires": { 472 | "is-glob": "^4.0.1" 473 | } 474 | }, 475 | "has": { 476 | "version": "1.0.3", 477 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", 478 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", 479 | "requires": { 480 | "function-bind": "^1.1.1" 481 | } 482 | }, 483 | "has-flag": { 484 | "version": "3.0.0", 485 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 486 | "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", 487 | "dev": true 488 | }, 489 | "has-symbols": { 490 | "version": "1.0.3", 491 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", 492 | "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" 493 | }, 494 | "helmet": { 495 | "version": "5.1.0", 496 | "resolved": "https://registry.npmjs.org/helmet/-/helmet-5.1.0.tgz", 497 | "integrity": "sha512-klsunXs8rgNSZoaUrNeuCiWUxyc+wzucnEnFejUg3/A+CaF589k9qepLZZ1Jehnzig7YbD4hEuscGXuBY3fq+g==" 498 | }, 499 | "http-errors": { 500 | "version": "2.0.0", 501 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", 502 | "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", 503 | "requires": { 504 | "depd": "2.0.0", 505 | "inherits": "2.0.4", 506 | "setprototypeof": "1.2.0", 507 | "statuses": "2.0.1", 508 | "toidentifier": "1.0.1" 509 | } 510 | }, 511 | "http-status-codes": { 512 | "version": "2.2.0", 513 | "resolved": "https://registry.npmjs.org/http-status-codes/-/http-status-codes-2.2.0.tgz", 514 | "integrity": "sha512-feERVo9iWxvnejp3SEfm/+oNG517npqL2/PIA8ORjyOZjGC7TwCRQsZylciLS64i6pJ0wRYz3rkXLRwbtFa8Ng==" 515 | }, 516 | "iconv-lite": { 517 | "version": "0.4.24", 518 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", 519 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", 520 | "requires": { 521 | "safer-buffer": ">= 2.1.2 < 3" 522 | } 523 | }, 524 | "ieee754": { 525 | "version": "1.2.1", 526 | "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", 527 | "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" 528 | }, 529 | "ignore-by-default": { 530 | "version": "1.0.1", 531 | "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", 532 | "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", 533 | "dev": true 534 | }, 535 | "inflight": { 536 | "version": "1.0.6", 537 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 538 | "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", 539 | "requires": { 540 | "once": "^1.3.0", 541 | "wrappy": "1" 542 | } 543 | }, 544 | "inherits": { 545 | "version": "2.0.4", 546 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 547 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 548 | }, 549 | "ip": { 550 | "version": "2.0.0", 551 | "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz", 552 | "integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==" 553 | }, 554 | "ipaddr.js": { 555 | "version": "1.9.1", 556 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", 557 | "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" 558 | }, 559 | "is-binary-path": { 560 | "version": "2.1.0", 561 | "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", 562 | "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", 563 | "dev": true, 564 | "requires": { 565 | "binary-extensions": "^2.0.0" 566 | } 567 | }, 568 | "is-extglob": { 569 | "version": "2.1.1", 570 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 571 | "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", 572 | "dev": true 573 | }, 574 | "is-glob": { 575 | "version": "4.0.3", 576 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", 577 | "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", 578 | "dev": true, 579 | "requires": { 580 | "is-extglob": "^2.1.1" 581 | } 582 | }, 583 | "is-number": { 584 | "version": "7.0.0", 585 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", 586 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", 587 | "dev": true 588 | }, 589 | "joi": { 590 | "version": "17.6.0", 591 | "resolved": "https://registry.npmjs.org/joi/-/joi-17.6.0.tgz", 592 | "integrity": "sha512-OX5dG6DTbcr/kbMFj0KGYxuew69HPcAE3K/sZpEV2nP6e/j/C0HV+HNiBPCASxdx5T7DMoa0s8UeHWMnb6n2zw==", 593 | "requires": { 594 | "@hapi/hoek": "^9.0.0", 595 | "@hapi/topo": "^5.0.0", 596 | "@sideway/address": "^4.1.3", 597 | "@sideway/formula": "^3.0.0", 598 | "@sideway/pinpoint": "^2.0.0" 599 | } 600 | }, 601 | "jsonwebtoken": { 602 | "version": "8.5.1", 603 | "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", 604 | "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==", 605 | "requires": { 606 | "jws": "^3.2.2", 607 | "lodash.includes": "^4.3.0", 608 | "lodash.isboolean": "^3.0.3", 609 | "lodash.isinteger": "^4.0.4", 610 | "lodash.isnumber": "^3.0.3", 611 | "lodash.isplainobject": "^4.0.6", 612 | "lodash.isstring": "^4.0.1", 613 | "lodash.once": "^4.0.0", 614 | "ms": "^2.1.1", 615 | "semver": "^5.6.0" 616 | }, 617 | "dependencies": { 618 | "ms": { 619 | "version": "2.1.3", 620 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 621 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" 622 | } 623 | } 624 | }, 625 | "jwa": { 626 | "version": "1.4.1", 627 | "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", 628 | "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", 629 | "requires": { 630 | "buffer-equal-constant-time": "1.0.1", 631 | "ecdsa-sig-formatter": "1.0.11", 632 | "safe-buffer": "^5.0.1" 633 | } 634 | }, 635 | "jws": { 636 | "version": "3.2.2", 637 | "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", 638 | "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", 639 | "requires": { 640 | "jwa": "^1.4.1", 641 | "safe-buffer": "^5.0.1" 642 | } 643 | }, 644 | "kareem": { 645 | "version": "2.4.1", 646 | "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.4.1.tgz", 647 | "integrity": "sha512-aJ9opVoXroQUPfovYP5kaj2lM7Jn02Gw13bL0lg9v0V7SaUc0qavPs0Eue7d2DcC3NjqI6QAUElXNsuZSeM+EA==" 648 | }, 649 | "lodash.includes": { 650 | "version": "4.3.0", 651 | "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", 652 | "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" 653 | }, 654 | "lodash.isboolean": { 655 | "version": "3.0.3", 656 | "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", 657 | "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" 658 | }, 659 | "lodash.isinteger": { 660 | "version": "4.0.4", 661 | "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", 662 | "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" 663 | }, 664 | "lodash.isnumber": { 665 | "version": "3.0.3", 666 | "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", 667 | "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" 668 | }, 669 | "lodash.isplainobject": { 670 | "version": "4.0.6", 671 | "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", 672 | "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" 673 | }, 674 | "lodash.isstring": { 675 | "version": "4.0.1", 676 | "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", 677 | "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" 678 | }, 679 | "lodash.once": { 680 | "version": "4.1.1", 681 | "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", 682 | "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" 683 | }, 684 | "media-typer": { 685 | "version": "0.3.0", 686 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", 687 | "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==" 688 | }, 689 | "memory-pager": { 690 | "version": "1.5.0", 691 | "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", 692 | "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", 693 | "optional": true 694 | }, 695 | "merge-descriptors": { 696 | "version": "1.0.1", 697 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", 698 | "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" 699 | }, 700 | "methods": { 701 | "version": "1.1.2", 702 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", 703 | "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==" 704 | }, 705 | "mime": { 706 | "version": "1.6.0", 707 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", 708 | "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" 709 | }, 710 | "mime-db": { 711 | "version": "1.52.0", 712 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", 713 | "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" 714 | }, 715 | "mime-types": { 716 | "version": "2.1.35", 717 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", 718 | "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", 719 | "requires": { 720 | "mime-db": "1.52.0" 721 | } 722 | }, 723 | "minimatch": { 724 | "version": "3.1.2", 725 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", 726 | "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", 727 | "requires": { 728 | "brace-expansion": "^1.1.7" 729 | } 730 | }, 731 | "mongodb": { 732 | "version": "4.7.0", 733 | "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-4.7.0.tgz", 734 | "integrity": "sha512-HhVar6hsUeMAVlIbwQwWtV36iyjKd9qdhY+s4wcU8K6TOj4Q331iiMy+FoPuxEntDIijTYWivwFJkLv8q/ZgvA==", 735 | "requires": { 736 | "bson": "^4.6.3", 737 | "denque": "^2.0.1", 738 | "mongodb-connection-string-url": "^2.5.2", 739 | "saslprep": "^1.0.3", 740 | "socks": "^2.6.2" 741 | } 742 | }, 743 | "mongodb-connection-string-url": { 744 | "version": "2.5.2", 745 | "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-2.5.2.tgz", 746 | "integrity": "sha512-tWDyIG8cQlI5k3skB6ywaEA5F9f5OntrKKsT/Lteub2zgwSUlhqEN2inGgBTm8bpYJf8QYBdA/5naz65XDpczA==", 747 | "requires": { 748 | "@types/whatwg-url": "^8.2.1", 749 | "whatwg-url": "^11.0.0" 750 | } 751 | }, 752 | "mongoose": { 753 | "version": "6.4.6", 754 | "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-6.4.6.tgz", 755 | "integrity": "sha512-ZAfNCWgXhwev6k/+rMzjHIJ/+wLkundQU/i+aUTqmCgGoYqc+B5e4EC4Y3E1XaLzNXiWID1/vocSM6zs5IAGvA==", 756 | "requires": { 757 | "bson": "^4.6.2", 758 | "kareem": "2.4.1", 759 | "mongodb": "4.7.0", 760 | "mpath": "0.9.0", 761 | "mquery": "4.0.3", 762 | "ms": "2.1.3", 763 | "sift": "16.0.0" 764 | }, 765 | "dependencies": { 766 | "ms": { 767 | "version": "2.1.3", 768 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 769 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" 770 | } 771 | } 772 | }, 773 | "morgan": { 774 | "version": "1.10.0", 775 | "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz", 776 | "integrity": "sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==", 777 | "requires": { 778 | "basic-auth": "~2.0.1", 779 | "debug": "2.6.9", 780 | "depd": "~2.0.0", 781 | "on-finished": "~2.3.0", 782 | "on-headers": "~1.0.2" 783 | }, 784 | "dependencies": { 785 | "on-finished": { 786 | "version": "2.3.0", 787 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", 788 | "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", 789 | "requires": { 790 | "ee-first": "1.1.1" 791 | } 792 | } 793 | } 794 | }, 795 | "mpath": { 796 | "version": "0.9.0", 797 | "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.9.0.tgz", 798 | "integrity": "sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew==" 799 | }, 800 | "mquery": { 801 | "version": "4.0.3", 802 | "resolved": "https://registry.npmjs.org/mquery/-/mquery-4.0.3.tgz", 803 | "integrity": "sha512-J5heI+P08I6VJ2Ky3+33IpCdAvlYGTSUjwTPxkAr8i8EoduPMBX2OY/wa3IKZIQl7MU4SbFk8ndgSKyB/cl1zA==", 804 | "requires": { 805 | "debug": "4.x" 806 | }, 807 | "dependencies": { 808 | "debug": { 809 | "version": "4.3.4", 810 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", 811 | "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", 812 | "requires": { 813 | "ms": "2.1.2" 814 | } 815 | }, 816 | "ms": { 817 | "version": "2.1.2", 818 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 819 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 820 | } 821 | } 822 | }, 823 | "ms": { 824 | "version": "2.0.0", 825 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 826 | "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" 827 | }, 828 | "negotiator": { 829 | "version": "0.6.3", 830 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", 831 | "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==" 832 | }, 833 | "nodemon": { 834 | "version": "2.0.19", 835 | "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.19.tgz", 836 | "integrity": "sha512-4pv1f2bMDj0Eeg/MhGqxrtveeQ5/G/UVe9iO6uTZzjnRluSA4PVWf8CW99LUPwGB3eNIA7zUFoP77YuI7hOc0A==", 837 | "dev": true, 838 | "requires": { 839 | "chokidar": "^3.5.2", 840 | "debug": "^3.2.7", 841 | "ignore-by-default": "^1.0.1", 842 | "minimatch": "^3.0.4", 843 | "pstree.remy": "^1.1.8", 844 | "semver": "^5.7.1", 845 | "simple-update-notifier": "^1.0.7", 846 | "supports-color": "^5.5.0", 847 | "touch": "^3.1.0", 848 | "undefsafe": "^2.0.5" 849 | }, 850 | "dependencies": { 851 | "debug": { 852 | "version": "3.2.7", 853 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", 854 | "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", 855 | "dev": true, 856 | "requires": { 857 | "ms": "^2.1.1" 858 | } 859 | }, 860 | "ms": { 861 | "version": "2.1.3", 862 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 863 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", 864 | "dev": true 865 | } 866 | } 867 | }, 868 | "nopt": { 869 | "version": "1.0.10", 870 | "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", 871 | "integrity": "sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==", 872 | "dev": true, 873 | "requires": { 874 | "abbrev": "1" 875 | } 876 | }, 877 | "normalize-path": { 878 | "version": "3.0.0", 879 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", 880 | "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", 881 | "dev": true 882 | }, 883 | "object-assign": { 884 | "version": "4.1.1", 885 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 886 | "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==" 887 | }, 888 | "object-inspect": { 889 | "version": "1.12.2", 890 | "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", 891 | "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==" 892 | }, 893 | "on-finished": { 894 | "version": "2.4.1", 895 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", 896 | "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", 897 | "requires": { 898 | "ee-first": "1.1.1" 899 | } 900 | }, 901 | "on-headers": { 902 | "version": "1.0.2", 903 | "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", 904 | "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==" 905 | }, 906 | "once": { 907 | "version": "1.4.0", 908 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 909 | "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", 910 | "requires": { 911 | "wrappy": "1" 912 | } 913 | }, 914 | "parseurl": { 915 | "version": "1.3.3", 916 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", 917 | "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" 918 | }, 919 | "path-is-absolute": { 920 | "version": "1.0.1", 921 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 922 | "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==" 923 | }, 924 | "path-to-regexp": { 925 | "version": "0.1.7", 926 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", 927 | "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" 928 | }, 929 | "picomatch": { 930 | "version": "2.3.1", 931 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", 932 | "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", 933 | "dev": true 934 | }, 935 | "proxy-addr": { 936 | "version": "2.0.7", 937 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", 938 | "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", 939 | "requires": { 940 | "forwarded": "0.2.0", 941 | "ipaddr.js": "1.9.1" 942 | } 943 | }, 944 | "pstree.remy": { 945 | "version": "1.1.8", 946 | "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", 947 | "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", 948 | "dev": true 949 | }, 950 | "punycode": { 951 | "version": "2.1.1", 952 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", 953 | "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" 954 | }, 955 | "qs": { 956 | "version": "6.10.3", 957 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", 958 | "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", 959 | "requires": { 960 | "side-channel": "^1.0.4" 961 | } 962 | }, 963 | "range-parser": { 964 | "version": "1.2.1", 965 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", 966 | "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" 967 | }, 968 | "rate-limiter": { 969 | "version": "0.2.0", 970 | "resolved": "https://registry.npmjs.org/rate-limiter/-/rate-limiter-0.2.0.tgz", 971 | "integrity": "sha512-c2znoXGtCzCB7BCIkhWu5zPCnYQXv3C3I7ZxNNzp/tjraNgkRQpa9jzb/DvignbE17MdkfOlVu2gCc7o52gWgg==", 972 | "requires": { 973 | "sprintf": ">= 0.1.1" 974 | } 975 | }, 976 | "raw-body": { 977 | "version": "2.5.1", 978 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", 979 | "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", 980 | "requires": { 981 | "bytes": "3.1.2", 982 | "http-errors": "2.0.0", 983 | "iconv-lite": "0.4.24", 984 | "unpipe": "1.0.0" 985 | } 986 | }, 987 | "readdirp": { 988 | "version": "3.6.0", 989 | "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", 990 | "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", 991 | "dev": true, 992 | "requires": { 993 | "picomatch": "^2.2.1" 994 | } 995 | }, 996 | "safe-buffer": { 997 | "version": "5.2.1", 998 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 999 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" 1000 | }, 1001 | "safer-buffer": { 1002 | "version": "2.1.2", 1003 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 1004 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 1005 | }, 1006 | "saslprep": { 1007 | "version": "1.0.3", 1008 | "resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.3.tgz", 1009 | "integrity": "sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag==", 1010 | "optional": true, 1011 | "requires": { 1012 | "sparse-bitfield": "^3.0.3" 1013 | } 1014 | }, 1015 | "semver": { 1016 | "version": "5.7.1", 1017 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", 1018 | "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" 1019 | }, 1020 | "send": { 1021 | "version": "0.18.0", 1022 | "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", 1023 | "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", 1024 | "requires": { 1025 | "debug": "2.6.9", 1026 | "depd": "2.0.0", 1027 | "destroy": "1.2.0", 1028 | "encodeurl": "~1.0.2", 1029 | "escape-html": "~1.0.3", 1030 | "etag": "~1.8.1", 1031 | "fresh": "0.5.2", 1032 | "http-errors": "2.0.0", 1033 | "mime": "1.6.0", 1034 | "ms": "2.1.3", 1035 | "on-finished": "2.4.1", 1036 | "range-parser": "~1.2.1", 1037 | "statuses": "2.0.1" 1038 | }, 1039 | "dependencies": { 1040 | "ms": { 1041 | "version": "2.1.3", 1042 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 1043 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" 1044 | } 1045 | } 1046 | }, 1047 | "serve-static": { 1048 | "version": "1.15.0", 1049 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", 1050 | "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", 1051 | "requires": { 1052 | "encodeurl": "~1.0.2", 1053 | "escape-html": "~1.0.3", 1054 | "parseurl": "~1.3.3", 1055 | "send": "0.18.0" 1056 | } 1057 | }, 1058 | "setprototypeof": { 1059 | "version": "1.2.0", 1060 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", 1061 | "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" 1062 | }, 1063 | "side-channel": { 1064 | "version": "1.0.4", 1065 | "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", 1066 | "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", 1067 | "requires": { 1068 | "call-bind": "^1.0.0", 1069 | "get-intrinsic": "^1.0.2", 1070 | "object-inspect": "^1.9.0" 1071 | } 1072 | }, 1073 | "sift": { 1074 | "version": "16.0.0", 1075 | "resolved": "https://registry.npmjs.org/sift/-/sift-16.0.0.tgz", 1076 | "integrity": "sha512-ILTjdP2Mv9V1kIxWMXeMTIRbOBrqKc4JAXmFMnFq3fKeyQ2Qwa3Dw1ubcye3vR+Y6ofA0b9gNDr/y2t6eUeIzQ==" 1077 | }, 1078 | "simple-update-notifier": { 1079 | "version": "1.0.7", 1080 | "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-1.0.7.tgz", 1081 | "integrity": "sha512-BBKgR84BJQJm6WjWFMHgLVuo61FBDSj1z/xSFUIozqO6wO7ii0JxCqlIud7Enr/+LhlbNI0whErq96P2qHNWew==", 1082 | "dev": true, 1083 | "requires": { 1084 | "semver": "~7.0.0" 1085 | }, 1086 | "dependencies": { 1087 | "semver": { 1088 | "version": "7.0.0", 1089 | "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", 1090 | "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", 1091 | "dev": true 1092 | } 1093 | } 1094 | }, 1095 | "smart-buffer": { 1096 | "version": "4.2.0", 1097 | "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", 1098 | "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==" 1099 | }, 1100 | "socks": { 1101 | "version": "2.7.0", 1102 | "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.0.tgz", 1103 | "integrity": "sha512-scnOe9y4VuiNUULJN72GrM26BNOjVsfPXI+j+98PkyEfsIXroa5ofyjT+FzGvn/xHs73U2JtoBYAVx9Hl4quSA==", 1104 | "requires": { 1105 | "ip": "^2.0.0", 1106 | "smart-buffer": "^4.2.0" 1107 | } 1108 | }, 1109 | "sparse-bitfield": { 1110 | "version": "3.0.3", 1111 | "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", 1112 | "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", 1113 | "optional": true, 1114 | "requires": { 1115 | "memory-pager": "^1.0.2" 1116 | } 1117 | }, 1118 | "sprintf": { 1119 | "version": "0.1.5", 1120 | "resolved": "https://registry.npmjs.org/sprintf/-/sprintf-0.1.5.tgz", 1121 | "integrity": "sha512-4X5KsuXFQ7f+d7Y+bi4qSb6eI+YoifDTGr0MQJXRoYO7BO7evfRCjds6kk3z7l5CiJYxgDN1x5Er4WiyCt+zTQ==" 1122 | }, 1123 | "sprintf-js": { 1124 | "version": "1.0.3", 1125 | "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", 1126 | "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" 1127 | }, 1128 | "statuses": { 1129 | "version": "2.0.1", 1130 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", 1131 | "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" 1132 | }, 1133 | "streamsearch": { 1134 | "version": "1.1.0", 1135 | "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", 1136 | "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==" 1137 | }, 1138 | "supports-color": { 1139 | "version": "5.5.0", 1140 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", 1141 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", 1142 | "dev": true, 1143 | "requires": { 1144 | "has-flag": "^3.0.0" 1145 | } 1146 | }, 1147 | "to-regex-range": { 1148 | "version": "5.0.1", 1149 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", 1150 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", 1151 | "dev": true, 1152 | "requires": { 1153 | "is-number": "^7.0.0" 1154 | } 1155 | }, 1156 | "toidentifier": { 1157 | "version": "1.0.1", 1158 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", 1159 | "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" 1160 | }, 1161 | "touch": { 1162 | "version": "3.1.0", 1163 | "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", 1164 | "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==", 1165 | "dev": true, 1166 | "requires": { 1167 | "nopt": "~1.0.10" 1168 | } 1169 | }, 1170 | "tr46": { 1171 | "version": "3.0.0", 1172 | "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", 1173 | "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", 1174 | "requires": { 1175 | "punycode": "^2.1.1" 1176 | } 1177 | }, 1178 | "type-is": { 1179 | "version": "1.6.18", 1180 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", 1181 | "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", 1182 | "requires": { 1183 | "media-typer": "0.3.0", 1184 | "mime-types": "~2.1.24" 1185 | } 1186 | }, 1187 | "undefsafe": { 1188 | "version": "2.0.5", 1189 | "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", 1190 | "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", 1191 | "dev": true 1192 | }, 1193 | "unpipe": { 1194 | "version": "1.0.0", 1195 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 1196 | "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==" 1197 | }, 1198 | "utils-merge": { 1199 | "version": "1.0.1", 1200 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", 1201 | "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==" 1202 | }, 1203 | "validator": { 1204 | "version": "13.7.0", 1205 | "resolved": "https://registry.npmjs.org/validator/-/validator-13.7.0.tgz", 1206 | "integrity": "sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw==" 1207 | }, 1208 | "vary": { 1209 | "version": "1.1.2", 1210 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", 1211 | "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==" 1212 | }, 1213 | "webidl-conversions": { 1214 | "version": "7.0.0", 1215 | "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", 1216 | "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==" 1217 | }, 1218 | "whatwg-url": { 1219 | "version": "11.0.0", 1220 | "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", 1221 | "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", 1222 | "requires": { 1223 | "tr46": "^3.0.0", 1224 | "webidl-conversions": "^7.0.0" 1225 | } 1226 | }, 1227 | "wrappy": { 1228 | "version": "1.0.2", 1229 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 1230 | "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" 1231 | }, 1232 | "xss-clean": { 1233 | "version": "0.1.1", 1234 | "resolved": "https://registry.npmjs.org/xss-clean/-/xss-clean-0.1.1.tgz", 1235 | "integrity": "sha512-On99yydxoAEkHpraR7Wjg9cD6UmKfbtH2HXO1it2djid32osHL7Qr8bIu+dGYoOeKzf3ZszLfz1gwR/D7whZ2A==", 1236 | "requires": { 1237 | "xss-filters": "1.2.6" 1238 | } 1239 | }, 1240 | "xss-filters": { 1241 | "version": "1.2.6", 1242 | "resolved": "https://registry.npmjs.org/xss-filters/-/xss-filters-1.2.6.tgz", 1243 | "integrity": "sha512-uqgwZRpVJCDfHsRX9lDrkPyCitQYzPklmLSbajJncATZKAUd1tF1x9y2VyPNFMv8SsSWed80xorSS5qGpw3WiA==" 1244 | }, 1245 | "yamljs": { 1246 | "version": "0.3.0", 1247 | "resolved": "https://registry.npmjs.org/yamljs/-/yamljs-0.3.0.tgz", 1248 | "integrity": "sha512-C/FsVVhht4iPQYXOInoxUM/1ELSf9EsgKH34FofQOp6hwCPrW4vG4w5++TED3xRUo8gD7l0P1J1dLlDYzODsTQ==", 1249 | "requires": { 1250 | "argparse": "^1.0.7", 1251 | "glob": "^7.0.5" 1252 | } 1253 | } 1254 | } 1255 | } 1256 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "e-commerce-api-1", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "server.js", 6 | "scripts": { 7 | "start": "nodemon server.js" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "bcryptjs": "^2.4.3", 13 | "cookie-parser": "^1.4.6", 14 | "cors": "^2.8.5", 15 | "dotenv": "^16.0.1", 16 | "express": "^4.18.1", 17 | "express-async-errors": "^3.1.1", 18 | "express-fileupload": "^1.4.0", 19 | "express-rate-limit": "^6.4.0", 20 | "helmet": "^5.1.0", 21 | "http-status-codes": "^2.2.0", 22 | "joi": "^17.6.0", 23 | "jsonwebtoken": "^8.5.1", 24 | "mongoose": "^6.4.6", 25 | "morgan": "^1.10.0", 26 | "rate-limiter": "^0.2.0", 27 | "validator": "^13.7.0", 28 | "xss-clean": "^0.1.1", 29 | "yamljs": "^0.3.0" 30 | }, 31 | "devDependencies": { 32 | "nodemon": "^2.0.19" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /public/uploads/5103Dl2NOvL._SL1500_.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/themodernmonk7/E-commerce-API/251c1bd9865470dc0e71f3e9d8dfea2b8cf81e69/public/uploads/5103Dl2NOvL._SL1500_.jpg -------------------------------------------------------------------------------- /public/uploads/dd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/themodernmonk7/E-commerce-API/251c1bd9865470dc0e71f3e9d8dfea2b8cf81e69/public/uploads/dd.png -------------------------------------------------------------------------------- /public/uploads/example.jpeg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/themodernmonk7/E-commerce-API/251c1bd9865470dc0e71f3e9d8dfea2b8cf81e69/public/uploads/example.jpeg.png -------------------------------------------------------------------------------- /routes/authRoutes.js: -------------------------------------------------------------------------------- 1 | const express = require("express") 2 | const router = express.Router() 3 | 4 | const { register, login, logout } = require("../controllers/authController") 5 | 6 | router.route("/register").post(register) 7 | router.route("/login").post(login) 8 | router.route("/logout").get(logout) 9 | 10 | module.exports = router 11 | -------------------------------------------------------------------------------- /routes/orderRoutes.js: -------------------------------------------------------------------------------- 1 | const express = require("express") 2 | const router = express.Router() 3 | 4 | const { 5 | authenticateUser, 6 | authorizePermissions, 7 | } = require("../middleware/authentication") 8 | 9 | const { 10 | getAllOrders, 11 | getSingleOrder, 12 | getCurrentUserOrder, 13 | createOrder, 14 | updateOrder, 15 | } = require("../controllers/orderController") 16 | 17 | router 18 | .route("/") 19 | .post(authenticateUser, createOrder) 20 | .get(authenticateUser, authorizePermissions("admin"), getAllOrders) 21 | 22 | router.route("/showAllMyOrders").get(authenticateUser, getCurrentUserOrder) 23 | 24 | router 25 | .route("/:id") 26 | .get(authenticateUser, getSingleOrder) 27 | .patch(authenticateUser, updateOrder) 28 | 29 | module.exports = router 30 | -------------------------------------------------------------------------------- /routes/productRoutes.js: -------------------------------------------------------------------------------- 1 | const express = require("express") 2 | const router = express.Router() 3 | 4 | const { 5 | createProduct, 6 | getAllProducts, 7 | getSingleProduct, 8 | updateProduct, 9 | deleteProduct, 10 | uploadImage, 11 | } = require("../controllers/productController") 12 | const { 13 | authenticateUser, 14 | authorizePermissions, 15 | } = require("../middleware/authentication") 16 | 17 | const { getSingleProductReviews } = require("../controllers/reviewController") 18 | 19 | router 20 | .route("/") 21 | .post([authenticateUser, authorizePermissions("admin")], createProduct) 22 | .get(getAllProducts) 23 | 24 | router 25 | .route("/uploadImage") 26 | .post([authenticateUser, authorizePermissions("admin")], uploadImage) 27 | 28 | router 29 | .route("/:id") 30 | .get(getSingleProduct) 31 | .patch([authenticateUser, authorizePermissions("admin")], updateProduct) 32 | .delete([authenticateUser, authorizePermissions("admin")], deleteProduct) 33 | 34 | router.route("/:id/reviews").get(getSingleProductReviews) 35 | 36 | module.exports = router 37 | -------------------------------------------------------------------------------- /routes/reviewRoutes.js: -------------------------------------------------------------------------------- 1 | const express = require("express") 2 | const router = express.Router() 3 | 4 | const { 5 | createReview, 6 | getAllReviews, 7 | getSingleReview, 8 | updateReview, 9 | deleteReview, 10 | } = require("../controllers/reviewController") 11 | 12 | const { authenticateUser } = require("../middleware/authentication") 13 | 14 | router.route("/").post(authenticateUser, createReview).get(getAllReviews) 15 | 16 | router 17 | .route("/:id") 18 | .get(getSingleReview) 19 | .patch(authenticateUser, updateReview) 20 | .delete(authenticateUser, deleteReview) 21 | 22 | module.exports = router 23 | -------------------------------------------------------------------------------- /routes/userRoutes.js: -------------------------------------------------------------------------------- 1 | const express = require("express") 2 | const router = express.Router() 3 | const { 4 | authenticateUser, 5 | authorizePermissions, 6 | } = require("../middleware/authentication") 7 | 8 | const { 9 | getAllUsers, 10 | getSingleUser, 11 | showCurrentUser, 12 | updateUser, 13 | updateUserPassword, 14 | } = require("../controllers/userController") 15 | router 16 | .route("/") 17 | .get(authenticateUser, authorizePermissions("admin", "owner"), getAllUsers) 18 | router.route("/showMe").get(authenticateUser, showCurrentUser) 19 | router.route("/updateUser").patch(authenticateUser, updateUser) 20 | router.route("/updateUserPassword").patch(authenticateUser, updateUserPassword) 21 | router.route("/:id").get(authenticateUser, getSingleUser) 22 | 23 | module.exports = router 24 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | require("dotenv").config() 2 | require("express-async-errors") 3 | 4 | const express = require("express") 5 | const app = express() 6 | 7 | // Rest of the packages 8 | const morgan = require("morgan") //HTTP request logger middleware 9 | const cookieParser = require("cookie-parser") 10 | const fileUpload = require("express-fileupload") 11 | // Require Database 12 | const connectDB = require("./db/connect") 13 | // Require Routers 14 | const authRouter = require("./routes/authRoutes") 15 | const userRouter = require("./routes/userRoutes") 16 | const productRouter = require("./routes/productRoutes") 17 | const reviewRouter = require("./routes/reviewRoutes") 18 | const orderRouter = require("./routes/orderRoutes") 19 | // Require Middleware 20 | const notFoundMiddleware = require("./middleware/not-found") 21 | const errorHandlerMiddleware = require("./middleware/error-handler") 22 | 23 | // Invoke Extra packages 24 | app.use(morgan("tiny")) 25 | app.use(express.json()) 26 | app.use(cookieParser(process.env.JWT_SECRET)) 27 | app.use(express.static("./public")) 28 | app.use(fileUpload()) 29 | 30 | // Home get 31 | app.get("/", (req, res) => { 32 | res.send("