Repository: hiteshchoudhary/pro-backend-developer Branch: main Commit: d876ccc661b5 Files: 91 Total size: 175.0 KB Directory structure: gitextract_2i27geyc/ ├── formsandimages/ │ ├── index.js │ ├── package.json │ └── views/ │ ├── getform.ejs │ └── postform.ejs ├── lcoauthsystem/ │ ├── app.js │ ├── config/ │ │ └── database.js │ ├── index.js │ ├── middleware/ │ │ └── auth.js │ ├── model/ │ │ └── user.js │ └── package.json ├── lcopassportgoogle/ │ ├── index.js │ ├── model/ │ │ └── user.js │ ├── package.json │ ├── passport/ │ │ └── passport.js │ ├── routes/ │ │ └── auth.js │ └── views/ │ ├── home.ejs │ └── login.ejs ├── mydocslco/ │ ├── index.js │ ├── nodemon.json │ ├── package.json │ └── swagger.yaml ├── razorpay/ │ ├── index.js │ ├── package.json │ └── public/ │ └── index.html ├── readme.md ├── socialapp/ │ ├── .gitignore │ ├── index.js │ ├── nodemon.json │ ├── package.json │ └── swagger.yaml └── tshirtstore/ ├── .gitignore ├── README.md ├── app.js ├── config/ │ └── db.js ├── controllers/ │ ├── homeController.js │ ├── orderController.js │ ├── paymentController.js │ ├── productController.js │ └── userController.js ├── index.js ├── lcotshirtstore/ │ ├── .gitignore │ ├── app.js │ ├── config/ │ │ └── db.js │ ├── controllers/ │ │ ├── homeController.js │ │ ├── orderController.js │ │ ├── paymentController.js │ │ ├── productController.js │ │ └── userController.js │ ├── index.js │ ├── middlewares/ │ │ ├── bigPromise.js │ │ └── user.js │ ├── models/ │ │ ├── order.js │ │ ├── product.js │ │ └── user.js │ ├── nodemon.json │ ├── package.json │ ├── routes/ │ │ ├── home.js │ │ ├── order.js │ │ ├── payment.js │ │ ├── product.js │ │ └── user.js │ ├── swagger.yaml │ ├── utils/ │ │ ├── cookieToken.js │ │ ├── customError.js │ │ ├── demotest.js │ │ ├── emailHelper.js │ │ ├── testOrder.json │ │ └── whereClause.js │ └── views/ │ └── signuptest.ejs ├── middlewares/ │ ├── bigPromise.js │ ├── productionError.js │ └── user.js ├── models/ │ ├── order.js │ ├── product.js │ └── user.js ├── nodemon.json ├── package.json ├── routes/ │ ├── home.js │ ├── order.js │ ├── payment.js │ ├── product.js │ └── user.js ├── swagger.yaml ├── utils/ │ ├── cookieToken.js │ ├── customError.js │ ├── demotest.js │ ├── emailHelper.js │ ├── testOrder.json │ └── whereClause.js └── views/ ├── home.ejs └── signuptest.ejs ================================================ FILE CONTENTS ================================================ ================================================ FILE: formsandimages/index.js ================================================ const express = require("express"); const fileUpload = require("express-fileupload"); const cloudinary = require("cloudinary").v2; const app = express(); cloudinary.config({ // cloud_name: processs.env.CLOUD_NAME cloud_name: "dk92l1yoc", api_key: "769888332458168", api_secret: "7Kh-q81hHRdyDNiBxISrouGEFCo", }); app.set("view engine", "ejs"); //middleware app.use(express.json()); app.use(express.urlencoded({ extended: true })); app.use( fileUpload({ useTempFiles: true, tempFileDir: "/tmp/", }) ); app.get("/myget", (req, res) => { console.log(req.body); res.send(req.body); }); app.post("/mypost", async (req, res) => { console.log(req.body); console.log(req.files); let result; let imageArray = []; // case - multiple images if (req.files) { for (let index = 0; index < req.files.samplefile.length; index++) { let result = await cloudinary.uploader.upload( req.files.samplefile[index].tempFilePath, { folder: "users", } ); imageArray.push({ public_id: result.public_id, secure_url: result.secure_url, }); } } // ### use case for single image // let file = req.files.samplefile; // result = await cloudinary.uploader.upload(file.tempFilePath, { // folder: "users", // }); console.log(result); details = { firstname: req.body.firstname, lastname: req.body.lastname, result, imageArray, }; console.log(details); res.send(details); }); // just to render the forms app.get("/mygetform", (req, res) => { res.render("getform"); }); app.get("/mypostform", (req, res) => { res.render("postform"); }); app.listen(4000, () => console.log(`Server is runnning at port 4000`)); ================================================ FILE: formsandimages/package.json ================================================ { "name": "formsandimages", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "start": "nodemon index.js" }, "keywords": [], "author": "", "license": "ISC", "dependencies": { "cloudinary": "^1.27.1", "ejs": "^3.1.6", "express": "^4.17.1", "express-fileupload": "^1.2.1" }, "devDependencies": { "nodemon": "^2.0.13" } } ================================================ FILE: formsandimages/views/getform.ejs ================================================ GET form

GET form

We'll never share your email with anyone else.
================================================ FILE: formsandimages/views/postform.ejs ================================================ GET form

POST form

We'll never share your email with anyone else.
================================================ FILE: lcoauthsystem/app.js ================================================ require("dotenv").config(); require("./config/database").connect(); const express = require("express"); const bcrypt = require("bcryptjs"); const jwt = require("jsonwebtoken"); var cookieParser = require("cookie-parser"); const User = require("./model/user"); const auth = require("./middleware/auth"); const app = express(); app.use(express.json()); app.use(cookieParser()); app.get("/", (req, res) => { res.send("

Hello from auth system - LCO

"); }); app.post("/register", async (req, res) => { try { const { firstname, lastname, email, password } = req.body; if (!(email && password && firstname && lastname)) { res.status(400).send("All fields are required"); } const existingUser = await User.findOne({ email }); // PROMISE if (existingUser) { res.status(401).send("User already exists"); } const myEncPassword = await bcrypt.hash(password, 10); const user = await User.create({ firstname, lastname, email: email.toLowerCase(), password: myEncPassword, }); //token const token = jwt.sign( { user_id: user._id, email }, process.env.SECRET_KEY, { expiresIn: "2h", } ); user.token = token; //update or not in DB // handle password situation user.password = undefined; // send token or send just success yes and redirect - choice res.status(201).json(user); } catch (error) { console.log(error); } }); app.post("/login", async (req, res) => { try { const { email, password } = req.body; if (!(email && password)) { res.status(400).send("Field is missing"); } const user = await User.findOne({ email }); // if(!user){ // res.status(400).send("You are not registered in our app") // } if (user && (await bcrypt.compare(password, user.password))) { const token = jwt.sign( { user_id: user._id, email }, process.env.SECRET_KEY, { expiresIn: "2h", } ); user.token = token; user.password = undefined; // res.status(200).json(user); // if you want to use cookies const options = { expires: new Date(Date.now() + 3 * 24 * 60 * 60 * 1000), httpOnly: true, }; res.status(200).cookie("token", token, options).json({ success: true, token, user, }); } res.sendStatus(400).send("email or password is incorrect"); } catch (error) { console.log(error); } }); app.get("/dashboard", auth, (req, res) => { res.send("Welcome to secret information"); }); module.exports = app; ================================================ FILE: lcoauthsystem/config/database.js ================================================ const mongoose = require("mongoose"); const { MONGODB_URL } = process.env; exports.connect = () => { mongoose .connect(MONGODB_URL, { useNewUrlParser: true, useUnifiedTopology: true, }) .then(console.log(`DB CONNECTED SUCCESSFULLY`)) .catch((error) => { console.log(`DB CONNECTION FAILED`); console.log(error); process.exit(1); }); }; ================================================ FILE: lcoauthsystem/index.js ================================================ const app = require("./app"); const { PORT } = process.env; app.listen(PORT, () => console.log(`Server is running at port ${PORT}...`)); ================================================ FILE: lcoauthsystem/middleware/auth.js ================================================ const jwt = require("jsonwebtoken"); //model is optional const auth = (req, res, next) => { console.log(req.cookies); const token = req.cookies.token || req.body.token || req.header("Authorization").replace("Bearer ", ""); if (!token) { return res.status(403).send("token is missing"); } try { const decode = jwt.verify(token, process.env.SECRET_KEY); console.log(decode); req.user = decode; // bring in info from DB } catch (error) { return res.status(401).send("Invalid Token"); } return next(); }; module.exports = auth; ================================================ FILE: lcoauthsystem/model/user.js ================================================ const mongoose = require("mongoose"); const userSchema = new mongoose.Schema({ firstname: { type: String, default: null, }, lastname: { type: String, default: null, }, email: { type: String, unique: true, }, password: { type: String, }, token: { type: String, }, }); module.exports = mongoose.model("user", userSchema); ================================================ FILE: lcoauthsystem/package.json ================================================ { "name": "lcoauthsystem", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "start": "node index.js", "dev": "nodemon index.js" }, "keywords": [], "author": "", "license": "ISC", "dependencies": { "bcryptjs": "^2.4.3", "cookie-parser": "^1.4.5", "dotenv": "^10.0.0", "express": "^4.17.1", "jsonwebtoken": "^8.5.1", "mongoose": "^6.0.11" }, "devDependencies": { "nodemon": "^2.0.13" } } ================================================ FILE: lcopassportgoogle/index.js ================================================ const express = require("express"); const mongoose = require("mongoose"); const auth = require("./routes/auth"); const passportConfig = require("./passport/passport"); const passport = require("passport"); const cookieSession = require("cookie-session"); const app = express(); //connect to the DB mongoose.connect("mongodb://127.0.0.1:27017/passport", () => console.log("DB CONNECTED") ); app.use( cookieSession({ maxAge: 3 * 24 * 60 * 60 * 1000, keys: ["thisislcotokenkey"], // dotenv }) ); app.use(passport.initialize()); app.use(passport.session()); const isLoggedIn = (req, res, next) => { if (!req.user) { res.redirect("/auth/login"); } next(); }; app.set("view engine", "ejs"); app.use("/auth", auth); app.get("/", isLoggedIn, (req, res) => { res.render("home"); }); app.listen(4000, () => console.log(`Server is running at port 4000...`)); ================================================ FILE: lcopassportgoogle/model/user.js ================================================ const mongoose = require("mongoose"); const userSchema = new mongoose.Schema({ name: String, googleId: String, email: String, }); module.exports = mongoose.model("User", userSchema); ================================================ FILE: lcopassportgoogle/package.json ================================================ { "name": "lcopassportgoogle", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "start": "node index.js", "dev": "nodemon index.js" }, "keywords": [], "author": "", "license": "ISC", "dependencies": { "cookie-session": "^1.4.0", "ejs": "^3.1.6", "express": "^4.17.1", "mongoose": "^6.0.12", "passport": "^0.5.0", "passport-google-oauth20": "^2.0.0" }, "devDependencies": { "nodemon": "^2.0.14" } } ================================================ FILE: lcopassportgoogle/passport/passport.js ================================================ const passport = require("passport"); const User = require("../model/user"); var GoogleStrategy = require("passport-google-oauth20").Strategy; passport.serializeUser(function (user, done) { done(null, user.id); }); passport.deserializeUser(function (id, done) { User.findById(id, function (err, user) { done(err, user); }); }); passport.use( new GoogleStrategy( { clientID: "learncodeonline", clientSecret: "aSecretGoesHere", callbackURL: "http://localhost:4000/auth/google/callback", }, (accessToken, refreshToken, profile, next) => { console.log("MY PROFILE", profile._json.email); User.findOne({ email: profile._json.email }).then((user) => { if (user) { console.log("User already exits in DB", user); next(null, user); // cookietoken() } else { User.create({ name: profile.displayName, googleId: profile.id, email: profile._json.email, }) .then((user) => { console.log("New User", user); next(null, user); // cookietoken() }) .catch((err) => console.log(err)); } }); // next(); } ) ); ================================================ FILE: lcopassportgoogle/routes/auth.js ================================================ const router = require("express").Router(); const passport = require("passport"); router.get("/login", (req, res) => { res.render("login"); }); router.get("/logout", (req, res) => { req.logout(); res.redirect("/auth/login"); }); router.get( "/google", passport.authenticate("google", { scope: ["profile", "email"], }), (req, res) => { res.send("login with google"); } ); router.get("/google/callback", passport.authenticate("google"), (req, res) => { res.send(req.user); }); module.exports = router; ================================================ FILE: lcopassportgoogle/views/home.ejs ================================================ LCO

LCO Social login

Logout Login Home

You are at HOME page

================================================ FILE: lcopassportgoogle/views/login.ejs ================================================ LCO

LCO Social login

Logout Login Home
google
================================================ FILE: mydocslco/index.js ================================================ const express = require("express"); const app = express(); const swaggerUi = require("swagger-ui-express"); const YAML = require("yamljs"); const swaggerDocument = YAML.load("./swagger.yaml"); const fileUpload = require("express-fileupload"); app.use("/api-docs", swaggerUi.serve, swaggerUi.setup(swaggerDocument)); app.use(express.json()); app.use(fileUpload()); let courses = [ { id: "11", name: "Learn Reactjs", price: 299, }, { id: "22", name: "Learn Angular", price: 399, }, { id: "33", name: "Learn Django", price: 499, }, ]; app.get("/", (req, res) => { res.send("hello from lco"); }); app.get("/api/v1/lco", (req, res) => { res.send("hello from lco docs"); }); app.get("/api/v1/lcoobject", (req, res) => { res.send({ id: "55", name: "Learn Backend", price: 999 }); }); app.get("/api/v1/courses", (req, res) => { res.send(courses); }); app.get("/api/v1/mycourse/:courseId", (req, res) => { const myCourse = courses.find((course) => course.id === req.params.courseId); res.send(myCourse); }); app.post("/api/v1/addCourse", (req, res) => { console.log(req.body); courses.push(req.body); res.send(true); }); app.get("/api/v1/coursequery", (req, res) => { let location = req.query.location; let device = req.query.device; res.send({ location, device }); }); app.post("/api/v1/courseupload", (req, res) => { console.log(req.headers); const file = req.files.file; console.log(file); let path = __dirname + "/images/" + Date.now() + ".jpg"; file.mv(path, (err) => { res.send(true); }); }); app.listen(4000, () => console.log(`Server is running at port 4000...`)); ================================================ FILE: mydocslco/nodemon.json ================================================ { "ext": ".js, .jsx, .yaml" } ================================================ FILE: mydocslco/package.json ================================================ { "name": "mydocslco", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "start": "nodemon index.js" }, "keywords": [], "author": "", "license": "ISC", "dependencies": { "express": "^4.17.1", "express-fileupload": "^1.2.1", "nodemon": "^2.0.13", "swagger-ui-express": "^4.1.6", "yamljs": "^0.3.0" } } ================================================ FILE: mydocslco/swagger.yaml ================================================ openapi: "3.0.0" info: title: learn express and swagger - LearnCodeOnline description: LCO - a course segment about writing docs version: 1.1.0 contact: email: hitesh@google.com url: "https://lco.dev" # servers: # - url: "https://localhost:4000/api/v1" # description: for local host - secure # - url: "http://localhost:4000/api/v1" # description: for local host - regular servers: - url: "{protocol}://localhost:4000/api/{version}" description: for local host variables: version: enum: - v1 - v2 default: v1 protocol: enum: - http - https default: http components: securitySchemes: cookieAuth: type: apiKey in: cookie name: token BearerAuth: type: http scheme: bearer paths: /lco: get: tags: - String summary: returns a greet message from LCO responses: 200: description: All good success content: application/json: schema: type: string example: "mystring" 400: description: Bad request 500: description: internal server error /lcoobject: get: tags: - Object summary: returns a unique course responses: 200: description: All good success content: application/json: schema: type: object properties: id: type: string name: type: string price: type: number 400: description: Bad request 500: description: internal server error /courses: get: tags: - Array summary: returns all courses responses: 200: description: All good success content: application/json: schema: type: array items: type: object properties: id: type: string name: type: string price: type: number 400: description: Bad request 500: description: internal server error /mycourse/{courseId}: get: tags: - String summary: returns course based on request id parameters: - name: courseId in: path required: true default: 22 schema: type: string responses: 200: description: All good success content: application/json: schema: type: object properties: id: type: string name: type: string price: type: number 400: description: Bad request 500: description: internal server error /addCourse: post: tags: - String summary: adds a new course to existing courses consumes: - application/json produces: - application/json requestBody: required: true content: application/json: schema: type: object properties: id: type: string name: type: string price: type: number responses: 200: description: All good success content: application/json: schema: type: boolean 400: description: Bad request 500: description: internal server error /coursequery: get: tags: - String summary: trying to learn about query parameters: - name: location in: query required: true schema: type: string enum: [delhi, london, jaipur] - name: device in: query required: true schema: type: string enum: [web, mobile] responses: 200: description: All good success content: application/json: schema: type: object properties: location: type: string device: type: string 400: description: Bad request 500: description: internal server error /courseupload: post: tags: - String summary: uploading course image parameters: - in: header name: auth requestBody: content: multipart/form-data: schema: type: object properties: file: type: string format: binary responses: 200: description: All good success 400: description: Bad request 500: description: internal server error ================================================ FILE: razorpay/index.js ================================================ const express = require("express"); const Razorpay = require("razorpay"); const app = express(); app.use(express.static("./public")); app.use(express.json()); // app.get("/", (req, res) => { // res.send("hi"); // }); app.post("/order", async (req, res) => { const amount = req.body.amount; var instance = new Razorpay({ key_id: "rzp_test_DfhbmLCnKJDWxO", key_secret: "YFZxjAWfyFyJidZXbXLu2UN0", // this needs to go in .env }); var options = { amount: amount * 100, // amount in the smallest currency unit currency: "INR", receipt: "order_rcptid_11", }; // instance.orders.create(options, function (err, order) { // console.log(order); // }); const myOrder = await instance.orders.create(options); res.status(200).json({ success: true, amount, order: myOrder, }); }); app.listen(4000, () => console.log(`Server is running at port 4000`)); ================================================ FILE: razorpay/package.json ================================================ { "name": "razorpay", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "start": "node index.js" }, "keywords": [], "author": "", "license": "ISC", "dependencies": { "express": "^4.17.1", "razorpay": "^2.0.7" } } ================================================ FILE: razorpay/public/index.html ================================================ Document

Testing public folder

================================================ FILE: readme.md ================================================ # welcome to pro backend developer course code files ## If you like this course, please drop a thanks node at my social profiles. [CoderCommunity](https://web.codercommunity.io) [Website](https://hiteshchoudahry.com) [Instagram](https://instagram.com/hiteshchoudharyofficial) of course there are bugs in these code files. As you teach you focus more on concept and less on production level execution. If you find any bug, consider that as your challenge to fix them. ================================================ FILE: socialapp/.gitignore ================================================ node_modules/ ================================================ FILE: socialapp/index.js ================================================ const express = require("express"); const format = require("date-format"); const app = express(); // swagger docs related const swaggerUi = require("swagger-ui-express"); const YAML = require("yamljs"); const swaggerDocument = YAML.load("./swagger.yaml"); app.use("/api-docs", swaggerUi.serve, swaggerUi.setup(swaggerDocument)); const PORT = process.env.PORT || 4000; app.get("/", (req, res) => { res.status(200).send("

Hello from LCO

"); }); app.get("/api/v1/instagram", (req, res) => { const instaSocial = { username: "hiteshchoudharyOfficial", folowers: 66, follows: 70, date: format.asString("dd[MM] - hh:mm:ss", new Date()), }; res.status(200).json(instaSocial); }); app.get("/api/v1/facebook", (req, res) => { const instaSocial = { username: "hiteshchoudharyPage", folowers: 88, follows: 10, date: format.asString("dd[MM] - hh:mm:ss", new Date()), }; res.status(200).json(instaSocial); }); app.get("/api/v1/linkedin", (req, res) => { const instaSocial = { username: "hiteshchoudhary", folowers: 800, follows: 80, date: new Date(), }; res.status(200).json(instaSocial); }); app.get("/api/v1/:token", (req, res) => { console.log(req.params.token); res.status(200).json({ param: req.params.token }); }); app.listen(PORT, () => { console.log(`Server is running at ${PORT}`); }); ================================================ FILE: socialapp/nodemon.json ================================================ { "ext": ".js, .json, .yaml, .jsx" } ================================================ FILE: socialapp/package.json ================================================ { "name": "socialapp", "version": "1.0.0", "description": "a basic social media app", "main": "index.js", "scripts": { "start": "nodemon index.js" }, "keywords": [ "express", "api" ], "author": "Hitesh Choudhary", "license": "ISC", "dependencies": { "date-format": "^3.0.0", "express": "^4.17.1", "swagger-ui-express": "^4.1.6", "yamljs": "^0.3.0" }, "devDependencies": { "nodemon": "^2.0.13" } } ================================================ FILE: socialapp/swagger.yaml ================================================ openapi: 3.0.0 info: title: Social App description: Our fist Social app at LCO - Hitesh version: 1.0.1 servers: - url: http://localhost:4000/api/v1 description: localhost version of our app - url: https://localhost:4000/api/v1 description: this is just a dummy api url, it doesn't work components: securitySchemes: BasicAuth: type: http scheme: basic BearerAuth: type: http scheme: bearer paths: /instagram: get: summary: returns username, followers and follows responses: '200': # status code description: returns an object content: application/json: schema: type: object properties: username: type: string followers: type: string follows: type: string post: summary: returns username, followers and follows responses: '200': # status code description: returns an object content: application/json: schema: type: object properties: username: type: string followers: type: string follows: type: string /{token}: get: summary: returns whatever is there in parameters parameters: - name: token default: 5 in: path schema: type: string responses: '200': # status code description: returns an object content: application/json: schema: type: object properties: params: type: string ================================================ FILE: tshirtstore/.gitignore ================================================ .env node_modules/ ================================================ FILE: tshirtstore/README.md ================================================ # Tshirt store for pro backend developer course Please add env variables for the project --- ### PORT=4000 ### DB_URL= ### JWT_SECRET= ### JWT_EXPIRY= ### COOKIE_TIME= ### CLOUDINARY_NAME= ### CLOUDINARY_API_KEY= ### CLOUDINARY_API_SECRET= ### SMTP_HOST= ### SMTP_PORT= ### SMTP_USER= ### SMTP_PASS= ### STRIPE_API_KEY= ### STRIPE_SECRET= ### RAZORPAY_API_KEY= ### RAZORPAY_SECRET= --- [LearnCodeOnline](https://courses.learncodeonline.in/learn) updat the variables before deploying ================================================ FILE: tshirtstore/app.js ================================================ const express = require("express"); require("dotenv").config(); const app = express(); const morgan = require("morgan"); const cookieParser = require("cookie-parser"); const fileUpload = require("express-fileupload"); //for swagger documentation const swaggerUi = require("swagger-ui-express"); const YAML = require("yamljs"); const swaggerDocument = YAML.load("./swagger.yaml"); app.use("/api-docs", swaggerUi.serve, swaggerUi.setup(swaggerDocument)); //custom error middleware for easy front end const productionError = require("./middlewares/productionError"); //regular middleware app.use(express.json()); app.use(express.urlencoded({ extended: true })); //cookies and file middleware app.use(cookieParser()); app.use( fileUpload({ useTempFiles: true, tempFileDir: "/tmp/", }) ); //temp check app.set("view engine", "ejs"); //morgan middleware app.use(morgan("tiny")); //import all routes here const home = require("./routes/home"); const user = require("./routes/user"); const product = require("./routes/product"); const payment = require("./routes/payment"); const order = require("./routes/order"); //router middleware app.use("/api/v1", home); app.use("/api/v1", user); app.use("/api/v1", product); app.use("/api/v1", payment); app.use("/api/v1", order); app.get("/signuptest", (req, res) => { res.render("signuptest"); }); app.get("/", (req, res) => { res.render("home"); }); //to handle production error app.use(productionError); // export app js module.exports = app; ================================================ FILE: tshirtstore/config/db.js ================================================ const mongoose = require("mongoose"); const connectWithDb = () => { mongoose .connect(process.env.DB_URL, { useNewUrlParser: true, useUnifiedTopology: true, }) .then(console.log(`DB GOT CONNECTED`)) .catch((error) => { console.log(`DB CONNECTION ISSUES`); console.log(error); process.exit(1); }); }; module.exports = connectWithDb; ================================================ FILE: tshirtstore/controllers/homeController.js ================================================ const BigPromise = require("../middlewares/bigPromise"); exports.home = BigPromise(async (req, res) => { // const db = await something() res.status(200).json({ success: true, greeting: "Hello from API", }); }); exports.homeDummy = async (req, res) => { try { // const db = await something() res.status(200).json({ success: true, greeting: "this is another dummy route", }); } catch (error) { console.log(error); } }; ================================================ FILE: tshirtstore/controllers/orderController.js ================================================ const Order = require("../models/order"); const Product = require("../models/product"); const BigPromise = require("../middlewares/bigPromise"); const CustomError = require("../utils/customError"); exports.createOrder = BigPromise(async (req, res, next) => { const { shippingInfo, orderItems, paymentInfo, taxAmount, shippingAmount, totalAmount, } = req.body; const order = await Order.create({ shippingInfo, orderItems, paymentInfo, taxAmount, shippingAmount, totalAmount, user: req.user._id, }); res.status(200).json({ success: true, order, }); }); exports.getOneOrder = BigPromise(async (req, res, next) => { const order = await Order.findById(req.params.id).populate( "user", "name email" ); if (!order) { return next(new CustomError("please check order id", 401)); } res.status(200).json({ success: true, order, }); }); exports.getLoggedInOrders = BigPromise(async (req, res, next) => { const order = await Order.find({ user: req.user._id }); if (!order) { return next(new CustomError("please check order id", 401)); } res.status(200).json({ success: true, order, }); }); exports.admingetAllOrders = BigPromise(async (req, res, next) => { const orders = await Order.find(); res.status(200).json({ success: true, orders, }); }); exports.adminUpdateOrder = BigPromise(async (req, res, next) => { const order = await Order.findById(req.params.id); if (order.orderStatus === "Delivered") { return next(new CustomError("Order is already marked for delivered", 401)); } order.orderStatus = req.body.orderStatus; order.orderItems.forEach(async (prod) => { await updateProductStock(prod.product, prod.quantity); }); await order.save(); res.status(200).json({ success: true, order, }); }); exports.adminDeleteOrder = BigPromise(async (req, res, next) => { const order = await Order.findById(req.params.id); await order.remove(); res.status(200).json({ success: true, }); }); async function updateProductStock(productId, quantity) { const product = await Product.findById(productId); product.stock = product.stock - quantity; await product.save({ validateBeforeSave: false }); } ================================================ FILE: tshirtstore/controllers/paymentController.js ================================================ const BigPromise = require("../middlewares/bigPromise"); const stripe = require("stripe")(process.env.STRIPE_SECRET); const Razorpay = require("razorpay"); exports.sendStripeKey = BigPromise(async (req, res, next) => { res.status(200).json({ stripekey: process.env.STRIPE_API_KEY, }); }); exports.captureStripePayment = BigPromise(async (req, res, next) => { const paymentIntent = await stripe.paymentIntents.create({ amount: req.body.amount, currency: "inr", //optional metadata: { integration_check: "accept_a_payment" }, }); res.status(200).json({ success: true, amount: req.body.amount, client_secret: paymentIntent.client_secret, //you can optionally send id as well }); }); exports.sendRazorpayKey = BigPromise(async (req, res, next) => { res.status(200).json({ razorpaykey: process.env.RAZORPAY_API_KEY, }); }); exports.captureRazorpayPayment = BigPromise(async (req, res, next) => { var instance = new Razorpay({ key_id: process.env.RAZORPAY_API_KEY, key_secret: process.env.RAZORPAY_SECRET, }); var options = { amount: req.body.amount, // amount in the smallest currency unit currency: "INR", }; console.log(req.body.amount); const myOrder = await instance.orders.create(options); res.status(200).json({ success: true, amount: req.body.amount, order: myOrder, }); }); ================================================ FILE: tshirtstore/controllers/productController.js ================================================ const Product = require("../models/product"); const BigPromise = require("../middlewares/bigPromise"); const CustomError = require("../utils/customError"); const cloudinary = require("cloudinary"); const WhereClause = require("../utils/whereClause"); exports.addProduct = BigPromise(async (req, res, next) => { // images let imageArray = []; if (!req.files) { return next(new CustomError("images are required", 401)); } console.log("RESULT", req.files.photos[0]); if (req.files) { for (let index = 0; index < req.files.photos.length; index++) { console.log("UPLOAD START..."); let result = await cloudinary.v2.uploader.upload( req.files.photos[index].tempFilePath, { folder: "products", } ); // another way to upload and check error // let result // cloudinary.v2.uploader.upload(req.files.photos[index].tempFilePath, { // folder: "products" // }, function(error, result){console.log(result, error)}) console.log("RESULT", result); imageArray.push({ id: result.public_id, secure_url: result.secure_url, }); } } req.body.photos = imageArray; req.body.user = req.user.id; const product = await Product.create(req.body); res.status(200).json({ success: true, product, }); }); exports.getAllProduct = BigPromise(async (req, res, next) => { const resultPerPage = 3; const totalcountProduct = await Product.countDocuments(); const productsObj = new WhereClause(Product.find(), req.query) .search() .filter(); let products = await productsObj.base; const filteredProductNumber = products.length; //products.limit().skip() productsObj.pager(resultPerPage); products = await productsObj.base.clone(); res.status(200).json({ success: true, products, filteredProductNumber, totalcountProduct, resultPerPage, }); }); exports.getOneProduct = BigPromise(async (req, res, next) => { const product = await Product.findById(req.params.id); if (!product) { return next(new CustomError("No product found with this id", 401)); } res.status(200).json({ success: true, product, }); }); exports.addReview = BigPromise(async (req, res, next) => { const { rating, comment, productId } = req.body; const review = { user: req.user._id, name: req.user.name, rating: Number(rating), comment, }; const product = await Product.findById(productId); const AlreadyReview = product.reviews.find( (rev) => rev.user.toString() === req.user._id.toString() ); if (AlreadyReview) { product.reviews.forEach((review) => { if (review.user.toString() === req.user._id.toString()) { review.comment = comment; review.rating = rating; } }); } else { product.reviews.push(review); product.numberOfReviews = product.reviews.length; } // adjust ratings product.ratings = product.reviews.reduce((acc, item) => item.rating + acc, 0) / product.reviews.length; //save await product.save({ validateBeforeSave: false }); res.status(200).json({ success: true, }); }); exports.deleteReview = BigPromise(async (req, res, next) => { const { productId } = req.query; const product = await Product.findById(productId); const reviews = product.reviews.filter( (rev) => rev.user.toString() === req.user._id.toString() ); const numberOfReviews = reviews.length; // adjust ratings product.ratings = product.reviews.reduce((acc, item) => item.rating + acc, 0) / product.reviews.length; //update the product await Product.findByIdAndUpdate( productId, { reviews, ratings, numberOfReviews, }, { new: true, runValidators: true, useFindAndModify: false, } ); res.status(200).json({ success: true, }); }); exports.getOnlyReviewsForOneProduct = BigPromise(async (req, res, next) => { const product = await Product.findById(req.query.id); res.status(200).json({ success: true, reviews: product.reviews, }); }); // admin only controllers exports.adminGetAllProduct = BigPromise(async (req, res, next) => { const products = await Product.find(); res.status(200).json({ success: true, products, }); }); exports.adminUpdateOneProduct = BigPromise(async (req, res, next) => { let product = await Product.findById(req.params.id); if (!product) { return next(new CustomError("No product found with this id", 401)); } let imagesArray = []; if (req.files) { //destroy the existing image for (let index = 0; index < product.photos.length; index++) { const res = await cloudinary.v2.uploader.destroy( product.photos[index].id ); } for (let index = 0; index < req.files.photos.length; index++) { let result = await cloudinary.v2.uploader.upload( req.files.photos[index].tempFilePath, { folder: "products", //folder name -> .env } ); imagesArray.push({ id: result.public_id, secure_url: result.secure_url, }); } } req.body.photos = imagesArray; product = await Product.findByIdAndUpdate(req.params.id, req.body, { new: true, runValidators: true, useFindAndModify: false, }); res.status(200).json({ success: true, product, }); }); exports.adminDeleteOneProduct = BigPromise(async (req, res, next) => { const product = await Product.findById(req.params.id); if (!product) { return next(new CustomError("No product found with this id", 401)); } //destroy the existing image for (let index = 0; index < product.photos.length; index++) { const res = await cloudinary.v2.uploader.destroy(product.photos[index].id); } await product.remove(); res.status(200).json({ success: true, message: "Product was deleted !", }); }); ================================================ FILE: tshirtstore/controllers/userController.js ================================================ const User = require("../models/user"); const BigPromise = require("../middlewares/bigPromise"); const CustomError = require("../utils/customError"); const cookieToken = require("../utils/cookieToken"); const cloudinary = require("cloudinary"); const mailHelper = require("../utils/emailHelper"); const crypto = require("crypto"); exports.signup = BigPromise(async (req, res, next) => { //let result; console.log(req.body); if (!req.files) { return next(new CustomError("photo is required for signup", 400)); } const { name, email, password } = req.body; if (!email || !name || !password) { return next(new CustomError("Name, email and password are required", 400)); } let file = req.files.photo; const result = await cloudinary.v2.uploader.upload(file.tempFilePath, { folder: "users", width: 150, crop: "scale", }); const user = await User.create({ name, email, password, photo: { id: result.public_id, secure_url: result.secure_url, }, }); cookieToken(user, res); }); exports.login = BigPromise(async (req, res, next) => { const { email, password } = req.body; // check for presence of email and password if (!email || !password) { return next(new CustomError("please provide email and password", 400)); } // get user from DB const user = await User.findOne({ email }).select("+password"); // if user not found in DB if (!user) { return next( new CustomError("Email or password does not match or exist", 400) ); } // match the password const isPasswordCorrect = await user.isValidatedPassword(password); //if password do not match if (!isPasswordCorrect) { return next( new CustomError("Email or password does not match or exist", 400) ); } // if all goes good and we send the token cookieToken(user, res); }); exports.logout = BigPromise(async (req, res, next) => { //clear the cookie res.cookie("token", null, { expires: new Date(Date.now()), httpOnly: true, }); //send JSON response for success res.status(200).json({ succes: true, message: "Logout success", }); }); exports.forgotPassword = BigPromise(async (req, res, next) => { // collect email const { email } = req.body; console.log(email); // find user in database const user = await User.findOne({ email }); // if user not found in database if (!user) { return next(new CustomError("Email not found as registered", 400)); } //get token from user model methods const forgotToken = user.getForgotPasswordToken(); // save user fields in DB await user.save({ validateBeforeSave: false }); // create a URL // const myUrl = `${req.protocol}://${req.get( // "host" // )}/api/v1/password/reset/${forgotToken}`; //URL for deployment as front end might be running at different URL const myUrl = `${process.env.FRONT_END}/password/reset/${forgotToken}`; // craft a message const message = `Copy paste this link in your URL and hit enter \n\n ${myUrl}`; // attempt to send email try { await mailHelper({ email: user.email, subject: "LCO TStore - Password reset email", message, }); // json reponse if email is success res.status(200).json({ succes: true, message: "Email sent successfully", }); } catch (error) { // reset user fields if things goes wrong user.forgotPasswordToken = undefined; user.forgotPasswordExpiry = undefined; await user.save({ validateBeforeSave: false }); // send error response return next(new CustomError(error.message, 500)); } }); exports.passwordReset = BigPromise(async (req, res, next) => { //get token from params const token = req.params.token; // hash the token as db also stores the hashed version const encryToken = crypto.createHash("sha256").update(token).digest("hex"); // find user based on hased on token and time in future const user = await User.findOne({ encryToken, forgotPasswordExpiry: { $gt: Date.now() }, }); if (!user) { return next(new CustomError("Token is invalid or expired", 400)); } // check if password and conf password matched if (req.body.password !== req.body.confirmPassword) { return next( new CustomError("password and confirm password do not match", 400) ); } // update password field in DB user.password = req.body.password; // reset token fields user.forgotPasswordToken = undefined; user.forgotPasswordExpiry = undefined; // save the user await user.save(); // send a JSON response OR send token cookieToken(user, res); }); exports.getLoggedInUserDetails = BigPromise(async (req, res, next) => { //req.user will be added by middleware // find user by id const user = await User.findById(req.user.id); //send response and user data res.status(200).json({ success: true, user, }); }); exports.changePassword = BigPromise(async (req, res, next) => { // get user from middleware const userId = req.user.id; // get user from database const user = await User.findById(userId).select("+password"); //check if old password is correct const isCorrectOldPassword = await user.isValidatedPassword( req.body.oldPassword ); if (!isCorrectOldPassword) { return next(new CustomError("old password is incorrect", 400)); } // allow to set new password user.password = req.body.password; // save user and send fresh token await user.save(); cookieToken(user, res); }); exports.updateUserDetails = BigPromise(async (req, res, next) => { // add a check for email and name in body // collect data from body const newData = { name: req.body.name, email: req.body.email, }; // if photo comes to us if (req.files) { const user = await User.findById(req.user.id); const imageId = user.photo.id; // delete photo on cloudinary const resp = await cloudinary.v2.uploader.destroy(imageId); // upload the new photo const result = await cloudinary.v2.uploader.upload( req.files.photo.tempFilePath, { folder: "users", width: 150, crop: "scale", } ); // add photo data in newData object newData.photo = { id: result.public_id, secure_url: result.secure_url, }; } // update the data in user const user = await User.findByIdAndUpdate(req.user.id, newData, { new: true, runValidators: true, useFindAndModify: false, }); res.status(200).json({ success: true, }); }); exports.adminAllUser = BigPromise(async (req, res, next) => { // select all users const users = await User.find(); // send all users res.status(200).json({ success: true, users, }); }); exports.admingetOneUser = BigPromise(async (req, res, next) => { // get id from url and get user from database const user = await User.findById(req.params.id); if (!user) { next(new CustomError("No user found", 400)); } // send user res.status(200).json({ success: true, user, }); }); exports.adminUpdateOneUserDetails = BigPromise(async (req, res, next) => { // add a check for email and name in body // get data from request body const newData = { name: req.body.name, email: req.body.email, role: req.body.role, }; // update the user in database const user = await User.findByIdAndUpdate(req.params.id, newData, { new: true, runValidators: true, useFindAndModify: false, }); res.status(200).json({ success: true, }); }); exports.adminDeleteOneUser = BigPromise(async (req, res, next) => { // get user from url const user = await User.findById(req.params.id); if (!user) { return next(new CustomError("No Such user found", 401)); } // get image id from user in database const imageId = user.photo.id; // delete image from cloudinary await cloudinary.v2.uploader.destroy(imageId); // remove user from databse await user.remove(); res.status(200).json({ success: true, }); }); exports.managerAllUser = BigPromise(async (req, res, next) => { // select the user with role of user const users = await User.find({ role: "user" }); res.status(200).json({ success: true, users, }); }); ================================================ FILE: tshirtstore/index.js ================================================ const app = require("./app"); const connectWithDb = require("./config/db"); require("dotenv").config(); const cloudinary = require("cloudinary"); // connect with databases connectWithDb(); //cloudinary config goes here cloudinary.config({ cloud_name: process.env.CLOUDINARY_NAME, api_key: process.env.CLOUDINARY_API_KEY, api_secret: process.env.CLOUDINARY_API_SECRET, }); app.listen(process.env.PORT, () => { console.log(`Server is running at port: ${process.env.PORT}`); }); ================================================ FILE: tshirtstore/lcotshirtstore/.gitignore ================================================ .env node_modules/ ================================================ FILE: tshirtstore/lcotshirtstore/app.js ================================================ const express = require("express"); require("dotenv").config(); const app = express(); const morgan = require("morgan"); const cookieParser = require("cookie-parser"); const fileUpload = require("express-fileupload"); //for swagger documentation const swaggerUi = require("swagger-ui-express"); const YAML = require("yamljs"); const swaggerDocument = YAML.load("./swagger.yaml"); app.use("/api-docs", swaggerUi.serve, swaggerUi.setup(swaggerDocument)); //regular middleware app.use(express.json()); app.use(express.urlencoded({ extended: true })); //cookies and file middleware app.use(cookieParser()); app.use( fileUpload({ useTempFiles: true, tempFileDir: "/tmp/", }) ); //temp check app.set("view engine", "ejs"); //morgan middleware app.use(morgan("tiny")); //import all routes here const home = require("./routes/home"); const user = require("./routes/user"); const product = require("./routes/product"); const payment = require("./routes/payment"); const order = require("./routes/order"); //router middleware app.use("/api/v1", home); app.use("/api/v1", user); app.use("/api/v1", product); app.use("/api/v1", payment); app.use("/api/v1", order); app.get("/signuptest", (req, res) => { res.render("signuptest"); }); // export app js module.exports = app; ================================================ FILE: tshirtstore/lcotshirtstore/config/db.js ================================================ const mongoose = require("mongoose"); const connectWithDb = () => { mongoose .connect(process.env.DB_URL, { useNewUrlParser: true, useUnifiedTopology: true, }) .then(console.log(`DB GOT CONNECTED`)) .catch((error) => { console.log(`DB CONNECTION ISSUES`); console.log(error); process.exit(1); }); }; module.exports = connectWithDb; ================================================ FILE: tshirtstore/lcotshirtstore/controllers/homeController.js ================================================ const BigPromise = require("../middlewares/bigPromise"); exports.home = BigPromise(async (req, res) => { // const db = await something() res.status(200).json({ success: true, greeting: "Hello from API", }); }); exports.homeDummy = async (req, res) => { try { // const db = await something() res.status(200).json({ success: true, greeting: "this is another dummy route", }); } catch (error) { console.log(error); } }; ================================================ FILE: tshirtstore/lcotshirtstore/controllers/orderController.js ================================================ const Order = require("../models/order"); const Product = require("../models/product"); const BigPromise = require("../middlewares/bigPromise"); const CustomError = require("../utils/customError"); exports.createOrder = BigPromise(async (req, res, next) => { const { shippingInfo, orderItems, paymentInfo, taxAmount, shippingAmount, totalAmount, } = req.body; const order = await Order.create({ shippingInfo, orderItems, paymentInfo, taxAmount, shippingAmount, totalAmount, user: req.user._id, }); res.status(200).json({ success: true, order, }); }); exports.getOneOrder = BigPromise(async (req, res, next) => { const order = await Order.findById(req.params.id).populate( "user", "name email" ); if (!order) { return next(new CustomError("please check order id", 401)); } res.status(200).json({ success: true, order, }); }); exports.getLoggedInOrders = BigPromise(async (req, res, next) => { const order = await Order.find({ user: req.user._id }); if (!order) { return next(new CustomError("please check order id", 401)); } res.status(200).json({ success: true, order, }); }); exports.admingetAllOrders = BigPromise(async (req, res, next) => { const orders = await Order.find(); res.status(200).json({ success: true, orders, }); }); exports.adminUpdateOrder = BigPromise(async (req, res, next) => { const order = await Order.findById(req.params.id); if (order.orderStatus === "Delivered") { return next(new CustomError("Order is already marked for delivered", 401)); } order.orderStatus = req.body.orderStatus; order.orderItems.forEach(async (prod) => { await updateProductStock(prod.product, prod.quantity); }); await order.save(); res.status(200).json({ success: true, orders, }); }); exports.adminDeleteOrder = BigPromise(async (req, res, next) => { const order = await Order.findById(req.params.id); await order.remove(); res.status(200).json({ success: true, }); }); async function updateProductStock(productId, quantity) { const product = await Product.findById(productId); product.stock = product.stock - quantity; await product.save({ validateBeforeSave: false }); } ================================================ FILE: tshirtstore/lcotshirtstore/controllers/paymentController.js ================================================ const BigPromise = require("../middlewares/bigPromise"); const stripe = require("stripe")(process.env.STRIPE_SECRET); exports.sendStripeKey = BigPromise(async (req, res, next) => { res.status(200).json({ stripekey: process.env.STRIPE_API_KEY, }); }); exports.captureStripePayment = BigPromise(async (req, res, next) => { const paymentIntent = await stripe.paymentIntents.create({ amount: req.body.amount, currency: "inr", //optional metadata: { integration_check: "accept_a_payment" }, }); res.status(200).json({ success: true, amount: req.body.amount, client_secret: paymentIntent.client_secret, //you can optionally send id as well }); }); exports.sendRazorpayKey = BigPromise(async (req, res, next) => { res.status(200).json({ stripekey: process.env.RAZORPAY_API_KEY, }); }); exports.captureRazorpayPayment = BigPromise(async (req, res, next) => { var instance = new Razorpay({ key_id: process.env.RAZORPAY_API_KEY, key_secret: process.env.RAZORPAY_SECRET, }); var options = { amount: req.body.amount, // amount in the smallest currency unit currency: "INR", }; const myOrder = await instance.orders.create(options); res.staus(200).json({ success: true, amount: req.body.amount, order: myOrder, }); }); ================================================ FILE: tshirtstore/lcotshirtstore/controllers/productController.js ================================================ const Product = require("../models/product"); const BigPromise = require("../middlewares/bigPromise"); const CustomError = require("../utils/customError"); const cloudinary = require("cloudinary"); const WhereClause = require("../utils/whereClause"); exports.addProduct = BigPromise(async (req, res, next) => { // images let imageArray = []; if (!req.files) { return next(new CustomError("images are required", 401)); } if (req.files) { for (let index = 0; index < req.files.photos.length; index++) { let result = await cloudinary.v2.uploader.upload( req.files.photos[index].tempFilePath, { folder: "products", } ); imageArray.push({ id: result.public_id, secure_url: result.secure_url, }); } } req.body.photos = imageArray; req.body.user = req.user.id; const product = await Product.create(req.body); res.status(200).json({ success: true, product, }); }); exports.getAllProduct = BigPromise(async (req, res, next) => { const resultPerPage = 6; const totalcountProduct = await Product.countDocuments(); const productsObj = new WhereClause(Product.find(), req.query) .search() .filter(); let products = await productsObj.base; const filteredProductNumber = products.length; //products.limit().skip() productsObj.pager(resultPerPage); products = await productsObj.base.clone(); res.status(200).json({ success: true, products, filteredProductNumber, totalcountProduct, }); }); exports.getOneProduct = BigPromise(async (req, res, next) => { const product = await Product.findById(req.params.id); if (!product) { return next(new CustomError("No product found with this id", 401)); } res.status(200).json({ success: true, product, }); }); exports.addReview = BigPromise(async (req, res, next) => { const { rating, comment, productId } = req.body; const review = { user: req.user._id, name: req.user.name, rating: Number(rating), comment, }; const product = await Product.findById(productId); const AlreadyReview = product.reviews.find( (rev) => rev.user.toString() === req.user._id.toString() ); if (AlreadyReview) { product.reviews.forEach((review) => { if (review.user.toString() === req.user._id.toString()) { review.comment = comment; review.rating = rating; } }); } else { product.reviews.push(review); product.numberOfReviews = product.reviews.length; } // adjust ratings product.ratings = product.reviews.reduce((acc, item) => item.rating + acc, 0) / product.reviews.length; //save await product.save({ validateBeforeSave: false }); res.status(200).json({ success: true, }); }); exports.deleteReview = BigPromise(async (req, res, next) => { const { productId } = req.query; const product = await Product.findById(productId); const reviews = product.reviews.filter( (rev) => rev.user.toString() === req.user._id.toString() ); const numberOfReviews = reviews.length; // adjust ratings product.ratings = product.reviews.reduce((acc, item) => item.rating + acc, 0) / product.reviews.length; //update the product await Product.findByIdAndUpdate( productId, { reviews, ratings, numberOfReviews, }, { new: true, runValidators: true, useFindAndModify: false, } ); res.status(200).json({ success: true, }); }); exports.getOnlyReviewsForOneProduct = BigPromise(async (req, res, next) => { const product = await Product.findById(req.query.id); res.status(200).json({ success: true, reviews: product.reviews, }); }); // admin only controllers exports.adminGetAllProduct = BigPromise(async (req, res, next) => { const products = await Product.find(); res.status(200).json({ success: true, products, }); }); exports.adminUpdateOneProduct = BigPromise(async (req, res, next) => { let product = await Product.findById(req.params.id); if (!product) { return next(new CustomError("No product found with this id", 401)); } let imagesArray = []; if (req.files) { //destroy the existing image for (let index = 0; index < product.photos.length; index++) { const res = await cloudinary.v2.uploader.destroy( product.photos[index].id ); } for (let index = 0; index < req.files.photos.length; index++) { let result = await cloudinary.v2.uploader.upload( req.files.photos[index].tempFilePath, { folder: "products", //folder name -> .env } ); imagesArray.push({ id: result.public_id, secure_url: result.secure_url, }); } } req.body.photos = imagesArray; product = await Product.findByIdAndUpdate(req.params.id, req.body, { new: true, runValidators: true, useFindAndModify: false, }); res.status(200).json({ success: true, product, }); }); exports.adminDeleteOneProduct = BigPromise(async (req, res, next) => { const product = await Product.findById(req.params.id); if (!product) { return next(new CustomError("No product found with this id", 401)); } //destroy the existing image for (let index = 0; index < product.photos.length; index++) { const res = await cloudinary.v2.uploader.destroy(product.photos[index].id); } await product.remove(); res.status(200).json({ success: true, message: "Product was deleted !", }); }); ================================================ FILE: tshirtstore/lcotshirtstore/controllers/userController.js ================================================ const User = require("../models/user"); const BigPromise = require("../middlewares/bigPromise"); const CustomError = require("../utils/customError"); const cookieToken = require("../utils/cookieToken"); const cloudinary = require("cloudinary"); const mailHelper = require("../utils/emailHelper"); const crypto = require("crypto"); exports.signup = BigPromise(async (req, res, next) => { //let result; if (!req.files) { return next(new CustomError("photo is required for signup", 400)); } const { name, email, password } = req.body; if (!email || !name || !password) { return next(new CustomError("Name, email and password are required", 400)); } let file = req.files.photo; const result = await cloudinary.v2.uploader.upload(file.tempFilePath, { folder: "users", width: 150, crop: "scale", }); const user = await User.create({ name, email, password, photo: { id: result.public_id, secure_url: result.secure_url, }, }); cookieToken(user, res); }); exports.login = BigPromise(async (req, res, next) => { const { email, password } = req.body; // check for presence of email and password if (!email || !password) { return next(new CustomError("please provide email and password", 400)); } // get user from DB const user = await User.findOne({ email }).select("+password"); // if user not found in DB if (!user) { return next( new CustomError("Email or password does not match or exist", 400) ); } // match the password const isPasswordCorrect = await user.isValidatedPassword(password); //if password do not match if (!isPasswordCorrect) { return next( new CustomError("Email or password does not match or exist", 400) ); } // if all goes good and we send the token cookieToken(user, res); }); exports.logout = BigPromise(async (req, res, next) => { //clear the cookie res.cookie("token", null, { expires: new Date(Date.now()), httpOnly: true, }); //send JSON response for success res.status(200).json({ succes: true, message: "Logout success", }); }); exports.forgotPassword = BigPromise(async (req, res, next) => { // collect email const { email } = req.body; // find user in database const user = await User.findOne({ email }); // if user not found in database if (!user) { return next(new CustomError("Email not found as registered", 400)); } //get token from user model methods const forgotToken = user.getForgotPasswordToken(); // save user fields in DB await user.save({ validateBeforeSave: false }); // create a URL const myUrl = `${req.protocol}://${req.get( "host" )}/api/v1/password/reset/${forgotToken}`; // craft a message const message = `Copy paste this link in your URL and hit enter \n\n ${myUrl}`; // attempt to send email try { await mailHelper({ email: user.email, subject: "LCO TStore - Password reset email", message, }); // json reponse if email is success res.status(200).json({ succes: true, message: "Email sent successfully", }); } catch (error) { // reset user fields if things goes wrong user.forgotPasswordToken = undefined; user.forgotPasswordExpiry = undefined; await user.save({ validateBeforeSave: false }); // send error response return next(new CustomError(error.message, 500)); } }); exports.passwordReset = BigPromise(async (req, res, next) => { //get token from params const token = req.params.token; // hash the token as db also stores the hashed version const encryToken = crypto.createHash("sha256").update(token).digest("hex"); // find user based on hased on token and time in future const user = await User.findOne({ encryToken, forgotPasswordExpiry: { $gt: Date.now() }, }); if (!user) { return next(new CustomError("Token is invalid or expired", 400)); } // check if password and conf password matched if (req.body.password !== req.body.confirmPassword) { return next( new CustomError("password and confirm password do not match", 400) ); } // update password field in DB user.password = req.body.password; // reset token fields user.forgotPasswordToken = undefined; user.forgotPasswordExpiry = undefined; // save the user await user.save(); // send a JSON response OR send token cookieToken(user, res); }); exports.getLoggedInUserDetails = BigPromise(async (req, res, next) => { //req.user will be added by middleware // find user by id const user = await User.findById(req.user.id); //send response and user data res.status(200).json({ success: true, user, }); }); exports.changePassword = BigPromise(async (req, res, next) => { // get user from middleware const userId = req.user.id; // get user from database const user = await User.findById(userId).select("+password"); //check if old password is correct const isCorrectOldPassword = await user.isValidatedPassword( req.body.oldPassword ); if (!isCorrectOldPassword) { return next(new CustomError("old password is incorrect", 400)); } // allow to set new password user.password = req.body.password; // save user and send fresh token await user.save(); cookieToken(user, res); }); exports.updateUserDetails = BigPromise(async (req, res, next) => { // add a check for email and name in body // collect data from body const newData = { name: req.body.name, email: req.body.email, }; // if photo comes to us if (req.files) { const user = await User.findById(req.user.id); const imageId = user.photo.id; // delete photo on cloudinary const resp = await cloudinary.v2.uploader.destroy(imageId); // upload the new photo const result = await cloudinary.v2.uploader.upload( req.files.photo.tempFilePath, { folder: "users", width: 150, crop: "scale", } ); // add photo data in newData object newData.photo = { id: result.public_id, secure_url: result.secure_url, }; } // update the data in user const user = await User.findByIdAndUpdate(req.user.id, newData, { new: true, runValidators: true, useFindAndModify: false, }); res.status(200).json({ success: true, }); }); exports.adminAllUser = BigPromise(async (req, res, next) => { // select all users const users = await User.find(); // send all users res.status(200).json({ success: true, users, }); }); exports.admingetOneUser = BigPromise(async (req, res, next) => { // get id from url and get user from database const user = await User.findById(req.params.id); if (!user) { next(new CustomError("No user found", 400)); } // send user res.status(200).json({ success: true, user, }); }); exports.adminUpdateOneUserDetails = BigPromise(async (req, res, next) => { // add a check for email and name in body // get data from request body const newData = { name: req.body.name, email: req.body.email, role: req.body.role, }; // update the user in database const user = await User.findByIdAndUpdate(req.params.id, newData, { new: true, runValidators: true, useFindAndModify: false, }); res.status(200).json({ success: true, }); }); exports.adminDeleteOneUser = BigPromise(async (req, res, next) => { // get user from url const user = await User.findById(req.params.id); if (!user) { return next(new CustomError("No Such user found", 401)); } // get image id from user in database const imageId = user.photo.id; // delete image from cloudinary await cloudinary.v2.uploader.destroy(imageId); // remove user from databse await user.remove(); res.status(200).json({ success: true, }); }); exports.managerAllUser = BigPromise(async (req, res, next) => { // select the user with role of user const users = await User.find({ role: "user" }); res.status(200).json({ success: true, users, }); }); ================================================ FILE: tshirtstore/lcotshirtstore/index.js ================================================ const app = require("./app"); const connectWithDb = require("./config/db"); require("dotenv").config(); const cloudinary = require("cloudinary"); // connect with databases connectWithDb(); //cloudinary config goes here cloudinary.config({ cloud_name: process.env.CLOUDINARY_NAME, api_key: process.env.CLOUDINARY_API_KEY, api_secret: process.env.CLOUDINARY_API_SECRET, }); app.listen(process.env.PORT, () => { console.log(`Server is running at port: ${process.env.PORT}`); }); ================================================ FILE: tshirtstore/lcotshirtstore/middlewares/bigPromise.js ================================================ // try catch and async - await || use promise module.exports = (func) => (req, res, next) => Promise.resolve(func(req, res, next)).catch(next); ================================================ FILE: tshirtstore/lcotshirtstore/middlewares/user.js ================================================ const User = require("../models/user"); const BigPromise = require("../middlewares/bigPromise"); const CustomError = require("../utils/customError"); const jwt = require("jsonwebtoken"); exports.isLoggedIn = BigPromise(async (req, res, next) => { const token = req.cookies.token || req.header("Authorization").replace("Bearer ", ""); if (!token) { return next(new CustomError("Login first to access this page", 401)); } const decoded = jwt.verify(token, process.env.JWT_SECRET); req.user = await User.findById(decoded.id); next(); }); exports.customRole = (...roles) => { return (req, res, next) => { if (!roles.includes(req.user.role)) { return next(new CustomError("You are not allowed for this resouce", 403)); } next(); }; }; ================================================ FILE: tshirtstore/lcotshirtstore/models/order.js ================================================ const mongoose = require("mongoose"); // shippingInfo{} // user, // paymentInfo{} // taxAmount // ShippingAmount // totalAmount // orderStatus // deliveredAt // createdAt // ------------- // orderItems: [ {} ] // - name // - quantity // - image[0] // - price // - product const orderSchema = new mongoose.Schema({ shippingInfo: { address: { type: String, required: true, }, city: { type: String, required: true, }, phoneNo: { type: String, required: true, }, postalCode: { type: String, required: true, }, state: { type: String, required: true, }, country: { type: String, required: true, }, }, user: { type: mongoose.Schema.ObjectId, //mongoose.Schema.Types.ObjectId ref: "User", required: true, }, orderItems: [ { name: { type: String, required: true, }, quantity: { type: Number, required: true, }, image: { type: String, required: true, }, price: { type: Number, required: true, }, product: { type: mongoose.Schema.ObjectId, //mongoose.Schema.Types.ObjectId ref: "Product", required: true, }, }, ], paymentInfo: { id: { type: String, }, }, taxAmount: { type: Number, required: true, }, shippingAmount: { type: Number, required: true, }, totalAmount: { type: Number, required: true, }, orderStatus: { type: String, required: true, default: "processing", }, deliveredAt: { type: Date, }, createdAt: { type: Date, default: Date.now, }, }); module.exports = mongoose.model("Order", orderSchema); // ref link // https://stackoverflow.com/questions/28997636/should-i-use-schema-types-objectid-or-schema-objectid-when-defining-a-mongoose-s ================================================ FILE: tshirtstore/lcotshirtstore/models/product.js ================================================ const mongoose = require("mongoose"); const productSchema = new mongoose.Schema({ name: { type: String, required: [true, "please provide product name"], trim: true, maxlength: [120, "Product name should not be more than 120 characters"], }, price: { type: Number, required: [true, "please provide product price"], maxlength: [6, "Product price should not be more than 6 digits"], }, description: { type: String, required: [true, "please provide product description"], }, photos: [ { id: { type: String, required: true, }, secure_url: { type: String, required: true, }, }, ], category: { type: String, required: [ true, "please select category from- short-sleeves, long-sleeves, sweat-shirts, hoodies", ], enum: { values: ["shortsleeves", "longsleeves", "sweatshirt", "hoodies"], message: "please select category ONLY from - short-sleeves, long-sleeves, sweat-shirts and hoodies ", }, }, //this field was updated in order videos later stock: { type: Number, required: [true, "please add a number in stock"], }, brand: { type: String, required: [true, "please add a brand for clothing"], }, ratings: { type: Number, default: 0, }, numberOfReviews: { type: Number, default: 0, }, reviews: [ { user: { type: mongoose.Schema.ObjectId, ref: "User", required: true, }, name: { type: String, required: true, }, rating: { type: Number, required: true, }, comment: { type: String, required: true, }, }, ], user: { type: mongoose.Schema.ObjectId, ref: "User", required: true, }, createdAt: { type: Date, default: Date.now, }, }); module.exports = mongoose.model("Product", productSchema); ================================================ FILE: tshirtstore/lcotshirtstore/models/user.js ================================================ const mongoose = require("mongoose"); const validator = require("validator"); const bcrypt = require("bcryptjs"); const jwt = require("jsonwebtoken"); const crypto = require("crypto"); const userSchema = new mongoose.Schema({ name: { type: String, required: [true, "Please provide a name"], maxlength: [40, "Name should be under 40 characters"], }, email: { type: String, required: [true, "Please provide an email"], validate: [validator.isEmail, "Please enter email in correct format"], unique: true, }, password: { type: String, required: [true, "Please provide a password"], minlength: [6, "password should be atleast 6 char"], select: false, }, role: { type: String, default: "user", }, photo: { id: { type: String, required: true, }, secure_url: { type: String, required: true, }, }, forgotPasswordToken: String, forgotPasswordExpiry: Date, createdAt: { type: Date, default: Date.now, }, }); //encrypt password before save - HOOKS userSchema.pre("save", async function (next) { if (!this.isModified("password")) { return next(); } this.password = await bcrypt.hash(this.password, 10); }); // validate the password with passed on user password userSchema.methods.isValidatedPassword = async function (usersendPassword) { return await bcrypt.compare(usersendPassword, this.password); }; //create and return jwt token userSchema.methods.getJwtToken = function () { return jwt.sign({ id: this._id }, process.env.JWT_SECRET, { expiresIn: process.env.JWT_EXPIRY, }); }; //generate forgot password token (string) userSchema.methods.getForgotPasswordToken = function () { // generate a long and randomg string const forgotToken = crypto.randomBytes(20).toString("hex"); // getting a hash - make sure to get a hash on backend this.forgotPasswordToken = crypto .createHash("sha256") .update(forgotToken) .digest("hex"); //time of token this.forgotPasswordExpiry = Date.now() + 20 * 60 * 1000; return forgotToken; }; module.exports = mongoose.model("User", userSchema); ================================================ FILE: tshirtstore/lcotshirtstore/nodemon.json ================================================ { "ext": ".js, .jsx, .yaml" } ================================================ FILE: tshirtstore/lcotshirtstore/package.json ================================================ { "name": "tshirtstore", "version": "1.0.0", "description": "a backend api for tshirt store", "main": "index.js", "scripts": { "start": "node index.js", "dev": "nodemon index.js" }, "keywords": [ "api", "backend" ], "author": "hitesh choudhary", "license": "ISC", "dependencies": { "bcryptjs": "^2.4.3", "cloudinary": "^1.27.1", "cookie-parser": "^1.4.5", "dotenv": "^10.0.0", "ejs": "^3.1.6", "express": "^4.17.1", "express-fileupload": "^1.2.1", "jsonwebtoken": "^8.5.1", "mongoose": "^6.0.12", "morgan": "^1.10.0", "nodemailer": "^6.7.0", "razorpay": "^2.0.7", "stripe": "^8.184.0", "swagger-ui-express": "^4.1.6", "validator": "^13.6.0", "yamljs": "^0.3.0" }, "devDependencies": { "nodemon": "^2.0.14" } } ================================================ FILE: tshirtstore/lcotshirtstore/routes/home.js ================================================ const express = require("express"); const router = express.Router(); const { home, homeDummy } = require("../controllers/homeController"); router.route("/").get(home); router.route("/dummy").get(homeDummy); module.exports = router; ================================================ FILE: tshirtstore/lcotshirtstore/routes/order.js ================================================ const express = require("express"); const { createOrder, getOneOrder, getLoggedInOrders, admingetAllOrders, adminUpdateOrder, adminDeleteOrder, } = require("../controllers/orderController"); const router = express.Router(); const { isLoggedIn, customRole } = require("../middlewares/user"); router.route("/order/create").post(isLoggedIn, createOrder); router.route("/order/:id").get(isLoggedIn, getOneOrder); router.route("/myorder").get(isLoggedIn, getLoggedInOrders); //admin routes router .route("/admin/orders") .get(isLoggedIn, customRole("admin"), admingetAllOrders); router .route("/admin/order/:id") .put(isLoggedIn, customRole("admin"), adminUpdateOrder) .delete(isLoggedIn, customRole("admin"), adminDeleteOrder); module.exports = router; ================================================ FILE: tshirtstore/lcotshirtstore/routes/payment.js ================================================ const express = require("express"); const router = express.Router(); const { sendRazorpayKey, sendStripeKey, captureStripePayment, captureRazorpayPayment, } = require("../controllers/paymentController"); const { isLoggedIn } = require("../middlewares/user"); router.route("/stripekey").get(isLoggedIn, sendStripeKey); router.route("/razorpaykey").get(isLoggedIn, sendRazorpayKey); router.route("/capturestripe").post(isLoggedIn, captureStripePayment); router.route("/capturerazorpay").post(isLoggedIn, captureRazorpayPayment); module.exports = router; ================================================ FILE: tshirtstore/lcotshirtstore/routes/product.js ================================================ const express = require("express"); const { addProduct, getAllProduct, adminGetAllProduct, getOneProduct, adminUpdateOneProduct, adminDeleteOneProduct, addReview, deleteReview, getOnlyReviewsForOneProduct, } = require("../controllers/productController"); const router = express.Router(); const { isLoggedIn, customRole } = require("../middlewares/user"); //user routes router.route("/products").get(getAllProduct); router.route("/product/:id").get(getOneProduct); router.route("/review").put(isLoggedIn, addReview); router.route("/review").delete(isLoggedIn, deleteReview); router.route("/reviews").get(isLoggedIn, getOnlyReviewsForOneProduct); //admin routes router .route("/admin/product/add") .post(isLoggedIn, customRole("admin"), addProduct); router .route("/admin/products") .get(isLoggedIn, customRole("admin"), adminGetAllProduct); router .route("/admin/product/:id") .put(isLoggedIn, customRole("admin"), adminUpdateOneProduct) .delete(isLoggedIn, customRole("admin"), adminDeleteOneProduct); module.exports = router; ================================================ FILE: tshirtstore/lcotshirtstore/routes/user.js ================================================ const express = require("express"); const router = express.Router(); const { signup, login, logout, forgotPassword, passwordReset, getLoggedInUserDetails, changePassword, updateUserDetails, adminAllUser, managerAllUser, admingetOneUser, adminUpdateOneUserDetails, adminDeleteOneUser, } = require("../controllers/userController"); const { isLoggedIn, customRole } = require("../middlewares/user"); router.route("/signup").post(signup); router.route("/login").post(login); router.route("/logout").get(logout); router.route("/forgotPassword").post(forgotPassword); router.route("/password/reset/:token").post(passwordReset); router.route("/userdashboard").get(isLoggedIn, getLoggedInUserDetails); router.route("/password/update").post(isLoggedIn, changePassword); router.route("/userdashboard/update").post(isLoggedIn, updateUserDetails); //admin only routes router.route("/admin/users").get(isLoggedIn, customRole("admin"), adminAllUser); router .route("/admin/user/:id") .get(isLoggedIn, customRole("admin"), admingetOneUser) .put(isLoggedIn, customRole("admin"), adminUpdateOneUserDetails) .delete(isLoggedIn, customRole("admin"), adminDeleteOneUser); // manager only route router .route("/manager/users") .get(isLoggedIn, customRole("manager"), managerAllUser); module.exports = router; ================================================ FILE: tshirtstore/lcotshirtstore/swagger.yaml ================================================ openapi: "3.0.0" info: title: Tshirt store API description: LCO - a course to create API for ecomm store version: 1.1.0 contact: email: hitesh@lco.dev url: "https://lco.dev" # servers: # - url: "https://localhost:4000/api/v1" # description: for local host - secure # - url: "http://localhost:4000/api/v1" # description: for local host - regular servers: - url: "{protocol}://localhost:4000/api/{version}" description: for local host variables: version: enum: - v1 - v2 default: v1 protocol: enum: - http - https default: http components: securitySchemes: cookieAuth: type: apiKey in: cookie name: token BearerAuth: type: http scheme: bearer paths: /dummy: get: tags: - Home summary: returns a greet message from LCO responses: 200: description: All good success content: application/json: schema: type: string example: "mystring" 400: description: Bad request 500: description: internal server error /signup: post: tags: - User summary: signup a new user. required files are - name, email, password and photo requestBody: content: multipart/form-data: schema: type: object required: - name - email - password - photo properties: name: type: string required: true email: type: string password: type: string photo: in: formData description: The uploaded file data type: file format: binary responses: 200: description: All good success content: application/json: schema: type: object properties: success: type: boolean token: type: string user: type: object properties: name: type: string email: type: string role: type: string photo: type: object properties: id: type: string secure_url: type: string _id: type: string createdAt: type: string 400: description: Bad request 500: description: internal server error /login: post: tags: - User summary: login a new user. required files are - email and password. Also add httpOnly cookie requestBody: content: application/json: schema: type: object required: - email - password properties: email: type: string password: type: string responses: 200: description: All good success content: application/json: schema: type: object properties: success: type: boolean token: type: string user: type: object properties: name: type: string email: type: string role: type: string photo: type: object properties: id: type: string secure_url: type: string _id: type: string createdAt: type: string 400: description: Bad request 500: description: internal server error /logout: get: tags: - User summary: get request to logout the user. Also removes httpOnly cookies responses: 200: description: All good success content: application/json: schema: type: object properties: success: type: boolean message: type: string 400: description: Bad request 500: description: internal server error /forgotPassword: post: tags: - User summary: sends an email with link to forgot password. Contains the token for user validation requestBody: content: application/json: schema: type: object required: - email properties: email: type: string responses: 200: description: All good success content: application/json: schema: type: object properties: success: type: boolean message: type: string 400: description: Bad request 500: description: internal server error /password/reset/{token}: post: tags: - User summary: Allows user to reset password, validated based on token. send password and confirm password fields parameters: - name: token in: path required: true schema: type: string requestBody: content: application/json: schema: type: object required: - password - confirmPassword properties: password: type: string confirmPassword: type: string responses: 200: description: All good success content: application/json: schema: type: object properties: success: type: boolean token: type: string user: type: object properties: name: type: string email: type: string role: type: string photo: type: object properties: id: type: string secure_url: type: string _id: type: string createdAt: type: string 400: description: Bad request 500: description: internal server error /userdashboard: get: tags: - User summary: Gets all details about logged in user. Send token in cookies as named token or send Bearer Auth requestBody: responses: 200: description: All good success content: application/json: schema: type: object properties: success: type: boolean user: type: object properties: name: type: string email: type: string role: type: string photo: type: object properties: id: type: string secure_url: type: string _id: type: string createdAt: type: string 400: description: Bad request 500: description: internal server error /password/update: post: tags: - User summary: User can update the password if he is logged in. Send oldPassword and password requestBody: content: application/json: schema: type: object required: - oldPassword - password properties: oldPassword: type: string password: type: string responses: 200: description: All good success content: application/json: schema: type: object properties: token: type: string user: type: object properties: name: type: string email: type: string role: type: string photo: type: object properties: id: type: string secure_url: type: string _id: type: string createdAt: type: string 400: description: Bad request 500: description: internal server error /userdashboard/update: post: tags: - User summary: User can update the name, emails and photo. Photo is optional requestBody: content: multipart/form-data: schema: type: object required: - name - email properties: name: type: string email: type: string photo: in: formData description: The uploaded file data type: file format: binary responses: 200: description: All good success content: application/json: schema: type: object properties: success: type: boolean 400: description: Bad request 500: description: internal server error /admin/users: get: tags: - Admin summary: If user is admin, response will get array of all users requestBody: responses: 200: description: All good success content: application/json: schema: type: array items: type: object properties: user: type: object properties: name: type: string email: type: string role: type: string photo: type: object properties: id: type: string secure_url: type: string _id: type: string createdAt: type: string 400: description: Bad request 500: description: internal server error /admin/user/{id}: get: tags: - Admin summary: If user is admin, response will get details of 1 user parameters: - name: id in: path required: true schema: type: string requestBody: responses: 200: description: All good success content: application/json: schema: type: object properties: user: type: object properties: name: type: string email: type: string role: type: string photo: type: object properties: id: type: string secure_url: type: string _id: type: string createdAt: type: string 400: description: Bad request 500: description: internal server error put: tags: - Admin summary: If user is admin, response will get details of 1 user parameters: - name: id in: path required: true schema: type: string - name: name in: formData required: true schema: type: string - name: email in: formData required: true schema: type: string - name: role in: formData required: true schema: type: string enum: [user, admin, manager] requestBody: responses: 200: description: All good success content: application/json: schema: type: object properties: success: type: boolean 400: description: Bad request 500: description: internal server error delete: tags: - Admin summary: If user is admin, delete the user with given id parameters: - name: id in: path required: true schema: type: string requestBody: responses: 200: description: All good success content: application/json: schema: type: object properties: success: type: boolean 400: description: Bad request 500: description: internal server error /manager/users: get: tags: - Manager summary: If manager, response will get array of all users whose role is user requestBody: responses: 200: description: All good success content: application/json: schema: type: array items: type: object properties: user: type: object properties: name: type: string email: type: string role: type: string photo: type: object properties: id: type: string secure_url: type: string _id: type: string createdAt: type: string 400: description: Bad request 500: description: internal server error ================================================ FILE: tshirtstore/lcotshirtstore/utils/cookieToken.js ================================================ const cookieToken = (user, res) => { const token = user.getJwtToken(); const options = { expires: new Date( Date.now() + process.env.COOKIE_TIME * 24 * 60 * 60 * 1000 ), httpOnly: true, }; user.password = undefined; res.status(200).cookie("token", token, options).json({ success: true, token, user, }); }; module.exports = cookieToken; ================================================ FILE: tshirtstore/lcotshirtstore/utils/customError.js ================================================ class CustomError extends Error { constructor(message, code) { super(message); this.code = code; } } module.exports = CustomError; ================================================ FILE: tshirtstore/lcotshirtstore/utils/demotest.js ================================================ // User.find({ qty: { $lte: 20 } }) // /api/v1/product?search=coder&page=2&category=shortsleeves&rating[gte]=4 // &price[lte]=999&price[gte]=199 // rating: { $gte: '4' } // URL - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace // const p = 'gte gte lte mygte'; // const regex = /\b(gte|lte)\b/g; // console.log(p.replace(regex, m => `$${m}`)); ================================================ FILE: tshirtstore/lcotshirtstore/utils/emailHelper.js ================================================ const nodemailer = require("nodemailer"); const mailHelper = async (option) => { const transporter = nodemailer.createTransport({ host: process.env.SMTP_HOST, port: process.env.SMTP_PORT, auth: { user: process.env.SMTP_USER, // generated ethereal user pass: process.env.SMTP_PASS, // generated ethereal password }, }); const message = { from: "hitesh@lco.dev", // sender address to: option.email, // list of receivers subject: option.subject, // Subject line text: option.message, // plain text body }; // send mail with defined transport object await transporter.sendMail(message); }; module.exports = mailHelper; ================================================ FILE: tshirtstore/lcotshirtstore/utils/testOrder.json ================================================ { "shippingInfo": { "address": "1 Jaipur", "city": "Jaipur", "phoneNo": "9898989898", "postalCode": "302020", "state": "Rajasthan", "country": "India" }, "orderItems": [ { "name": "Pro Coder tshirts", "quantity": 1, "image": "https://res.cloudinary.com/dk92l1yoc/image/upload/v1635757229/products/b4laryk4dbp6vdrvq3wv.png", "price": 999, "product": "617facb5333fd4c0fdfdee65" } ], "paymentInfo": { "id": "testString" }, "taxAmount": 40, "shippingAmount": 10, "totalAmount": 100 } ================================================ FILE: tshirtstore/lcotshirtstore/utils/whereClause.js ================================================ // base - Product.find() // base - Product.find(email: {"hitesh@lco.dev"}) //bigQ - //search=coder&page=2&category=shortsleeves&rating[gte]=4 // &price[lte]=999&price[gte]=199&limit=5 class WhereClause { constructor(base, bigQ) { this.base = base; this.bigQ = bigQ; } search() { const searchword = this.bigQ.search ? { name: { $regex: this.bigQ.search, $options: "i", }, } : {}; this.base = this.base.find({ ...searchword }); return this; } filter() { const copyQ = { ...this.bigQ }; delete copyQ["search"]; delete copyQ["limit"]; delete copyQ["page"]; //convert bigQ into a string => copyQ let stringOfCopyQ = JSON.stringify(copyQ); stringOfCopyQ = stringOfCopyQ.replace( /\b(gte|lte|gt|lt)\b/g, (m) => `$${m}` ); const jsonOfCopyQ = JSON.parse(stringOfCopyQ); this.base = this.base.find(jsonOfCopyQ); return this; } pager(resultperPage) { let currentPage = 1; if (this.bigQ.page) { currentPage = this.bigQ.page; } const skipVal = resultperPage * (currentPage - 1); this.base = this.base.limit(resultperPage).skip(skipVal); return this; } } module.exports = WhereClause; ================================================ FILE: tshirtstore/lcotshirtstore/views/signuptest.ejs ================================================ GET form

POST form

================================================ FILE: tshirtstore/middlewares/bigPromise.js ================================================ // try catch and async - await || use promise module.exports = (func) => (req, res, next) => Promise.resolve(func(req, res, next)).catch(next); ================================================ FILE: tshirtstore/middlewares/productionError.js ================================================ const CustomError = require("../utils/customError"); module.exports = (err, req, res, next) => { let error = { ...err }; error.message = err.message; res.status(error.statusCode || 500).json({ success: false, message: error.message || "Internal Server Error", }); }; ================================================ FILE: tshirtstore/middlewares/user.js ================================================ const User = require("../models/user"); const BigPromise = require("../middlewares/bigPromise"); const CustomError = require("../utils/customError"); const jwt = require("jsonwebtoken"); exports.isLoggedIn = BigPromise(async (req, res, next) => { // const token = req.cookies.token || req.header("Authorization").replace("Bearer ", ""); // check token first in cookies let token = req.cookies.token; // if token not found in cookies, check if header contains Auth field if (!token && req.header("Authorization")) { token = req.header("Authorization").replace("Bearer ", ""); } if (!token) { return next(new CustomError("Login first to access this page", 401)); } const decoded = jwt.verify(token, process.env.JWT_SECRET); req.user = await User.findById(decoded.id); next(); }); exports.customRole = (...roles) => { return (req, res, next) => { if (!roles.includes(req.user.role)) { return next(new CustomError("You are not allowed for this resouce", 403)); } console.log(req.user.role); next(); }; }; ================================================ FILE: tshirtstore/models/order.js ================================================ const mongoose = require("mongoose"); // shippingInfo{} // user, // paymentInfo{} // taxAmount // ShippingAmount // totalAmount // orderStatus // deliveredAt // createdAt // ------------- // orderItems: [ {} ] // - name // - quantity // - image[0] // - price // - product const orderSchema = new mongoose.Schema({ shippingInfo: { address: { type: String, required: true, }, city: { type: String, required: true, }, phoneNo: { type: String, required: true, }, postalCode: { type: String, required: true, }, state: { type: String, // required: true, }, country: { type: String, required: true, }, }, user: { type: mongoose.Schema.ObjectId, //mongoose.Schema.Types.ObjectId ref: "User", required: true, }, orderItems: [ { name: { type: String, required: true, }, quantity: { type: Number, required: true, }, image: { type: String, required: true, }, price: { type: Number, required: true, }, product: { type: mongoose.Schema.ObjectId, //mongoose.Schema.Types.ObjectId ref: "Product", required: true, }, }, ], paymentInfo: { id: { type: String, }, }, taxAmount: { type: Number, required: true, }, shippingAmount: { type: Number, required: true, }, totalAmount: { type: Number, required: true, }, orderStatus: { type: String, required: true, default: "processing", }, deliveredAt: { type: Date, }, createdAt: { type: Date, default: Date.now, }, }); module.exports = mongoose.model("Order", orderSchema); // ref link // https://stackoverflow.com/questions/28997636/should-i-use-schema-types-objectid-or-schema-objectid-when-defining-a-mongoose-s ================================================ FILE: tshirtstore/models/product.js ================================================ const mongoose = require("mongoose"); const productSchema = new mongoose.Schema({ name: { type: String, required: [true, "please provide product name"], trim: true, maxlength: [120, "Product name should not be more than 120 characters"], }, price: { type: Number, required: [true, "please provide product price"], maxlength: [6, "Product price should not be more than 6 digits"], }, description: { type: String, required: [true, "please provide product description"], }, photos: [ { id: { type: String, required: true, }, secure_url: { type: String, required: true, }, }, ], category: { type: String, required: [ true, "please select category from- short-sleeves, long-sleeves, sweat-shirts, hoodies", ], enum: { values: ["shortsleeves", "longsleeves", "sweatshirt", "hoodies"], message: "please select category ONLY from - short-sleeves, long-sleeves, sweat-shirts and hoodies ", }, }, //this field was updated in order videos later stock: { type: Number, required: [true, "please add a number in stock"], }, brand: { type: String, required: [true, "please add a brand for clothing"], }, ratings: { type: Number, default: 0, }, numberOfReviews: { type: Number, default: 0, }, reviews: [ { user: { type: mongoose.Schema.ObjectId, ref: "User", required: true, }, name: { type: String, required: true, }, rating: { type: Number, required: true, }, comment: { type: String, required: true, }, }, ], user: { type: mongoose.Schema.ObjectId, ref: "User", required: true, }, createdAt: { type: Date, default: Date.now, }, }); module.exports = mongoose.model("Product", productSchema); ================================================ FILE: tshirtstore/models/user.js ================================================ const mongoose = require("mongoose"); const validator = require("validator"); const bcrypt = require("bcryptjs"); const jwt = require("jsonwebtoken"); const crypto = require("crypto"); const userSchema = new mongoose.Schema({ name: { type: String, required: [true, "Please provide a name"], maxlength: [40, "Name should be under 40 characters"], }, email: { type: String, required: [true, "Please provide an email"], validate: [validator.isEmail, "Please enter email in correct format"], unique: true, }, password: { type: String, required: [true, "Please provide a password"], minlength: [6, "password should be atleast 6 char"], select: false, }, role: { type: String, default: "user", }, photo: { id: { type: String, required: true, }, secure_url: { type: String, required: true, }, }, forgotPasswordToken: String, forgotPasswordExpiry: Date, createdAt: { type: Date, default: Date.now, }, }); //encrypt password before save - HOOKS userSchema.pre("save", async function (next) { if (!this.isModified("password")) { return next(); } this.password = await bcrypt.hash(this.password, 10); }); // validate the password with passed on user password userSchema.methods.isValidatedPassword = async function (usersendPassword) { return await bcrypt.compare(usersendPassword, this.password); }; //create and return jwt token userSchema.methods.getJwtToken = function () { return jwt.sign({ id: this._id }, process.env.JWT_SECRET, { expiresIn: process.env.JWT_EXPIRY, }); }; //generate forgot password token (string) userSchema.methods.getForgotPasswordToken = function () { // generate a long and randomg string const forgotToken = crypto.randomBytes(20).toString("hex"); // getting a hash - make sure to get a hash on backend this.forgotPasswordToken = crypto .createHash("sha256") .update(forgotToken) .digest("hex"); //time of token this.forgotPasswordExpiry = Date.now() + 20 * 60 * 1000; return forgotToken; }; module.exports = mongoose.model("User", userSchema); ================================================ FILE: tshirtstore/nodemon.json ================================================ { "ext": ".js, .jsx, .yaml" } ================================================ FILE: tshirtstore/package.json ================================================ { "name": "tshirtstore", "version": "1.0.0", "description": "a backend api for tshirt store", "main": "index.js", "scripts": { "start": "node index.js", "dev": "nodemon index.js" }, "keywords": [ "api", "backend" ], "author": "hitesh choudhary", "license": "ISC", "dependencies": { "bcryptjs": "^2.4.3", "cloudinary": "^1.27.1", "cookie-parser": "^1.4.5", "dotenv": "^10.0.0", "ejs": "^3.1.6", "express": "^4.17.1", "express-fileupload": "^1.2.1", "jsonwebtoken": "^8.5.1", "mongoose": "^6.0.12", "morgan": "^1.10.0", "nodemailer": "^6.7.0", "razorpay": "^2.0.7", "stripe": "^8.184.0", "swagger-ui-express": "^4.1.6", "validator": "^13.6.0", "yamljs": "^0.3.0" }, "devDependencies": { "nodemon": "^2.0.14" } } ================================================ FILE: tshirtstore/routes/home.js ================================================ const express = require("express"); const router = express.Router(); const { home, homeDummy } = require("../controllers/homeController"); router.route("/").get(home); router.route("/dummy").get(homeDummy); module.exports = router; ================================================ FILE: tshirtstore/routes/order.js ================================================ const express = require("express"); const { createOrder, getOneOrder, getLoggedInOrders, admingetAllOrders, adminUpdateOrder, adminDeleteOrder, } = require("../controllers/orderController"); const router = express.Router(); const { isLoggedIn, customRole } = require("../middlewares/user"); router.route("/order/create").post(isLoggedIn, createOrder); router.route("/order/:id").get(isLoggedIn, getOneOrder); router.route("/myorder").get(isLoggedIn, getLoggedInOrders); //admin routes router .route("/admin/orders") .get(isLoggedIn, customRole("admin"), admingetAllOrders); router .route("/admin/order/:id") .put(isLoggedIn, customRole("admin"), adminUpdateOrder) .delete(isLoggedIn, customRole("admin"), adminDeleteOrder); module.exports = router; ================================================ FILE: tshirtstore/routes/payment.js ================================================ const express = require("express"); const router = express.Router(); const { sendRazorpayKey, sendStripeKey, captureStripePayment, captureRazorpayPayment, } = require("../controllers/paymentController"); const { isLoggedIn } = require("../middlewares/user"); router.route("/stripekey").get(isLoggedIn, sendStripeKey); router.route("/razorpaykey").get(isLoggedIn, sendRazorpayKey); router.route("/capturestripe").post(isLoggedIn, captureStripePayment); router.route("/capturerazorpay").post(isLoggedIn, captureRazorpayPayment); module.exports = router; ================================================ FILE: tshirtstore/routes/product.js ================================================ const express = require("express"); const { addProduct, getAllProduct, adminGetAllProduct, getOneProduct, adminUpdateOneProduct, adminDeleteOneProduct, addReview, deleteReview, getOnlyReviewsForOneProduct, } = require("../controllers/productController"); const router = express.Router(); const { isLoggedIn, customRole } = require("../middlewares/user"); //user routes router.route("/products").get(getAllProduct); router.route("/product/:id").get(getOneProduct); router.route("/review").put(isLoggedIn, addReview); router.route("/review").delete(isLoggedIn, deleteReview); router.route("/reviews").get(isLoggedIn, getOnlyReviewsForOneProduct); //admin routes router .route("/admin/product/add") .post(isLoggedIn, customRole("admin"), addProduct); router .route("/admin/products") .get(isLoggedIn, customRole("admin"), adminGetAllProduct); router .route("/admin/product/:id") .put(isLoggedIn, customRole("admin"), adminUpdateOneProduct) .delete(isLoggedIn, customRole("admin"), adminDeleteOneProduct); module.exports = router; ================================================ FILE: tshirtstore/routes/user.js ================================================ const express = require("express"); const router = express.Router(); const { signup, login, logout, forgotPassword, passwordReset, getLoggedInUserDetails, changePassword, updateUserDetails, adminAllUser, managerAllUser, admingetOneUser, adminUpdateOneUserDetails, adminDeleteOneUser, } = require("../controllers/userController"); const { isLoggedIn, customRole } = require("../middlewares/user"); router.route("/signup").post(signup); router.route("/login").post(login); router.route("/logout").get(logout); router.route("/forgotPassword").post(forgotPassword); router.route("/password/reset/:token").put(passwordReset); router.route("/userdashboard").get(isLoggedIn, getLoggedInUserDetails); router.route("/password/update").put(isLoggedIn, changePassword); router.route("/userdashboard/update").put(isLoggedIn, updateUserDetails); //admin only routes router.route("/admin/users").get(isLoggedIn, customRole("admin"), adminAllUser); router .route("/admin/user/:id") .get(isLoggedIn, customRole("admin"), admingetOneUser) .put(isLoggedIn, customRole("admin"), adminUpdateOneUserDetails) .delete(isLoggedIn, customRole("admin"), adminDeleteOneUser); // manager only route router .route("/manager/users") .get(isLoggedIn, customRole("manager"), managerAllUser); module.exports = router; ================================================ FILE: tshirtstore/swagger.yaml ================================================ openapi: "3.0.0" info: title: Tshirt store API description: LCO - a course to create API for ecomm store version: 1.1.0 contact: email: hitesh@lco.dev url: "https://lco.dev" # servers: # - url: "https://localhost:4000/api/v1" # description: for local host - secure # - url: "http://localhost:4000/api/v1" # description: for local host - regular servers: - url: "{protocol}://{url}/api/{version}" description: for local host variables: version: enum: - v1 - v2 default: v1 url: enum: - localhost:4000 - lcotshirtstore.herokuapp.com default: lcotshirtstore.herokuapp.com protocol: enum: - http - https default: https components: securitySchemes: cookieAuth: type: apiKey in: cookie name: token BearerAuth: type: http scheme: bearer paths: /dummy: get: tags: - Home summary: returns a greet message from LCO responses: 200: description: All good success content: application/json: schema: type: string example: "mystring" 400: description: Bad request 500: description: internal server error /signup: post: tags: - User summary: signup a new user. required files are - name, email, password and photo requestBody: content: multipart/form-data: schema: type: object required: - name - email - password - photo properties: name: type: string required: true email: type: string password: type: string photo: in: formData description: The uploaded file data type: file format: binary responses: 200: description: All good success content: application/json: schema: type: object properties: success: type: boolean token: type: string user: type: object properties: name: type: string email: type: string role: type: string photo: type: object properties: id: type: string secure_url: type: string _id: type: string createdAt: type: string 400: description: Bad request 500: description: internal server error /login: post: tags: - User summary: login a new user. required files are - email and password. Also adds httpOnly cookie in browser. It sends token in response too. requestBody: content: application/json: schema: type: object required: - email - password properties: email: type: string password: type: string responses: 200: description: All good success content: application/json: schema: type: object properties: success: type: boolean token: type: string user: type: object properties: name: type: string email: type: string role: type: string photo: type: object properties: id: type: string secure_url: type: string _id: type: string createdAt: type: string 400: description: Bad request 500: description: internal server error /logout: get: tags: - User summary: get request to logout the user. Also removes httpOnly cookies responses: 200: description: All good success content: application/json: schema: type: object properties: success: type: boolean message: type: string 400: description: Bad request 500: description: internal server error /forgotPassword: post: tags: - User summary: sends an email with link to forgot password. Contains the token for user validation. Public hosted version will not send email to your account. Use your own SMTP details to access this feature on local project requestBody: content: application/json: schema: type: object required: - email properties: email: type: string responses: 200: description: All good success content: application/json: schema: type: object properties: success: type: boolean message: type: string 400: description: Bad request 500: description: internal server error /password/reset/{token}: post: tags: - User summary: Allows user to reset password, validated based on token. send password and confirm password fields parameters: - name: token in: path required: true schema: type: string requestBody: content: application/json: schema: type: object required: - password - confirmPassword properties: password: type: string confirmPassword: type: string responses: 200: description: All good success content: application/json: schema: type: object properties: success: type: boolean token: type: string user: type: object properties: name: type: string email: type: string role: type: string photo: type: object properties: id: type: string secure_url: type: string _id: type: string createdAt: type: string 400: description: Bad request 500: description: internal server error /userdashboard: get: tags: - User summary: Gets all details about logged in user. Send token in cookies as named token or send Bearer Auth requestBody: responses: 200: description: All good success content: application/json: schema: type: object properties: success: type: boolean user: type: object properties: name: type: string email: type: string role: type: string photo: type: object properties: id: type: string secure_url: type: string _id: type: string createdAt: type: string 400: description: Bad request 500: description: internal server error /password/update: post: tags: - User summary: User can update the password if he is logged in. Send oldPassword and password requestBody: content: application/json: schema: type: object required: - oldPassword - password properties: oldPassword: type: string password: type: string responses: 200: description: All good success content: application/json: schema: type: object properties: token: type: string user: type: object properties: name: type: string email: type: string role: type: string photo: type: object properties: id: type: string secure_url: type: string _id: type: string createdAt: type: string 400: description: Bad request 500: description: internal server error /userdashboard/update: post: tags: - User summary: User can update the name, emails and photo. Photo is optional requestBody: content: multipart/form-data: schema: type: object required: - name - email properties: name: type: string email: type: string photo: in: formData description: The uploaded file data type: file format: binary responses: 200: description: All good success content: application/json: schema: type: object properties: success: type: boolean 400: description: Bad request 500: description: internal server error /admin/users: get: tags: - Admin summary: If user is admin, response will get array of all users requestBody: responses: 200: description: All good success content: application/json: schema: type: array items: type: object properties: user: type: object properties: name: type: string email: type: string role: type: string photo: type: object properties: id: type: string secure_url: type: string _id: type: string createdAt: type: string 400: description: Bad request 500: description: internal server error /admin/user/{id}: get: tags: - Admin summary: If user is admin, response will get details of 1 user parameters: - name: id in: path required: true schema: type: string requestBody: responses: 200: description: All good success content: application/json: schema: type: object properties: user: type: object properties: name: type: string email: type: string role: type: string photo: type: object properties: id: type: string secure_url: type: string _id: type: string createdAt: type: string 400: description: Bad request 500: description: internal server error put: tags: - Admin summary: If user is admin, he can update mentioned fields in user parameters: - name: id in: path required: true schema: type: string - name: name in: formData required: true schema: type: string - name: email in: formData required: true schema: type: string - name: role in: formData required: true schema: type: string enum: [user, admin, manager] requestBody: responses: 200: description: All good success content: application/json: schema: type: object properties: success: type: boolean 400: description: Bad request 500: description: internal server error delete: tags: - Admin summary: If user is admin, delete the user with given id parameters: - name: id in: path required: true schema: type: string requestBody: responses: 200: description: All good success content: application/json: schema: type: object properties: success: type: boolean 400: description: Bad request 500: description: internal server error /manager/users: get: tags: - Manager summary: If manager, response will get array of all users whose role is user requestBody: responses: 200: description: All good success content: application/json: schema: type: array items: type: object properties: user: type: object properties: name: type: string email: type: string role: type: string photo: type: object properties: id: type: string secure_url: type: string _id: type: string createdAt: type: string 400: description: Bad request 500: description: internal server error /products: get: tags: - Product summary: A simple get request will give you all available products requestBody: responses: 200: description: All good success content: application/json: schema: type: object properties: success: type: boolean filteredProductNumber: type: number totalcountProduct: type: number products: type: array items: type: object properties: _id: type: string name: type: string price: type: number description: type: string category: type: string brand: type: string ratings: type: number numberOfReviews: type: number user: type: string photos: type: array items: type: object properties: id: type: string secure_url: type: string reviews: type: array items: type: object properties: id: type: string comment: type: string user: type: string name: type: string rating: type: number 400: description: Bad request 500: description: internal server error /product/{id}: get: tags: - Product summary: Get a single product parameters: - name: id in: path required: true schema: type: string requestBody: responses: 200: description: All good success content: application/json: schema: type: object properties: success: type: boolean product: type: object properties: _id: type: string name: type: string price: type: number description: type: string category: type: string brand: type: string ratings: type: number numberOfReviews: type: number user: type: string photos: type: array items: type: object properties: id: type: string secure_url: type: string reviews: type: array items: type: object properties: id: type: string comment: type: string user: type: string name: type: string rating: type: number 400: description: Bad request 500: description: internal server error /admin/products: get: tags: - Admin summary: If user is admin, he get all products requestBody: responses: 200: description: All good success content: application/json: schema: type: object properties: success: type: boolean products: type: array items: type: object properties: _id: type: string name: type: string price: type: number description: type: string category: type: string brand: type: string ratings: type: number numberOfReviews: type: number user: type: string photos: type: array items: type: object properties: id: type: string secure_url: type: string reviews: type: array items: type: object properties: id: type: string comment: type: string user: type: string name: type: string rating: type: number 400: description: Bad request 500: description: internal server error /admin/product/add: post: tags: - Admin summary: If admin, Add a product requestBody: content: multipart/form-data: schema: type: object required: - name - price - description - category - stock - brand - photos properties: name: type: string required: true price: type: number required: true description: type: string required: true category: type: string required: true enum: [shortsleeves, longsleeves, sweatshirt, hoodies] stock: type: number required: true brand: type: string required: true photos: in: formData description: The uploaded photos type: array items: type: file format: binary responses: 200: description: All good success content: application/json: schema: type: object properties: success: type: boolean product: type: object properties: _id: type: string name: type: string price: type: number description: type: string category: type: string brand: type: string ratings: type: number numberOfReviews: type: number user: type: string photos: type: array items: type: object properties: id: type: string secure_url: type: string reviews: type: array items: type: object properties: id: type: string comment: type: string user: type: string name: type: string rating: type: number 400: description: Bad request 500: description: internal server error /admin/product/{id}: put: tags: - Admin summary: If admin, update the product. photos are optional to pass. Rest all body is set to be updated to what you pass here. parameters: - name: id in: path required: true schema: type: string requestBody: content: multipart/form-data: schema: type: object required: - name - price - description - category - stock - brand properties: name: type: string required: true price: type: number required: true description: type: number required: true category: type: string required: true enum: [shortsleeves, longsleeves, sweatshirt, hoodies] stock: type: number required: true brand: type: string required: true photos: in: formData description: The uploaded photos type: file format: binary responses: 200: description: All good success content: application/json: schema: type: object properties: success: type: boolean product: type: object properties: _id: type: string name: type: string price: type: number description: type: string category: type: string brand: type: string ratings: type: number numberOfReviews: type: number user: type: string photos: type: array items: type: object properties: id: type: string secure_url: type: string reviews: type: array items: type: object properties: id: type: string comment: type: string user: type: string name: type: string rating: type: number 400: description: Bad request 500: description: internal server error delete: tags: - Admin summary: If admin, pass the id of product and product will be removed parameters: - name: id in: path required: true schema: type: string requestBody: responses: 200: description: All good success content: application/json: schema: type: object properties: success: type: boolean message: type: string 400: description: Bad request 500: description: internal server error /reviews: get: tags: - Product summary: A simple get request will give you all reviews for given products parameters: - name: id in: query description: id of the product required: true schema: type: string requestBody: responses: 200: description: All good success content: application/json: schema: type: object properties: success: type: boolean reviews: type: array items: type: object properties: user: type: string name: type: string rating: type: number comment: type: string _id: type: string 400: description: Bad request 500: description: internal server error /review: put: tags: - Product summary: A logged in user can post a review on any product. If review is already posted, it will just update the existing review parameters: requestBody: content: application/json: schema: type: object required: - rating - comment - productId properties: rating: type: number required: true comment: type: string required: true productId: type: string required: true responses: 200: description: All good success content: application/json: schema: type: object properties: success: type: boolean reviews: type: array items: type: object properties: user: type: string name: type: string rating: type: number comment: type: string _id: type: string 400: description: Bad request 500: description: internal server error delete: tags: - Product summary: Delete the review of logged in user on given product id. parameters: - name: productId in: query required: true schema: type: string requestBody: content: application/json: schema: type: object required: - rating - comment - productId properties: rating: type: number required: true comment: type: string required: true productId: type: string required: true responses: 200: description: All good success content: application/json: schema: type: object properties: success: type: boolean reviews: type: array items: type: object properties: user: type: string name: type: string rating: type: number comment: type: string _id: type: string 400: description: Bad request 500: description: internal server error /order/create: post: tags: - Order summary: create a new order with given details parameters: requestBody: content: application/json: schema: type: object required: - shippingInfo - paymentInfo - taxAmount - shippingAmount - totalAmount - orderItems properties: shippingInfo: type: object properties: address: type: string city: type: string phoneNo: type: string postalCode: type: string state: type: string country: type: string paymentInfo: type: object properties: id: type: string taxAmount: type: number shippingAmount: type: number totalAmount: type: number orderItems: type: array items: type: object properties: name: type: string quantity: type: string image: type: string price: type: number product: type: string description: id of product responses: 200: description: All good success content: application/json: schema: type: object properties: success: type: boolean order: type: object properties: _id: type: string user: type: string orderStatus: type: string createdAt: type: string shippingInfo: type: object properties: address: type: string city: type: string phoneNo: type: string postalCode: type: string state: type: string country: type: string paymentInfo: type: object properties: id: type: string taxAmount: type: number shippingAmount: type: number totalAmount: type: number orderItems: type: array items: type: object properties: name: type: string quantity: type: string image: type: string price: type: number product: type: string description: id of product 400: description: Bad request 500: description: internal server error /order/{id}: get: tags: - Order summary: get details about a order with order id parameters: - name: id in: path required: true description: id of the order schema: type: string requestBody: responses: 200: description: All good success content: application/json: schema: type: object properties: success: type: boolean order: type: object properties: _id: type: string user: type: object properties: name: type: string email: type: string id: type: string orderStatus: type: string createdAt: type: string shippingInfo: type: object properties: address: type: string city: type: string phoneNo: type: string postalCode: type: string state: type: string country: type: string paymentInfo: type: object properties: id: type: string taxAmount: type: number shippingAmount: type: number totalAmount: type: number orderItems: type: array items: type: object properties: name: type: string quantity: type: string image: type: string price: type: number product: type: string description: id of product 400: description: Bad request 500: description: internal server error /myorder: get: tags: - Order summary: get order of logged in user requestBody: responses: 200: description: All good success content: application/json: schema: type: object properties: success: type: boolean order: type: object properties: _id: type: string user: type: object properties: name: type: string email: type: string id: type: string orderStatus: type: string createdAt: type: string shippingInfo: type: object properties: address: type: string city: type: string phoneNo: type: string postalCode: type: string state: type: string country: type: string paymentInfo: type: object properties: id: type: string taxAmount: type: number shippingAmount: type: number totalAmount: type: number orderItems: type: array items: type: object properties: name: type: string quantity: type: string image: type: string price: type: number product: type: string description: id of product 400: description: Bad request 500: description: internal server error /admin/orders: get: tags: - Admin summary: if admin, get list of all orders requestBody: responses: 200: description: All good success content: application/json: schema: type: object properties: success: type: boolean orders: type: array items: properties: _id: type: string user: type: string orderStatus: type: string createdAt: type: string shippingInfo: type: object properties: address: type: string city: type: string phoneNo: type: string postalCode: type: string state: type: string country: type: string paymentInfo: type: object properties: id: type: string taxAmount: type: number shippingAmount: type: number totalAmount: type: number orderItems: type: array items: type: object properties: name: type: string quantity: type: string image: type: string price: type: number product: type: string description: id of product 400: description: Bad request 500: description: internal server error /admin/order/{id}: put: tags: - Admin summary: if admin, update the status of the order parameters: - name: id in: path required: true description: id of the order schema: type: string requestBody: content: multipart/form-data: schema: type: object required: - orderStatus properties: orderStatus: type: string enum: [Processing, Delivered] required: true responses: 200: description: All good success content: application/json: schema: type: object properties: success: type: boolean order: type: object properties: _id: type: string user: type: string orderStatus: type: string createdAt: type: string shippingInfo: type: object properties: address: type: string city: type: string phoneNo: type: string postalCode: type: string state: type: string country: type: string paymentInfo: type: object properties: id: type: string taxAmount: type: number shippingAmount: type: number totalAmount: type: number orderItems: type: array items: type: object properties: name: type: string quantity: type: string image: type: string price: type: number product: type: string description: id of product 400: description: Bad request 500: description: internal server error delete: tags: - Admin summary: if admin, delete an order with given id parameters: - name: id in: path required: true description: id of the order schema: type: string requestBody: responses: 200: description: All good success content: application/json: schema: type: object properties: success: type: boolean 400: description: Bad request 500: description: internal server error /stripekey: get: tags: - Payment summary: gets you the public stripe key requestBody: responses: 200: description: All good success content: application/json: schema: type: object properties: stripekey: type: string 400: description: Bad request 500: description: internal server error /razorpaykey: get: tags: - Payment summary: gets you the public razorpay key requestBody: responses: 200: description: All good success content: application/json: schema: type: object properties: razorpaykey: type: string 400: description: Bad request 500: description: internal server error /capturestripe: post: tags: - Payment summary: send amount in number. Send amount after multiplying by 100. Also request from swagger UI are not proper as to capture payment we need to send dummy card details requestBody: content: multipart/form-data: schema: type: object required: - amount properties: amount: type: number responses: 200: description: All good success content: application/json: schema: type: object properties: success: type: boolean amount: type: number client_secret: type: string 400: description: Bad request 500: description: internal server error /capturerazorpay: post: tags: - Payment summary: send amount in number. Send amount after multiplying by 100. Check orders tab, not payments. Also request from swagger UI are not proper as to capture payment we need to send dummy card details. requestBody: content: multipart/form-data: schema: type: object required: - amount properties: amount: type: number responses: 200: description: All good success content: application/json: schema: type: object properties: success: type: boolean amount: type: number order: type: object properties: id: type: string amount: type: number currency: type: string 400: description: Bad request 500: description: internal server error ================================================ FILE: tshirtstore/utils/cookieToken.js ================================================ const cookieToken = (user, res) => { const token = user.getJwtToken(); const options = { expires: new Date( Date.now() + process.env.COOKIE_TIME * 24 * 60 * 60 * 1000 ), httpOnly: true, }; user.password = undefined; res.status(200).cookie("token", token, options).json({ success: true, token, user, }); }; module.exports = cookieToken; ================================================ FILE: tshirtstore/utils/customError.js ================================================ class CustomError extends Error { constructor(message, statusCode) { super(message); this.statusCode = statusCode; Error.captureStackTrace(this, this.constructor) } } module.exports = CustomError; ================================================ FILE: tshirtstore/utils/demotest.js ================================================ // User.find({ qty: { $lte: 20 } }) // /api/v1/product?search=coder&page=2&category=shortsleeves&rating[gte]=4 // &price[lte]=999&price[gte]=199 // rating: { $gte: '4' } // URL - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace // const p = 'gte gte lte mygte'; // const regex = /\b(gte|lte)\b/g; // console.log(p.replace(regex, m => `$${m}`)); ================================================ FILE: tshirtstore/utils/emailHelper.js ================================================ const nodemailer = require("nodemailer"); const mailHelper = async (option) => { const transporter = nodemailer.createTransport({ host: process.env.SMTP_HOST, port: process.env.SMTP_PORT, auth: { user: process.env.SMTP_USER, // generated ethereal user pass: process.env.SMTP_PASS, // generated ethereal password }, }); const message = { from: "hitesh@lco.dev", // sender address to: option.email, // list of receivers subject: option.subject, // Subject line text: option.message, // plain text body }; // send mail with defined transport object await transporter.sendMail(message); }; module.exports = mailHelper; ================================================ FILE: tshirtstore/utils/testOrder.json ================================================ { "shippingInfo": { "address": "1 Jaipur", "city": "Jaipur", "phoneNo": "9898989898", "postalCode": "302020", "state": "Rajasthan", "country": "India" }, "orderItems": [ { "name": "Pro Coder tshirts", "quantity": 1, "image": "https://res.cloudinary.com/dk92l1yoc/image/upload/v1635757229/products/b4laryk4dbp6vdrvq3wv.png", "price": 999, "product": "617facb5333fd4c0fdfdee65" } ], "paymentInfo": { "id": "testString" }, "taxAmount": 40, "shippingAmount": 10, "totalAmount": 100 } ================================================ FILE: tshirtstore/utils/whereClause.js ================================================ // base - Product.find() // base - Product.find(email: {"hitesh@lco.dev"}) //bigQ - //search=coder&page=2&category=shortsleeves&rating[gte]=4 // &price[lte]=999&price[gte]=199&limit=5 class WhereClause { constructor(base, bigQ) { this.base = base; this.bigQ = bigQ; } search() { const searchword = this.bigQ.search ? { name: { $regex: this.bigQ.search, $options: "i", }, } : {}; this.base = this.base.find({ ...searchword }); return this; } filter() { const copyQ = { ...this.bigQ }; delete copyQ["search"]; delete copyQ["limit"]; delete copyQ["page"]; //convert bigQ into a string => copyQ let stringOfCopyQ = JSON.stringify(copyQ); stringOfCopyQ = stringOfCopyQ.replace( /\b(gte|lte|gt|lt)\b/g, (m) => `$${m}` ); const jsonOfCopyQ = JSON.parse(stringOfCopyQ); this.base = this.base.find(jsonOfCopyQ); return this; } pager(resultperPage) { let currentPage = 1; if (this.bigQ.page) { currentPage = this.bigQ.page; } const skipVal = resultperPage * (currentPage - 1); this.base = this.base.limit(resultperPage).skip(skipVal); return this; } } module.exports = WhereClause; ================================================ FILE: tshirtstore/views/home.ejs ================================================ LCO Tshirt store
Read API docs here

All API docs are available here. You have access to everything except admin credentials. If you are a team member of LCO, get it from me (Hitesh)

Store DOCS
LCO course

You can visit our courses at LearnCodeonline. There is something amazing for everyone there.

Visit LCO
================================================ FILE: tshirtstore/views/signuptest.ejs ================================================ GET form

POST form