diff --git a/Client/Burger Bites/.eslintrc.cjs b/Client/Burger Bites/.eslintrc.cjs new file mode 100644 index 0000000..3e212e1 --- /dev/null +++ b/Client/Burger Bites/.eslintrc.cjs @@ -0,0 +1,21 @@ +module.exports = { + root: true, + env: { browser: true, es2020: true }, + extends: [ + 'eslint:recommended', + 'plugin:react/recommended', + 'plugin:react/jsx-runtime', + 'plugin:react-hooks/recommended', + ], + ignorePatterns: ['dist', '.eslintrc.cjs'], + parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, + settings: { react: { version: '18.2' } }, + plugins: ['react-refresh'], + rules: { + 'react/jsx-no-target-blank': 'off', + 'react-refresh/only-export-components': [ + 'warn', + { allowConstantExport: true }, + ], + }, +} diff --git a/Client/Burger Bites/.gitignore b/Client/Burger Bites/.gitignore new file mode 100644 index 0000000..a547bf3 --- /dev/null +++ b/Client/Burger Bites/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/Client/Burger Bites/README.md b/Client/Burger Bites/README.md new file mode 100644 index 0000000..f768e33 --- /dev/null +++ b/Client/Burger Bites/README.md @@ -0,0 +1,8 @@ +# React + Vite + +This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. + +Currently, two official plugins are available: + +- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh +- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh diff --git a/Client/Burger Bites/index.html b/Client/Burger Bites/index.html new file mode 100644 index 0000000..eabf574 --- /dev/null +++ b/Client/Burger Bites/index.html @@ -0,0 +1,37 @@ + + + +
+ + + + ++ Lorem ipsum dolor sit amet consectetur adipisicing elit. Saepe sed + repellat, cum, enim doloribus fugit incidunt aut sunt, autem doloremque + quae laboriosam velit maiores? Voluptatum incidunt animi illo! + Reprehenderit quis minus adipisci accusantium atque omnis assumenda + aperiam architecto, nulla qui ad doloribus harum doloremque ducimus + iusto alias exercitationem corporis mollitia! +
++ {userData.email} +
++ {userData.role} +
++ Updated at: {formatCreatedAt(userData.updatedAt)} +
+ + + ++ {burgerDetails.desc} +
++ {burgerDetails.veg ? "Veg" : "Non-Veg"} +
+ + + + + +| ID | +Name | ++ Description + | ++ Price + | +Veg | +Image | +Created At | +Updated At | ++ Actions + | +
|---|---|---|---|---|---|---|---|---|
| {index + 1} | +{burger.name} | ++ {burger.desc} + | +{formatCurrency(burger.price)} | +{burger.veg.toString()} | +
+ |
+
+
+ {formatCreatedAt(burger.createdAt)} | +{formatCreatedAt(burger.updatedAt)} | ++ + { + e.preventDefault(); + deleteBurger(burger.id)}}> + + delete + + + + + + edit + + + + + + + image + + + + + | +
+ {desc} +
++ {veg ? "Veg" : "Non-Veg"} +
+| Name | ++ Description + | ++ Price + | +Burger Type | +Image | +Quantity | + +Updated At | ++ Actions + | +
|---|---|---|---|---|---|---|---|
| {burger.name} | ++ {burger.desc} + | +{formatCurrency(burger.price)} | +{burger.veg ? "Veg" : "Non-Veg"} | +
+ |
+ + {cart.quantity} + | + +{formatCreatedAt(cart.updatedAt)} | + ++ + { + e.preventDefault(); + deleteBurger(cart.BurgerId) + }}> + + delete + + + + + | +
Sorry, an Unexpected error has occured.
++ {error.statusText || error.message} +
++ Lorem ipsum dolor sit, amet consectetur adipisicing elit. Quisquam + aliquid facere quod nulla aut rerum earum magnam minima laudantium + magni. +
+ ++ Lorem ipsum, dolor sit amet consectetur adipisicing elit. Quod ipsam + facilis voluptatem doloribus eligendi natus maxime eum aliquid + dignissimos dicta? +
++ Lorem ipsum, dolor sit amet consectetur adipisicing elit. Quod ipsam + facilis voluptatem doloribus eligendi natus maxime eum aliquid + dignissimos dicta? +
++ Lorem ipsum, dolor sit amet consectetur adipisicing elit. Quod ipsam + facilis voluptatem doloribus eligendi natus maxime eum aliquid + dignissimos dicta? +
++ Become a member +
+ + Join our community by signing up today and enhance your experience with us! + +404: Page Not Found
+
+ + Lorem ipsum, dolor sit amet consectetur adipisicing elit. Quod ipsam + facilis voluptatem doloribus eligendi natus maxime eum aliquid + dignissimos dicta? +
++ {userData.email} +
++ {userData.role} +
++ Updated at: {formatCreatedAt(userData.updatedAt)} +
+ + + ++ Become a member +
+ + Join our community by signing up today and enhance your experience with us! + ++ Lorem ipsum dolor sit amet, consectetur adipisicing elit. Omnis + repudiandae dolorum ab beatae perferendis asperiores rem corrupti + blanditiis commodi et? +
++ Lorem ipsum dolor sit amet, consectetur adipisicing elit. Omnis + repudiandae dolorum ab beatae perferendis asperiores rem corrupti + blanditiis commodi et? +
++ Lorem ipsum dolor sit amet, consectetur adipisicing elit. Omnis + repudiandae dolorum ab beatae perferendis asperiores rem corrupti + blanditiis commodi et? +
+Burger Name: {burger.name}
+Current Burger Image:
+ {desc} +
++ {veg ? "Veg" : "Non-Veg"} +
++ Press n or j to go to the next uncovered block, b, p or k for the previous block. +
+ +| 1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 | 11x +11x + + +11x +11x +11x + +11x +11x +11x +11x +11x + +11x +11x +11x + +11x +11x +11x + +11x + + + +11x +11x + +11x + +11x + + + +11x +11x +11x +11x +11x +11x +11x +11x +11x +11x +11x +11x + +11x + +11x | Eif(process.env.NODE_ENV !== "production") { + require('dotenv').config(); +} + +const express = require("express"); +const app = express(); +const port = 3000; + +const UserController = require("./controllers/UserController"); +const BurgerController = require("./controllers/BurgerController"); +const authentication = require("./middlewares/authentication"); +const authorization = require("./middlewares/authorization"); +const errorHandler = require("./middlewares/errorHandler"); + +const multer = require("multer"); +const upload = multer({ storage: multer.memoryStorage() }); +const cors = require("cors"); + +app.use(express.urlencoded({extended: true})); +app.use(express.json()); +app.use(cors()); + +app.get("/", (req, res) => { + res.json({message: "Welcome to the Burger Bites API"}) +}) + +app.post("/register", UserController.registerUser); +app.post("/login", UserController.loginUser); + +app.get("/burgers", BurgerController.getBurgers); + +app.post("/google-login", BurgerController.googleLogin); + +// app.use(authentication); + +app.get("/users", authentication, BurgerController.getUser); +app.patch("/users/profile", authentication, upload.single("image"), BurgerController.updateUserImage); +app.get("/cart", authentication, BurgerController.getMyBurger); +app.patch("/cart/purchase", authentication, BurgerController.purchaseBurger); +app.post("/cart/generateMidTransToken", authentication, BurgerController.generateMidTransToken); +app.post("/burgers", authentication, BurgerController.addBurger); +app.get("/burgers/:burgerId", BurgerController.getBurgerById); +app.put("/burgers/:burgerId", authentication, authorization, BurgerController.updateBurgerById); +app.delete("/burgers/:burgerId", authentication, authorization, BurgerController.deleteBurgerById); +app.patch("/burgers/:burgerId/image", authentication, authorization, upload.single("image"), BurgerController.updateImageById); +app.post("/cart/:burgerId", authentication, BurgerController.addToCart); +app.delete("/cart/:burgerId", authentication, BurgerController.removeFromCart); + +app.use(errorHandler); + +module.exports = app; |
+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +
+ +| 1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290 +291 +292 +293 +294 +295 +296 +297 +298 +299 +300 +301 +302 +303 +304 +305 +306 +307 +308 +309 +310 +311 +312 +313 +314 +315 +316 +317 +318 +319 +320 +321 +322 +323 +324 +325 +326 +327 +328 +329 | 11x +11x +11x +11x +11x +11x +11x +11x + +11x + +2x +2x +2x +2x +1x + + + + + +2x + + + + + + +2x + + + + +2x +2x + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +1x +1x +1x + + +1x + + + + + + + + +2x +2x + + +2x +2x +1x + + +1x +1x + + + + +2x +2x +2x +2x + + +2x +2x +1x + + +1x +1x + + + + +1x +1x +1x +1x + + +1x +1x + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +1x +1x +1x +1x + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + | const { User, Burger, Cart } = require("../models");
+const { Op } = require("sequelize");
+const burgersHubEndPoint = "https://burgers-hub.p.rapidapi.com";
+const axios = require("axios");
+const { OAuth2Client } = require('google-auth-library');
+const client = new OAuth2Client();
+const { signToken } = require("../helpers/jwt");
+const midtransClient = require('midtrans-client');
+
+module.exports = class BurgerController {
+ static async getBurgers(req, res, next) {
+ try {
+ const { filter, search, sort, page } = req.query;
+ let option = {}
+ if (filter) {
+ option.where = {
+
+ id: filter
+
+ }
+ }
+ Iif (search) {
+ option.where = {
+ name: {
+ [Op.iLike]: `%${search}%`
+ }
+ }
+ }
+ Iif (sort) {
+ const orderby = sort[0] === "-" ? "desc" : "asc"
+ const columnName = orderby === "desc" ? sort.slice(1) : sort
+ option.order = [[columnName, orderby]]
+ }
+ const burgerData = await Burger.findAll(option);
+ res.status(200).json(burgerData)
+ } catch (error) {
+ console.log(error)
+ next(error);
+ }
+
+ }
+
+ static async getBurgerById(req, res, next) {
+
+ const { burgerId } = req.params;
+ try {
+ const burgerData = await Burger.findByPk(burgerId);
+ if (!burgerData) {
+ throw { name: "Not found" }
+ }
+ res.status(200).json(burgerData)
+ } catch (error) {
+ console.log(error)
+ next(error);
+ }
+ }
+
+ static async getBurgerByName(req, res, next) {
+
+ const { name } = req.query;
+
+ const options = {
+ method: 'GET',
+ url: `${burgersHubEndPoint}/find-burger/`,
+ params: {
+ searchQuery: "search",
+ search: name
+ },
+ headers: {
+ 'X-RapidAPI-Key': process.env.BURGERS_HUB_API_KEY,
+ 'X-RapidAPI-Host': 'burgers-hub.p.rapidapi.com'
+ }
+ };
+
+ try {
+ const response = await axios.request(options);
+ console.log(response.data);
+ res.status(200).json(response.data)
+ } catch (error) {
+ console.error(error);
+ next(error);
+ }
+ }
+
+ static async getMyBurger(req, res, next) {
+
+ try {
+ const myBurgerData = await Cart.findAll({ where: { UserId: req.user.id } });
+ Iif(!myBurgerData) {
+ throw {name: "Not found"}
+ }
+ return res.status(200).json(myBurgerData);
+
+ } catch (error) {
+ console.log(error);
+ next(error)
+ }
+ }
+
+ static async addBurger(req, res, next) {
+ try {
+ Iif(req.user.role !== "Admin") {
+ throw {name: "Unauthorized"}
+ }
+ const { name, desc, price, veg, images } = req.body;
+ const burgerData = await Burger.create({ name, desc, price, veg, images });
+ return res.status(201).json({ message: "Burger successfully added", burgerData });
+
+ } catch (error) {
+ console.log(error);
+ next(error)
+ }
+ }
+
+ static async updateBurgerById(req, res, next) {
+ try {
+ const { burgerId } = req.params;
+ const burgerData = await Burger.findByPk(burgerId);
+ Iif (!burgerData) {
+ throw { name: "Not found" }
+ }
+ const { name, desc, price, veg, images } = req.body;
+ await Burger.update({ name, desc, price, veg, images }, { where: { id: req.params.burgerId } });
+ return res.status(200).json({ message: "Burger has been updated" });
+
+ } catch (error) {
+ console.log(error);
+ next(error)
+ }
+ }
+
+ static async deleteBurgerById(req, res, next) {
+ try {
+ const { burgerId } = req.params;
+ const burgerData = await Burger.findByPk(burgerId);
+ Iif (!burgerData) {
+ throw { name: "Not found" }
+ }
+ await Burger.destroy({ where: { id: req.params.burgerId } });
+ return res.status(200).json({ message: "Burger has been deleted" });
+
+ } catch (error) {
+ console.log(error);
+ next(error)
+ }
+ }
+
+ static async updateImageById(req, res, next) {
+ try {
+
+ if (!req.file) throw { name: "CustomFileError" }
+
+ const cloudinary = require("cloudinary").v2;
+ cloudinary.config({
+ cloud_name: process.env.cloudinary_cloud_name,
+ api_key: process.env.cloudinary_api_key,
+ api_secret: process.env.cloudinary_api_secret
+ });
+ const dataURI = `data:${req.file.mimetype};base64,${Buffer.from(req.file.buffer).toString("base64")}`
+ const result = await cloudinary.uploader.upload(dataURI, { public_id: "test" })
+
+ const { burgerId } = req.params;
+ const burger = await Burger.findByPk(burgerId);
+
+ if (!burger) throw { name: "Not found" };
+
+ await burger.update({ images: result.secure_url });
+
+ res.json({ message: "Image successfully updated" })
+ } catch (error) {
+ next(error);
+ }
+ }
+
+ static async addToCart(req, res, next) {
+ try {
+ const { burgerId } = req.params;
+ const burgerData = await Burger.findByPk(burgerId);
+ if (!burgerData) {
+ throw { name: "Not found" }
+ }
+ const { quantity } = req.body;
+ await Cart.create({ quantity, BurgerId: burgerId, UserId: req.user.id });
+ return res.status(201).json({ message: "Burger successfully added to cart", burgerData });
+
+ } catch (error) {
+ console.log(error);
+ next(error)
+ }
+ }
+
+ static async removeFromCart(req, res, next) {
+ try {
+ const { burgerId } = req.params;
+ const burgerData = await Cart.findOne({where: { BurgerId: burgerId }});
+ if (!burgerData) {
+ throw { name: "Not found" }
+ }
+
+ await Cart.destroy({ where: { BurgerId: req.params.burgerId } });
+ return res.status(201).json({ message: "Burger successfully removed from cart" });
+
+ } catch (error) {
+ console.log(error);
+ next(error)
+ }
+ }
+
+ static async getUser(req, res, next) {
+
+ try {
+ const userData = await User.findByPk(req.user.id);
+ const { name, email, role, imageUrl, createdAt, updatedAt } = userData;
+ return res.status(200).json({ name, email, role, imageUrl, createdAt, updatedAt });
+
+ } catch (error) {
+ console.log(error);
+ next(error)
+ }
+ }
+
+ static async updateUserImage(req, res, next) {
+ try {
+
+ if (!req.file) throw { name: "CustomFileError" }
+
+ const cloudinary = require("cloudinary").v2;
+ cloudinary.config({
+ cloud_name: process.env.cloudinary_cloud_name,
+ api_key: process.env.cloudinary_api_key,
+ api_secret: process.env.cloudinary_api_secret
+ });
+ const dataURI = `data:${req.file.mimetype};base64,${Buffer.from(req.file.buffer).toString("base64")}`
+ const result = await cloudinary.uploader.upload(dataURI, { public_id: "test" })
+
+ const user = await User.findByPk(req.user.id);
+
+ if (!user) throw { name: "Not found" };
+
+ await user.update({ imageUrl: result.secure_url });
+
+ res.json({ message: "Image successfully updated" })
+ } catch (error) {
+ next(error);
+ }
+ }
+
+ static async googleLogin(req, res, next) {
+
+ const { googleToken } = req.body;
+
+ console.log(req.body)
+
+ try {
+ const ticket = await client.verifyIdToken({
+ idToken: googleToken,
+ audience: "483160331749-3i3b3l3nvvostt3ccublhnmi64dtmkob.apps.googleusercontent.com", // Specify the CLIENT_ID of the app that accesses the backend
+ // Or, if multiple clients access the backend:
+ //[CLIENT_ID_1, CLIENT_ID_2, CLIENT_ID_3]
+ });
+ const { email, name } = ticket.getPayload();
+ const [user, created] = await User.findOrCreate({
+ where: { email },
+ defaults: {
+ name,
+ email,
+ password: Math.random().toString()
+ }
+ });
+ console.log({ user, created })
+ const token = signToken({ id: user.id })
+ res.status(200).json({ message: `Login as ${user.email}`, token });
+
+ } catch (error) {
+ console.log(error);
+ next(error);
+ }
+ }
+
+ static async purchaseBurger(req, res, next) {
+ try {
+ await Cart.update({ purchased: true, purchasedAt: new Date() }, { where: { UserId: req.user.id } });
+ return res.status(200).json({ message: "Burger has been purchased" });
+
+ } catch (error) {
+ console.log(error);
+ next(error)
+ }
+ }
+
+ static async generateMidTransToken(req, res, next) {
+ try {
+ const userData = await User.findByPk(req.user.id);
+ const cartData = await Cart.findAll({ where: { UserId: userData.id } });
+ console.log(userData, cartData, "<<<<<")
+ if (cartData.purchased) {
+ throw { name: "CustomPurchaseError" } // error code: 400
+ }
+ let snap = new midtransClient.Snap({
+ // Set to true if you want Production Environment (accept real transaction).
+ isProduction: false,
+ serverKey: process.env.MIDTRANS_SERVER_KEY
+ });
+
+ let parameter = {
+ transaction_details: {
+ order_id: `TRANSACTION_${Math.floor(100000 + Math.random() * 900000)}`,
+ gross_amount: req.body.totalPrice
+ },
+ credit_card: {
+ secure: true
+ },
+ customer_details: {
+ name: userData.name,
+ email: userData.email
+ }
+ };
+
+ const midtransToken = await snap.createTransaction(parameter);
+ console.log(midtransToken, "<<<<")
+ res.status(201).json(midtransToken);
+
+
+ } catch (error) {
+ console.log(error);
+ next(error);
+ }
+ }
+} |
+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +
+ +| 1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 | 11x +11x +11x + +11x + +5x +5x +5x +1x + +4x +4x + + + + +5x +5x +5x +1x + +4x +1x + +3x +3x +2x + +1x +1x + +4x +4x + + + | const { User } = require("../models")
+const { comparePassword } = require("../helpers/bcrypt")
+const { signToken } = require("../helpers/jwt")
+
+module.exports = class UserController {
+ static async registerUser(req, res, next) {
+ try {
+ const { name, email, password, role } = req.body;
+ await User.create({ name, email, password, role });
+ res.status(201).json({ name, email, role })
+ } catch (error) {
+ console.log(error);
+ next(error);
+ }
+ }
+
+ static async loginUser(req, res, next) {
+ try {
+ const { email, password } = req.body;
+ if (!email) {
+ throw { name: "CustomEmailError" }
+ }
+ if (!password) {
+ throw { name: "CustomPasswordError" }
+ }
+ const userData = await User.findOne({ where: { email: email } });
+ if (!userData || !comparePassword(password, userData.password)) {
+ throw { name: "CustomError" }
+ }
+ const token = signToken({ id: userData.id })
+ res.status(200).json({ access_token: token })
+ } catch (error) {
+ console.log(error);
+ next(error);
+ }
+ }
+} |
+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +
+ +| File | ++ | Statements | ++ | Branches | ++ | Functions | ++ | Lines | ++ |
|---|---|---|---|---|---|---|---|---|---|
| BurgerController.js | +
+
+ |
+ 30.96% | +48/155 | +23.52% | +8/34 | +40% | +6/15 | +31.78% | +48/151 | +
| UserController.js | +
+
+ |
+ 100% | +23/23 | +100% | +8/8 | +100% | +2/2 | +100% | +23/23 | +
+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +
+ +| 1 +2 +3 +4 +5 +6 | 11x + +11x +13x +2x + | const {hashSync, compareSync} = require("bcryptjs");
+
+module.exports = {
+ hashPassword: (password) => hashSync(password, 10),
+ comparePassword: (password, db_password) => compareSync(password, db_password)
+} |
+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +
+ ++ Press n or j to go to the next uncovered block, b, p or k for the previous block. +
+ +| 1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 | 11x + +11x + + +20x + + + +21x + + +11x + + + | const jsonwebtoken = require("jsonwebtoken")
+
+const secret = process.env.JWT_SECRET;
+
+function signToken(payload) {
+ return jsonwebtoken.sign(payload, secret)
+}
+
+function verifyToken(token) {
+ return jsonwebtoken.verify(token, secret)
+}
+
+module.exports = {
+ signToken,
+ verifyToken
+} |
+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +
+ +| File | ++ | Statements | ++ | Branches | ++ | Functions | ++ | Lines | ++ |
|---|---|---|---|---|---|---|---|---|---|
| app.js | +
+
+ |
+ 97.22% | +35/36 | +50% | +1/2 | +0% | +0/1 | +97.22% | +35/36 | +
+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +
+ +| 1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 | 11x +11x + + +31x +31x +31x +6x + +25x +25x +4x + +21x +14x +14x + + +14x + +14x + + +17x +17x + + + +11x | const { User } = require("../models");
+const { verifyToken } = require("../helpers/jwt");
+
+async function authentication(req, res, next) {
+ try {
+ const { authorization } = req.headers;
+ if (!authorization) {
+ throw { name: "Unauthenticated" }
+ }
+ const [type, token] = authorization.split(" ");
+ if (type !== "Bearer") {
+ throw { name: "Unauthenticated" }
+ }
+ const payload = verifyToken(token);
+ const user = await User.findByPk(payload.id)
+ Iif (!user) {
+ throw { name: "Unauthenticated" }
+ }
+ req.user = user;
+
+ next();
+
+ } catch (error) {
+ console.log(error);
+ next(error);
+ }
+}
+
+module.exports = authentication; |
+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +
+ +| 1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 | 11x + + +10x +10x +10x +10x +6x + +4x +1x + + +3x + + +7x +7x + + + +11x | const { Burger } = require("../models");
+
+async function authorization(req, res, next) {
+ try {
+ const {burgerId} = req.params;
+ const burgerData = await Burger.findByPk(burgerId);
+ if(!burgerData) {
+ throw {name: "Not found"}
+ }
+ if(req.user.role !== "Admin") {
+ throw {name: "Unauthorized"}
+ }
+
+ next();
+
+ } catch (error) {
+ console.log(error);
+ next(error);
+ }
+}
+
+module.exports = authorization; |
+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +
+ +| 1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 | 11x +34x +34x +34x + +34x + + +17x +17x +17x + +2x +2x +2x + +1x +1x +1x + +6x +6x +6x + + +6x +6x +6x + +1x +1x +1x + +1x +1x +1x + + + + + + + + + + + + +34x + | module.exports = (error, req, res, next) => {
+ console.log(error, "<<<<<<<<< from errorHandler")
+ let status = error.status || 500;
+ let message = error.message || "Internal Server Error";
+
+ switch (error.name) {
+ case "JsonWebTokenError":
+ case "Unauthenticated":
+ status = 401
+ message = "Invalid token"
+ break;
+ case "CustomError":
+ status = 401
+ message = "Invalid email or password"
+ break;
+ case "Unauthorized":
+ status = 403
+ message = "Forbidden"
+ break;
+ case "Not found":
+ status = 404
+ message = `Data not found`
+ break;
+ case "SequelizeValidationError":
+ case "SequelizeUniqueConstraintError":
+ status = 400
+ message = error.errors[0].message
+ break;
+ case "CustomEmailError":
+ status = 400
+ message = "Email is required"
+ break;
+ case "CustomPasswordError":
+ status = 400
+ message = "Password is required"
+ break;
+ case "CustomFileError":
+ status = 400
+ message = "Image file is required"
+ break;
+ case "CustomPurchaseError":
+ status = 400
+ message = "Burger has been purchased"
+ break;
+ default:
+ break;
+ }
+
+ res.status(status).json({message: message, error});
+} |
+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +
+ +| File | ++ | Statements | ++ | Branches | ++ | Functions | ++ | Lines | ++ |
|---|---|---|---|---|---|---|---|---|---|
| authentication.js | +
+
+ |
+ 94.44% | +17/18 | +83.33% | +5/6 | +100% | +1/1 | +94.44% | +17/18 | +
| authorization.js | +
+
+ |
+ 100% | +12/12 | +100% | +4/4 | +100% | +1/1 | +100% | +12/12 | +
| errorHandler.js | +
+
+ |
+ 79.41% | +27/34 | +81.25% | +13/16 | +100% | +1/1 | +79.41% | +27/34 | +
+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +
+ +| 1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 | + + +11x +11x + + + + + + + + +11x + + +11x + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +11x + | 'use strict';
+const {
+ Model
+} = require('sequelize');
+module.exports = (sequelize, DataTypes) => {
+ class Burger extends Model {
+ /**
+ * Helper method for defining associations.
+ * This method is not a part of Sequelize lifecycle.
+ * The `models/index` file will call this method automatically.
+ */
+ static associate(models) {
+ // define association here
+ Burger.hasMany(models.Cart, {foreignKey: "BurgerId"})
+ }
+ }
+ Burger.init({
+ name: {
+ type: DataTypes.STRING,
+ allowNull: false,
+ unique: {
+ args: true,
+ msg: "Burger already exists"
+ },
+ validate: {
+ notNull: {
+ msg: "Name is required"
+ },
+ notEmpty: {
+ msg: "Name is required"
+ }
+ }
+ },
+ desc: {
+ type: DataTypes.STRING,
+ allowNull: false,
+ validate: {
+ notNull: {
+ msg: "Description is required"
+ },
+ notEmpty: {
+ msg: "Description is required"
+ }
+ }
+ },
+ price: {
+ type: DataTypes.INTEGER,
+ allowNull: false,
+ validate: {
+ notNull: {
+ msg: "Price is required"
+ },
+ notEmpty: {
+ msg: "Price is required"
+ }
+ }
+ },
+ veg: {
+ type: DataTypes.BOOLEAN,
+ allowNull: false,
+ validate: {
+ notNull: {
+ msg: "Veg status is required"
+ },
+ notEmpty: {
+ msg: "Veg status is required"
+ }
+ }
+ },
+ images: {
+ type: DataTypes.STRING,
+ allowNull: false,
+ validate: {
+ notNull: {
+ msg: "Image is required"
+ },
+ notEmpty: {
+ msg: "Image is required"
+ }
+ }
+ }
+ }, {
+ sequelize,
+ modelName: 'Burger',
+ });
+ return Burger;
+}; |
+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +
+ +| 1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 | + + +11x +11x + + + + + + + + +11x +11x + + +11x + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +11x + | 'use strict';
+const {
+ Model
+} = require('sequelize');
+module.exports = (sequelize, DataTypes) => {
+ class Cart extends Model {
+ /**
+ * Helper method for defining associations.
+ * This method is not a part of Sequelize lifecycle.
+ * The `models/index` file will call this method automatically.
+ */
+ static associate(models) {
+ // define association here
+ Cart.belongsTo(models.User, {foreignKey: "UserId"})
+ Cart.belongsTo(models.Burger, {foreignKey: "BurgerId"})
+ }
+ }
+ Cart.init({
+ quantity: {
+ type: DataTypes.INTEGER,
+ allowNull: false,
+ validate: {
+ notNull: {
+ msg: "Quantity is required"
+ },
+ notEmpty: {
+ msg: "Quantity is required"
+ }
+ }
+ },
+ UserId: {
+ type: DataTypes.INTEGER,
+ allowNull: false,
+ validate: {
+ notNull: {
+ msg: "User is required"
+ },
+ notEmpty: {
+ msg: "User is required"
+ }
+ }
+ },
+ BurgerId: {
+ type: DataTypes.INTEGER,
+ allowNull: false,
+ validate: {
+ notNull: {
+ msg: "Burger is required"
+ },
+ notEmpty: {
+ msg: "Burger is required"
+ }
+ }
+ },
+ purchased: {
+ type: DataTypes.BOOLEAN,
+ allowNull: false,
+ defaultValue: false
+ },
+ purchasedAt: {
+ type: DataTypes.DATE,
+ allowNull: true
+ },
+ }, {
+ sequelize,
+ modelName: 'Cart',
+ });
+ return Cart;
+}; |
+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +
+ +| File | ++ | Statements | ++ | Branches | ++ | Functions | ++ | Lines | ++ |
|---|---|---|---|---|---|---|---|---|---|
| burger.js | +
+
+ |
+ 100% | +5/5 | +100% | +0/0 | +100% | +2/2 | +100% | +5/5 | +
| cart.js | +
+
+ |
+ 100% | +6/6 | +100% | +0/0 | +100% | +2/2 | +100% | +6/6 | +
| index.js | +
+
+ |
+ 95.23% | +20/21 | +70% | +7/10 | +100% | +3/3 | +95.23% | +20/21 | +
| user.js | +
+
+ |
+ 100% | +7/7 | +100% | +0/0 | +100% | +3/3 | +100% | +7/7 | +
+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +
+ +| 1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 | + +11x +11x +11x +11x +11x +11x +11x +11x + + +11x + + +11x + + +11x + + +44x + + + + + + + +33x +33x + + +11x +33x +33x + + + +11x +11x + +11x + | 'use strict';
+
+const fs = require('fs');
+const path = require('path');
+const Sequelize = require('sequelize');
+const process = require('process');
+const basename = path.basename(__filename);
+const env = process.env.NODE_ENV || 'development';
+const config = require(__dirname + '/../config/config.json')[env];
+const db = {};
+
+let sequelize;
+Iif (config.use_env_variable) {
+ sequelize = new Sequelize(process.env[config.use_env_variable], config);
+} else {
+ sequelize = new Sequelize(config.database, config.username, config.password, config);
+}
+
+fs
+ .readdirSync(__dirname)
+ .filter(file => {
+ return (
+ file.indexOf('.') !== 0 &&
+ file !== basename &&
+ file.slice(-3) === '.js' &&
+ file.indexOf('.test.js') === -1
+ );
+ })
+ .forEach(file => {
+ const model = require(path.join(__dirname, file))(sequelize, Sequelize.DataTypes);
+ db[model.name] = model;
+ });
+
+Object.keys(db).forEach(modelName => {
+ Eif (db[modelName].associate) {
+ db[modelName].associate(db);
+ }
+});
+
+db.sequelize = sequelize;
+db.Sequelize = Sequelize;
+
+module.exports = db;
+ |
+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +
+ +| 1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 | +11x + + +11x +11x + + + + + + + + +11x + + +11x + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +2x + + + +11x + | 'use strict';
+const {hashPassword} = require("../helpers/bcrypt")
+const {
+ Model
+} = require('sequelize');
+module.exports = (sequelize, DataTypes) => {
+ class User extends Model {
+ /**
+ * Helper method for defining associations.
+ * This method is not a part of Sequelize lifecycle.
+ * The `models/index` file will call this method automatically.
+ */
+ static associate(models) {
+ // define association here
+ User.hasMany(models.Cart, {foreignKey: "UserId"})
+ }
+ }
+ User.init({
+ name: {
+ type: DataTypes.STRING,
+ allowNull: false,
+ validate: {
+ notNull: {
+ msg: "Name is required"
+ },
+ notEmpty: {
+ msg: "Name is required"
+ }
+ }
+ },
+ email: {
+ type: DataTypes.STRING,
+ allowNull: false,
+ unique: {
+ args: true,
+ msg: "Email is already registered"
+ },
+ validate: {
+ notNull: {
+ msg: "Email is required"
+ },
+ notEmpty: {
+ msg: "Email is required"
+ },
+ isEmail: {
+ args: true,
+ msg: "Invalid email format"
+ }
+ }
+ },
+ password: {
+ type: DataTypes.STRING,
+ allowNull: false,
+ validate: {
+ notNull: {
+ msg: "Password is required"
+ },
+ notEmpty: {
+ msg: "Password is required"
+ },
+ len: {
+ args: [5],
+ msg: "Password is too short"
+ }
+ }
+ },
+ role: {
+ type: DataTypes.STRING,
+ defaultValue: "Customer"
+ },
+ imageUrl: {
+ type: DataTypes.STRING,
+ defaultValue: "https://t4.ftcdn.net/jpg/04/10/43/77/360_F_410437733_hdq4Q3QOH9uwh0mcqAhRFzOKfrCR24Ta.jpg"
+ }
+ }, {
+ sequelize,
+ modelName: 'User',
+ hooks: {
+ beforeCreate (value, option) {
+ value.password = hashPassword(value.password)
+ }
+ }
+ });
+ return User;
+}; |
+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +
+ +| File | ++ | Statements | ++ | Branches | ++ | Functions | ++ | Lines | ++ |
|---|---|---|---|---|---|---|---|---|---|
| Server | +
+
+ |
+ 97.22% | +35/36 | +50% | +1/2 | +0% | +0/1 | +97.22% | +35/36 | +
| Server/controllers | +
+
+ |
+ 39.88% | +71/178 | +38.09% | +16/42 | +47.05% | +8/17 | +40.8% | +71/174 | +
| Server/helpers | +
+
+ |
+ 100% | +9/9 | +100% | +0/0 | +100% | +4/4 | +100% | +9/9 | +
| Server/middlewares | +
+
+ |
+ 87.5% | +56/64 | +84.61% | +22/26 | +100% | +3/3 | +87.5% | +56/64 | +
| Server/models | +
+
+ |
+ 97.43% | +38/39 | +70% | +7/10 | +100% | +10/10 | +97.43% | +38/39 | +