From 2a91951473aaddfccfc4273dd3cf69f86f84a684 Mon Sep 17 00:00:00 2001 From: JEDIBASIL Date: Tue, 28 Feb 2023 03:43:12 +0100 Subject: [PATCH 01/57] setup admin and user model --- src/controller/user.controller.js | 5 ++- src/exceptions/HttpException.js | 2 +- src/middleware/error.middleware.js | 4 +- src/models/admin.model.js | 49 ++++++++++++++++++++++ src/models/user.model.js | 15 +++++++ src/validator_schema/createAccountShema.js | 1 + 6 files changed, 72 insertions(+), 4 deletions(-) create mode 100644 src/models/admin.model.js diff --git a/src/controller/user.controller.js b/src/controller/user.controller.js index fbf3b6c..b7ddb95 100644 --- a/src/controller/user.controller.js +++ b/src/controller/user.controller.js @@ -1,6 +1,9 @@ +import HttpException from "../exceptions/HttpException" + const createAccount = async (req, res, next) => { try { - return res.status(200).send("create a stakepro account") + throw new HttpException(400,"Email address take") + // return res.status(200).send("create a stakepro account") } catch (err) { next(err) } diff --git a/src/exceptions/HttpException.js b/src/exceptions/HttpException.js index 00eae0c..c61ae20 100644 --- a/src/exceptions/HttpException.js +++ b/src/exceptions/HttpException.js @@ -1,6 +1,6 @@ class HttpException extends Error{ constructor(status, message){ - super(message); + super(message); this.status = status this.message = message } diff --git a/src/middleware/error.middleware.js b/src/middleware/error.middleware.js index 9e007d7..05a76cc 100644 --- a/src/middleware/error.middleware.js +++ b/src/middleware/error.middleware.js @@ -3,10 +3,10 @@ import logger from "../utils/logger"; const errorMiddleware = (error, req, res, next) => { - const status = error.status || 500; + const status = error.status || 500; const message = error.message || "Something went wrong"; - logger.error( + logger.error( `[${req.method}] ${req.path} >> StatusCode:: ${status}, Message:: ${message}` ); return res.status(status).json({ message }); diff --git a/src/models/admin.model.js b/src/models/admin.model.js new file mode 100644 index 0000000..84d8ff1 --- /dev/null +++ b/src/models/admin.model.js @@ -0,0 +1,49 @@ +import moment from "moment"; +import bcrypt from "bcrypt"; +import { Schema } from "mongoose"; + +const adminSchema = new Schema({ + email: { + type: String, + required: true, + unique: true, + lowercase: true, + }, + username: { + type: String, + required: true, + unique: true, + lowercase: true, + trim: true, + }, + password: { + type: String, + required: true, + select: false, + }, + isVerified: { + type: Boolean, + default: false, + }, + isBlocked: { + type: Boolean, + default: false, + }, + addedAt: { + type: Date, + default: () => moment().toDate(), + }, +}); + +adminSchema.pre("save", async function (next) { + if (!this.isModified("password")) next(); + const salt = await bcrypt.genSalt(10); + this.password = await bcrypt.hash(this.password, salt); +}); + +adminSchema.methods.isPasswordMatch = async function (password) { + return await bcrypt.compare(password, this.password); +}; + +const adminModel = model("Admin", adminSchema); +export default adminModel; diff --git a/src/models/user.model.js b/src/models/user.model.js index 29b3221..1b15e5d 100644 --- a/src/models/user.model.js +++ b/src/models/user.model.js @@ -21,6 +21,21 @@ const userSchema = new Schema({ required: true, select: false, }, + phoneNumber: { + type: Number, + required: true, + }, + isVerified: { + type: Boolean, + default: false + }, + isBlocked: { + type: Boolean, + default: false + }, + dob: { + type: Date + }, joinedAt: { type: Date, default: () => moment().toDate(), diff --git a/src/validator_schema/createAccountShema.js b/src/validator_schema/createAccountShema.js index c3e0759..2d079a3 100644 --- a/src/validator_schema/createAccountShema.js +++ b/src/validator_schema/createAccountShema.js @@ -4,4 +4,5 @@ const createAccountSchema = Joi.object({ email: Joi.string().email().required(), password: Joi.string().required() }) + export default createAccountSchema \ No newline at end of file From 2f9edca4313b0d2be11d0ae692fba8bdcb46e6c9 Mon Sep 17 00:00:00 2001 From: JEDIBASIL Date: Wed, 1 Mar 2023 02:31:24 +0100 Subject: [PATCH 02/57] setup database and node mailer --- src/app.js | 3 +++ src/config/index.js | 40 +++++++++++++++++++++++++++++++++--- src/models/admin.model.js | 4 ++++ src/response/HttpResponse.js | 9 ++++++++ src/utils/sendMail.js | 24 ++++++++++++++++++++++ 5 files changed, 77 insertions(+), 3 deletions(-) create mode 100644 src/response/HttpResponse.js create mode 100644 src/utils/sendMail.js diff --git a/src/app.js b/src/app.js index f56bc5f..3f95a77 100644 --- a/src/app.js +++ b/src/app.js @@ -1,10 +1,13 @@ import express from "express" import userRouter from "./routes/user.route" import errorMiddleware from "./middleware/error.middleware" +import { connectDB } from "./config" + const app = express() const port = 8080 const path = "/api/v1" +connectDB() app.use(express.json()) diff --git a/src/config/index.js b/src/config/index.js index e512ce9..eb506a4 100644 --- a/src/config/index.js +++ b/src/config/index.js @@ -1,4 +1,38 @@ -import { config } from "dotenv" -config() +import { config } from "dotenv"; +import { connect, set } from "mongoose"; +import logger from "../utils/logger"; + +config(); -export const { ACCESS_TOKEN } = process.env \ No newline at end of file + + +export async function connectDB() { + try { + set("strictQuery", false); + console.log(process.env.MONGODB_URI) + await connect(process.env.MONGODB_URI, { + useNewUrlParser: true, + useUnifiedTopology: true, + }); + logger.info( + "▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎" + ); + logger.info(`▼ ▼`); + logger.info(`▼ 🛢 🅳🅰🆃🅰🅱🅰🆂🅴 🅲🅾🅽🅽🅴🅲🆃🅴🅳 ▼`); + logger.info(`▼ ▼`); + logger.info( + "▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎" + ); + } catch (err) { + if (err instanceof Error) logger.error(err.message); + } +}; + +export const { + MONGODB_URI, + ACCESS_TOKEN, + MAIL_SERVICE, + MAIL_PASS, + MAIL_USER, + WEB_URL, +} = process.env; \ No newline at end of file diff --git a/src/models/admin.model.js b/src/models/admin.model.js index 84d8ff1..b1a2d62 100644 --- a/src/models/admin.model.js +++ b/src/models/admin.model.js @@ -25,6 +25,10 @@ const adminSchema = new Schema({ type: Boolean, default: false, }, + isAccepted: { + type: Boolean, + default: false, + }, isBlocked: { type: Boolean, default: false, diff --git a/src/response/HttpResponse.js b/src/response/HttpResponse.js new file mode 100644 index 0000000..ae69c9b --- /dev/null +++ b/src/response/HttpResponse.js @@ -0,0 +1,9 @@ +class HttpResponse { + constructor(status, message, data) { + this.data = data + this.status = status + this.message = message + } +} + +export default HttpResponse; \ No newline at end of file diff --git a/src/utils/sendMail.js b/src/utils/sendMail.js new file mode 100644 index 0000000..ff8cad7 --- /dev/null +++ b/src/utils/sendMail.js @@ -0,0 +1,24 @@ +import { MAIL_PASS, MAIL_SERVICE, MAIL_USER } from "../config" +import nodemailer from "nodemailer" +import HttpException from "../exceptions/HttpException" + +export async function sendMail(options) { + try { + const transporter = nodemailer.createTransport({ + service: MAIL_SERVICE, + auth: { + user: MAIL_USER, + pass: MAIL_PASS + }, + }) + await transporter.sendMail(options) + console.log("mail sent") + } catch (err) { + if (err instanceof Error) { + console.log(err.message) + throw new HttpException(500, "couldn't send mail") + } + } +} + +export default sendMail \ No newline at end of file From 9214ad7a16f28dcb92a0db57f994f40b8cd0cae4 Mon Sep 17 00:00:00 2001 From: JEDIBASIL Date: Wed, 1 Mar 2023 02:32:24 +0100 Subject: [PATCH 03/57] working on sigin up --- src/app.js | 3 + src/config/index.js | 40 +++- src/controller/user.controller.js | 89 +++++++- src/models/admin.model.js | 2 +- src/models/user.model.js | 2 +- src/response/HttpResponse.js | 9 + src/utils/sendMail.js | 24 +++ src/validator_schema/createAccountShema.js | 4 +- src/views/email/verify.hbs | 239 +++++++++++++++++++++ 9 files changed, 400 insertions(+), 12 deletions(-) create mode 100644 src/response/HttpResponse.js create mode 100644 src/utils/sendMail.js create mode 100644 src/views/email/verify.hbs diff --git a/src/app.js b/src/app.js index f56bc5f..3f95a77 100644 --- a/src/app.js +++ b/src/app.js @@ -1,10 +1,13 @@ import express from "express" import userRouter from "./routes/user.route" import errorMiddleware from "./middleware/error.middleware" +import { connectDB } from "./config" + const app = express() const port = 8080 const path = "/api/v1" +connectDB() app.use(express.json()) diff --git a/src/config/index.js b/src/config/index.js index e512ce9..eb506a4 100644 --- a/src/config/index.js +++ b/src/config/index.js @@ -1,4 +1,38 @@ -import { config } from "dotenv" -config() +import { config } from "dotenv"; +import { connect, set } from "mongoose"; +import logger from "../utils/logger"; + +config(); -export const { ACCESS_TOKEN } = process.env \ No newline at end of file + + +export async function connectDB() { + try { + set("strictQuery", false); + console.log(process.env.MONGODB_URI) + await connect(process.env.MONGODB_URI, { + useNewUrlParser: true, + useUnifiedTopology: true, + }); + logger.info( + "▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎" + ); + logger.info(`▼ ▼`); + logger.info(`▼ 🛢 🅳🅰🆃🅰🅱🅰🆂🅴 🅲🅾🅽🅽🅴🅲🆃🅴🅳 ▼`); + logger.info(`▼ ▼`); + logger.info( + "▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎▶︎" + ); + } catch (err) { + if (err instanceof Error) logger.error(err.message); + } +}; + +export const { + MONGODB_URI, + ACCESS_TOKEN, + MAIL_SERVICE, + MAIL_PASS, + MAIL_USER, + WEB_URL, +} = process.env; \ No newline at end of file diff --git a/src/controller/user.controller.js b/src/controller/user.controller.js index b7ddb95..7b7f4ee 100644 --- a/src/controller/user.controller.js +++ b/src/controller/user.controller.js @@ -1,12 +1,89 @@ -import HttpException from "../exceptions/HttpException" +import HttpException from "../exceptions/HttpException"; +import userModel from "../models/user.model"; +import jwt from "jsonwebtoken"; +import emailTemplateReader from "../utils/emailTemplateReader"; +import HttpResponse from "../response/HttpResponse"; +import { ACCESS_TOKEN } from "../config"; +import sendMail from "../utils/sendMail"; -const createAccount = async (req, res, next) => { + +async function createAccount(req, res, next) { try { - throw new HttpException(400,"Email address take") - // return res.status(200).send("create a stakepro account") + const data = req.body; + const emailTaken = await userModel.findOne({ email: data.email }); + if (emailTaken) throw new HttpException(409, "email taken"); + + const userTaken = await userModel.findOne({ username: data.username }); + if (userTaken) throw new HttpException(409, "username taken"); + + const phoneNumberTaken = await userModel.findOne({ username: data.phoneNumber }); + if (phoneNumberTaken) throw new HttpException(409, "phone number taken"); + + const verificationToken = jwt.sign({ value: data.email }, ACCESS_TOKEN, { + expiresIn: "6000s", + }); + + const emailTemplate = await emailTemplateReader(`verify.hbs`, { + username: data.username, + link: `{verificationToken}`, + }); + const newAccount = await userModel.create({ ...data }); + if (!newAccount) + return res + .status(500) + .send(new HttpResponse("failed", "an error occurred")); + + await sendMail({ + to: data.email, + subject: "verify account", + html: emailTemplate, + }); + return res + .status(200) + .send(new HttpResponse("success", "account created successfully")); } catch (err) { - next(err) + next(err); } } -export { createAccount } \ No newline at end of file +async function loginAccount(req, res, next) { + const { password, email } = req.body; + const findByEmail = await userModel.findOne({ email }).select("+password"); + if (!findByEmail) + throw new HttpException(404, "incorrect username or email, and password"); + + if (!(await findByEmail.isPasswordMatch(password))) + throw new HttpException(404, "incorrect username or email, and password"); + + if (!findByEmail.isVerified) + throw new HttpException(403, "account is not verified"); + + if (!findByEmail.isBlocked) + throw new HttpException(403, "account is has been blocked"); + + const accessToken = this.jwt.signJwt(user.username, "30d"); + return res + .status(200) + .send( + new HttpResponse("success", "account authenticated", { accessToken }) + ); +} + +async function verify(req, res, next) { + +} + + +async function resendVerificationMail(req, res, next) { + +} + +async function sendResetPasswordMail(req, res, next) { + +} + +async function resetPassword(req, res, next) { + +} + +export { createAccount }; diff --git a/src/models/admin.model.js b/src/models/admin.model.js index 84d8ff1..8312b3c 100644 --- a/src/models/admin.model.js +++ b/src/models/admin.model.js @@ -1,6 +1,6 @@ import moment from "moment"; import bcrypt from "bcrypt"; -import { Schema } from "mongoose"; +import { Schema, model } from "mongoose"; const adminSchema = new Schema({ email: { diff --git a/src/models/user.model.js b/src/models/user.model.js index 1b15e5d..b2b5f0f 100644 --- a/src/models/user.model.js +++ b/src/models/user.model.js @@ -1,6 +1,6 @@ import moment from 'moment'; import bcrypt from "bcrypt" -import { Schema } from 'mongoose'; +import { Schema, model } from 'mongoose'; const userSchema = new Schema({ email: { diff --git a/src/response/HttpResponse.js b/src/response/HttpResponse.js new file mode 100644 index 0000000..ae69c9b --- /dev/null +++ b/src/response/HttpResponse.js @@ -0,0 +1,9 @@ +class HttpResponse { + constructor(status, message, data) { + this.data = data + this.status = status + this.message = message + } +} + +export default HttpResponse; \ No newline at end of file diff --git a/src/utils/sendMail.js b/src/utils/sendMail.js new file mode 100644 index 0000000..ff8cad7 --- /dev/null +++ b/src/utils/sendMail.js @@ -0,0 +1,24 @@ +import { MAIL_PASS, MAIL_SERVICE, MAIL_USER } from "../config" +import nodemailer from "nodemailer" +import HttpException from "../exceptions/HttpException" + +export async function sendMail(options) { + try { + const transporter = nodemailer.createTransport({ + service: MAIL_SERVICE, + auth: { + user: MAIL_USER, + pass: MAIL_PASS + }, + }) + await transporter.sendMail(options) + console.log("mail sent") + } catch (err) { + if (err instanceof Error) { + console.log(err.message) + throw new HttpException(500, "couldn't send mail") + } + } +} + +export default sendMail \ No newline at end of file diff --git a/src/validator_schema/createAccountShema.js b/src/validator_schema/createAccountShema.js index 2d079a3..8ee0bb4 100644 --- a/src/validator_schema/createAccountShema.js +++ b/src/validator_schema/createAccountShema.js @@ -1,8 +1,10 @@ import Joi from "joi"; const createAccountSchema = Joi.object({ + username: Joi.string().required().min(3), email: Joi.string().email().required(), - password: Joi.string().required() + password: Joi.string().required(), + phoneNumber:Joi.number().required() }) export default createAccountSchema \ No newline at end of file diff --git a/src/views/email/verify.hbs b/src/views/email/verify.hbs new file mode 100644 index 0000000..0bfacae --- /dev/null +++ b/src/views/email/verify.hbs @@ -0,0 +1,239 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+
+
+ + + +
+
+ +
+ + + + + + + + +
+ +

Gram

+ +
+ + + + + + + +
+ +
+

Lets verify your email address

+
+ + + +
+ + + + + + + + + + + + + + + +
+ +
+

Hi {{username}}, we need to verify that this email address is intended to receive communications from Gram to manage your Task. We’ll send task progress, password resets, and account + updates to this email address, so we want to make sure that it’s you!

+

 

+

Please use the button below to verify within 24 hours of receiving this email. If time runs out, that’s okay—just log into your Gram account, and we will send you a new verification email and try again!

+
+ +
+ + + + + + + + + + +
+ +
+
+ + +
+
+
+ + + +
+ + + + + \ No newline at end of file From 2ce31976a4afdb9d8203e9a5b2a0b9f2d3ddd747 Mon Sep 17 00:00:00 2001 From: Jedidiah Basil <89284682+JEDIBASIL@users.noreply.github.com> Date: Wed, 1 Mar 2023 02:37:47 +0100 Subject: [PATCH 04/57] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b5afb7c..1e83d0c 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ Specify the path inwhich you want to clone the project into on your terminal or Once you are in the folder where you want the project to be then copy the git command below to clone the project ``` -git clone https://github.com/Ayagigs/stakepro +git clone --branch DEVELOPMENT https://github.com/Ayagigs/stakepro.git ``` After cloning the project the will be a new folder added on your local computer with the reopsitory name, you can now open the folder in your IDE `Visual Studio code` From 067c70234824144d6eb3bae84fe60754d667b563 Mon Sep 17 00:00:00 2001 From: hardeylarning Date: Wed, 1 Mar 2023 11:04:54 +0100 Subject: [PATCH 05/57] added admin.controller.js --- src/controller/admin.controller.js | 11 +++++++++++ src/controller/user.controller.js | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 src/controller/admin.controller.js diff --git a/src/controller/admin.controller.js b/src/controller/admin.controller.js new file mode 100644 index 0000000..2a212b3 --- /dev/null +++ b/src/controller/admin.controller.js @@ -0,0 +1,11 @@ +import HttpException from "../exceptions/HttpException" + +export const registerEmail = async (req, res) => { + try { + + throw new HttpException(400,"Email address taken") + // return res.status(200).send("create a stakepro account") + } catch (err) { + next(err) + } +} \ No newline at end of file diff --git a/src/controller/user.controller.js b/src/controller/user.controller.js index b7ddb95..b4aaab4 100644 --- a/src/controller/user.controller.js +++ b/src/controller/user.controller.js @@ -2,7 +2,7 @@ import HttpException from "../exceptions/HttpException" const createAccount = async (req, res, next) => { try { - throw new HttpException(400,"Email address take") + throw new HttpException(400,"Email address taken") // return res.status(200).send("create a stakepro account") } catch (err) { next(err) From 50d93038e03af02f4171b8da701099558e95cce3 Mon Sep 17 00:00:00 2001 From: hardeylarning Date: Wed, 1 Mar 2023 11:19:35 +0100 Subject: [PATCH 06/57] added email-validator.js --- src/controller/admin.controller.js | 11 +++++++++-- src/utils/email-validator.js | 5 +++++ 2 files changed, 14 insertions(+), 2 deletions(-) create mode 100644 src/utils/email-validator.js diff --git a/src/controller/admin.controller.js b/src/controller/admin.controller.js index 2a212b3..e352a42 100644 --- a/src/controller/admin.controller.js +++ b/src/controller/admin.controller.js @@ -1,10 +1,17 @@ import HttpException from "../exceptions/HttpException" +import { validateEmail } from "../utils/email-validator" export const registerEmail = async (req, res) => { try { + const {email} = req.body + if (validateEmail(email)) { + + await Medicine.create({ + email + }); + } + throw new HttpException(400,"Invaid Email!") - throw new HttpException(400,"Email address taken") - // return res.status(200).send("create a stakepro account") } catch (err) { next(err) } diff --git a/src/utils/email-validator.js b/src/utils/email-validator.js new file mode 100644 index 0000000..dcaa7c8 --- /dev/null +++ b/src/utils/email-validator.js @@ -0,0 +1,5 @@ +export const validateEmail = (email) => { + return email.match( + /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/ + ); + }; \ No newline at end of file From d7953da626fb1ad790cf86b86b87a5e3b2bd3f11 Mon Sep 17 00:00:00 2001 From: hardeylarning Date: Wed, 1 Mar 2023 11:20:05 +0100 Subject: [PATCH 07/57] fixed two exports from sendEmail.js --- src/controller/admin.controller.js | 5 +++-- src/utils/sendMail.js | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/controller/admin.controller.js b/src/controller/admin.controller.js index e352a42..bd7d856 100644 --- a/src/controller/admin.controller.js +++ b/src/controller/admin.controller.js @@ -1,14 +1,15 @@ import HttpException from "../exceptions/HttpException" +import adminModel from "../models/admin.model"; import { validateEmail } from "../utils/email-validator" export const registerEmail = async (req, res) => { try { const {email} = req.body if (validateEmail(email)) { - - await Medicine.create({ + await adminModel.create({ email }); + } throw new HttpException(400,"Invaid Email!") diff --git a/src/utils/sendMail.js b/src/utils/sendMail.js index ff8cad7..93b8581 100644 --- a/src/utils/sendMail.js +++ b/src/utils/sendMail.js @@ -2,7 +2,7 @@ import { MAIL_PASS, MAIL_SERVICE, MAIL_USER } from "../config" import nodemailer from "nodemailer" import HttpException from "../exceptions/HttpException" -export async function sendMail(options) { +async function sendMail(options) { try { const transporter = nodemailer.createTransport({ service: MAIL_SERVICE, From c3cf2168675b5c67c6fdb1d04751407448d1310e Mon Sep 17 00:00:00 2001 From: hardeylarning Date: Wed, 1 Mar 2023 11:29:27 +0100 Subject: [PATCH 08/57] added generate token function --- src/controller/admin.controller.js | 9 ++++++++- src/utils/jwt/generate-token.js | 10 ++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 src/utils/jwt/generate-token.js diff --git a/src/controller/admin.controller.js b/src/controller/admin.controller.js index bd7d856..7854f7e 100644 --- a/src/controller/admin.controller.js +++ b/src/controller/admin.controller.js @@ -1,6 +1,7 @@ import HttpException from "../exceptions/HttpException" import adminModel from "../models/admin.model"; import { validateEmail } from "../utils/email-validator" +import sendMail from "../utils/sendMail"; export const registerEmail = async (req, res) => { try { @@ -9,7 +10,13 @@ export const registerEmail = async (req, res) => { await adminModel.create({ email }); - + let mailOptions = { + from: process.env.clientEmail, + to: email, + subject: 'Admin account registration', + text: + } + await sendMail() } throw new HttpException(400,"Invaid Email!") diff --git a/src/utils/jwt/generate-token.js b/src/utils/jwt/generate-token.js new file mode 100644 index 0000000..96c3fe7 --- /dev/null +++ b/src/utils/jwt/generate-token.js @@ -0,0 +1,10 @@ +import jwt from "jsonwebtoken" + +const generateToken = (email) => { + return jwt.sign( + {email}, process.env.JWT_KEY, + {expiresIn: process.env.JWT_EXPIRATION_TIME} + ) +} + +export default generateToken; \ No newline at end of file From 3ad4a68cf979d32daa7362535959e637d78162a8 Mon Sep 17 00:00:00 2001 From: hardeylarning Date: Wed, 1 Mar 2023 11:44:36 +0100 Subject: [PATCH 09/57] register email has been added successfully --- src/controller/admin.controller.js | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/controller/admin.controller.js b/src/controller/admin.controller.js index 7854f7e..3ba01be 100644 --- a/src/controller/admin.controller.js +++ b/src/controller/admin.controller.js @@ -1,6 +1,7 @@ import HttpException from "../exceptions/HttpException" import adminModel from "../models/admin.model"; import { validateEmail } from "../utils/email-validator" +import generateToken from "../utils/jwt/generate-token"; import sendMail from "../utils/sendMail"; export const registerEmail = async (req, res) => { @@ -14,11 +15,18 @@ export const registerEmail = async (req, res) => { from: process.env.clientEmail, to: email, subject: 'Admin account registration', - text: + text: `Kindly follow this link in order to continue with your registration process as an admin! + sign up` } - await sendMail() + await sendMail(mailOptions) + return res.json({ + status: "success", + message: "A registration link has been successfully sent to your email, kindly continue your registration from there.", + }); + } + else { + throw new HttpException(400,"Invaid Email!") } - throw new HttpException(400,"Invaid Email!") } catch (err) { next(err) From ac3a5b2632433a655ea24ec9f890230e1d7c4b6e Mon Sep 17 00:00:00 2001 From: hardeylarning Date: Wed, 1 Mar 2023 12:13:45 +0100 Subject: [PATCH 10/57] added admin-route file and also registered admin-register-email endpoint --- src/controller/admin.controller.js | 35 +++++++++++++++++------------- src/routes/admin.route.js | 6 +++++ 2 files changed, 26 insertions(+), 15 deletions(-) create mode 100644 src/routes/admin.route.js diff --git a/src/controller/admin.controller.js b/src/controller/admin.controller.js index 3ba01be..2fa60a4 100644 --- a/src/controller/admin.controller.js +++ b/src/controller/admin.controller.js @@ -8,21 +8,26 @@ export const registerEmail = async (req, res) => { try { const {email} = req.body if (validateEmail(email)) { - await adminModel.create({ - email - }); - let mailOptions = { - from: process.env.clientEmail, - to: email, - subject: 'Admin account registration', - text: `Kindly follow this link in order to continue with your registration process as an admin! - sign up` - } - await sendMail(mailOptions) - return res.json({ - status: "success", - message: "A registration link has been successfully sent to your email, kindly continue your registration from there.", - }); + return res.json({ + status: "success", + message: `Correct email ${email}`, + }); + + // await adminModel.create({ + // email + // }); + // let mailOptions = { + // from: process.env.clientEmail, + // to: email, + // subject: 'Admin account registration', + // text: `Kindly follow this link in order to continue with your registration process as an admin! + // sign up` + // } + // await sendMail(mailOptions) + // return res.json({ + // status: "success", + // message: "A registration link has been successfully sent to your email, kindly continue your registration from there.", + // }); } else { throw new HttpException(400,"Invaid Email!") diff --git a/src/routes/admin.route.js b/src/routes/admin.route.js new file mode 100644 index 0000000..6c83ad0 --- /dev/null +++ b/src/routes/admin.route.js @@ -0,0 +1,6 @@ +import express from "express"; +import { registerEmail } from "../controller/admin.controller"; + +const adminRoute = express.Router(); + +adminRoute.post("/admin-register-email", registerEmail) \ No newline at end of file From 456222e61e0ef4bf2963943ef8dc244bf72ef154 Mon Sep 17 00:00:00 2001 From: hardeylarning Date: Wed, 1 Mar 2023 12:43:34 +0100 Subject: [PATCH 11/57] fixed the admin model and tested email validation --- src/app.js | 2 ++ src/controller/admin.controller.js | 37 ++++++++++++++++-------------- src/models/admin.model.js | 10 ++++---- src/routes/admin.route.js | 7 ++++-- 4 files changed, 31 insertions(+), 25 deletions(-) diff --git a/src/app.js b/src/app.js index 3f95a77..6ea13aa 100644 --- a/src/app.js +++ b/src/app.js @@ -2,6 +2,7 @@ import express from "express" import userRouter from "./routes/user.route" import errorMiddleware from "./middleware/error.middleware" import { connectDB } from "./config" +import adminRouter from "./routes/admin.route" const app = express() @@ -13,6 +14,7 @@ app.use(express.json()) // add routes here app.use(`${path}/user`, userRouter) +app.use(`${path}/admin`, adminRouter) diff --git a/src/controller/admin.controller.js b/src/controller/admin.controller.js index 2fa60a4..e3683a2 100644 --- a/src/controller/admin.controller.js +++ b/src/controller/admin.controller.js @@ -1,29 +1,32 @@ import HttpException from "../exceptions/HttpException" -import adminModel from "../models/admin.model"; +import Admin from "../models/admin.model"; +// import {adminModel} from "../models/admin.model"; +// import adminModel from "../models/admin.model"; import { validateEmail } from "../utils/email-validator" import generateToken from "../utils/jwt/generate-token"; import sendMail from "../utils/sendMail"; -export const registerEmail = async (req, res) => { +export const registerEmail = async (req, res, next) => { try { const {email} = req.body if (validateEmail(email)) { - return res.json({ - status: "success", - message: `Correct email ${email}`, - }); + const admin = await Admin.create({ + email + }); + let mailOptions = { + from: process.env.clientEmail, + to: email, + subject: 'Admin account registration', + text: `Kindly follow this link in order to continue with your registration process as an admin! + sign up` + } + await sendMail(mailOptions) - // await adminModel.create({ - // email - // }); - // let mailOptions = { - // from: process.env.clientEmail, - // to: email, - // subject: 'Admin account registration', - // text: `Kindly follow this link in order to continue with your registration process as an admin! - // sign up` - // } - // await sendMail(mailOptions) + return res.json({ + status: "success", + message: "A registration link has been successfully sent to your email, kindly continue your registration from there.", + body: admin, + }); // return res.json({ // status: "success", // message: "A registration link has been successfully sent to your email, kindly continue your registration from there.", diff --git a/src/models/admin.model.js b/src/models/admin.model.js index b1a2d62..6b8b14e 100644 --- a/src/models/admin.model.js +++ b/src/models/admin.model.js @@ -1,8 +1,8 @@ import moment from "moment"; import bcrypt from "bcrypt"; -import { Schema } from "mongoose"; +import mongoose from "mongoose"; -const adminSchema = new Schema({ +const adminSchema = new mongoose.Schema({ email: { type: String, required: true, @@ -11,14 +11,12 @@ const adminSchema = new Schema({ }, username: { type: String, - required: true, unique: true, lowercase: true, trim: true, }, password: { type: String, - required: true, select: false, }, isVerified: { @@ -49,5 +47,5 @@ adminSchema.methods.isPasswordMatch = async function (password) { return await bcrypt.compare(password, this.password); }; -const adminModel = model("Admin", adminSchema); -export default adminModel; +const Admin = mongoose.model("Admin", adminSchema); +export default Admin; diff --git a/src/routes/admin.route.js b/src/routes/admin.route.js index 6c83ad0..46ceb6a 100644 --- a/src/routes/admin.route.js +++ b/src/routes/admin.route.js @@ -1,6 +1,9 @@ import express from "express"; import { registerEmail } from "../controller/admin.controller"; -const adminRoute = express.Router(); +const adminRouter = express.Router(); -adminRoute.post("/admin-register-email", registerEmail) \ No newline at end of file +adminRouter.post("/admin-register-email", registerEmail) + + +export default adminRouter \ No newline at end of file From 96f9834cdcff7adc41b4f3c2d8b4c78dea710f39 Mon Sep 17 00:00:00 2001 From: JEDIBASIL Date: Wed, 1 Mar 2023 20:36:39 +0100 Subject: [PATCH 12/57] done with user auth --- .vscode/settings.json | 3 + package-lock.json | 48 +++++ package.json | 1 + src/config/index.js | 1 - src/controller/user.controller.js | 189 ++++++++++++---- src/models/otp.model.js | 23 ++ src/routes/user.route.js | 59 ++++- src/validator_schema/loginSchema.js | 8 + .../resendVerificationSchema.js | 7 + src/validator_schema/resetPasswordSchema.js | 9 + src/validator_schema/verifyAccountSchema.js | 7 + src/views/email/reset-password.hbs | 201 ++++++++++++++++++ src/views/email/verify.hbs | 4 +- 13 files changed, 508 insertions(+), 52 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 src/models/otp.model.js create mode 100644 src/validator_schema/loginSchema.js create mode 100644 src/validator_schema/resendVerificationSchema.js create mode 100644 src/validator_schema/resetPasswordSchema.js create mode 100644 src/validator_schema/verifyAccountSchema.js create mode 100644 src/views/email/reset-password.hbs diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..28a51d5 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "compile-hero.disable-compile-files-on-did-save-code": false +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index ac8d2a6..fa4c68b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,6 +17,7 @@ "mongoose": "^6.9.1", "nodemailer": "^6.9.1", "nodemon": "^2.0.20", + "randomstring": "^1.2.3", "uuid": "^9.0.0", "winston": "^3.8.2" } @@ -1277,6 +1278,14 @@ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" }, + "node_modules/array-uniq": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.2.tgz", + "integrity": "sha512-GVYjmpL05al4dNlKJm53mKE4w9OOLiuVHWorsIA3YVz+Hu0hcn6PtE3Ydl0EqU7v+7ABC4mjjWsnLUxbpno+CA==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/async": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", @@ -2716,6 +2725,26 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/randombytes": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.0.3.tgz", + "integrity": "sha512-lDVjxQQFoCG1jcrP06LNo2lbWp4QTShEXnhActFBwYuHprllQV6VUpwreApsYqCgD+N1mHoqJ/BI/4eV4R2GYg==" + }, + "node_modules/randomstring": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/randomstring/-/randomstring-1.2.3.tgz", + "integrity": "sha512-3dEFySepTzp2CvH6W/ASYGguPPveBuz5MpZ7MuoUkoVehmyNl9+F9c9GFVrz2QPbM9NXTIHGcmJDY/3j4677kQ==", + "dependencies": { + "array-uniq": "1.0.2", + "randombytes": "2.0.3" + }, + "bin": { + "randomstring": "bin/randomstring" + }, + "engines": { + "node": "*" + } + }, "node_modules/range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", @@ -4340,6 +4369,11 @@ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" }, + "array-uniq": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.2.tgz", + "integrity": "sha512-GVYjmpL05al4dNlKJm53mKE4w9OOLiuVHWorsIA3YVz+Hu0hcn6PtE3Ydl0EqU7v+7ABC4mjjWsnLUxbpno+CA==" + }, "async": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", @@ -5413,6 +5447,20 @@ "side-channel": "^1.0.4" } }, + "randombytes": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.0.3.tgz", + "integrity": "sha512-lDVjxQQFoCG1jcrP06LNo2lbWp4QTShEXnhActFBwYuHprllQV6VUpwreApsYqCgD+N1mHoqJ/BI/4eV4R2GYg==" + }, + "randomstring": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/randomstring/-/randomstring-1.2.3.tgz", + "integrity": "sha512-3dEFySepTzp2CvH6W/ASYGguPPveBuz5MpZ7MuoUkoVehmyNl9+F9c9GFVrz2QPbM9NXTIHGcmJDY/3j4677kQ==", + "requires": { + "array-uniq": "1.0.2", + "randombytes": "2.0.3" + } + }, "range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", diff --git a/package.json b/package.json index 1b94796..9606b12 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "mongoose": "^6.9.1", "nodemailer": "^6.9.1", "nodemon": "^2.0.20", + "randomstring": "^1.2.3", "uuid": "^9.0.0", "winston": "^3.8.2" }, diff --git a/src/config/index.js b/src/config/index.js index eb506a4..1c277d4 100644 --- a/src/config/index.js +++ b/src/config/index.js @@ -9,7 +9,6 @@ config(); export async function connectDB() { try { set("strictQuery", false); - console.log(process.env.MONGODB_URI) await connect(process.env.MONGODB_URI, { useNewUrlParser: true, useUnifiedTopology: true, diff --git a/src/controller/user.controller.js b/src/controller/user.controller.js index 7b7f4ee..1963672 100644 --- a/src/controller/user.controller.js +++ b/src/controller/user.controller.js @@ -3,11 +3,12 @@ import userModel from "../models/user.model"; import jwt from "jsonwebtoken"; import emailTemplateReader from "../utils/emailTemplateReader"; import HttpResponse from "../response/HttpResponse"; -import { ACCESS_TOKEN } from "../config"; +import { ACCESS_TOKEN, WEB_URL } from "../config"; import sendMail from "../utils/sendMail"; +import randomstring from "randomstring"; +import otpModel from "../models/otp.model"; - -async function createAccount(req, res, next) { +export async function createAccount(req, res, next) { try { const data = req.body; const emailTaken = await userModel.findOne({ email: data.email }); @@ -16,28 +17,16 @@ async function createAccount(req, res, next) { const userTaken = await userModel.findOne({ username: data.username }); if (userTaken) throw new HttpException(409, "username taken"); - const phoneNumberTaken = await userModel.findOne({ username: data.phoneNumber }); - if (phoneNumberTaken) throw new HttpException(409, "phone number taken"); - - const verificationToken = jwt.sign({ value: data.email }, ACCESS_TOKEN, { - expiresIn: "6000s", + const phoneNumberTaken = await userModel.findOne({ + phoneNumber: data.phoneNumber, }); + if (phoneNumberTaken) throw new HttpException(409, "phone number taken"); - const emailTemplate = await emailTemplateReader(`verify.hbs`, { - username: data.username, - link: `{verificationToken}`, - }); const newAccount = await userModel.create({ ...data }); - if (!newAccount) - return res - .status(500) - .send(new HttpResponse("failed", "an error occurred")); + if (!newAccount) throw new HttpException(500, "an error occurred"); + + await sendVerificationMail(data.email); - await sendMail({ - to: data.email, - subject: "verify account", - html: emailTemplate, - }); return res .status(200) .send(new HttpResponse("success", "account created successfully")); @@ -46,44 +35,158 @@ async function createAccount(req, res, next) { } } -async function loginAccount(req, res, next) { - const { password, email } = req.body; - const findByEmail = await userModel.findOne({ email }).select("+password"); - if (!findByEmail) - throw new HttpException(404, "incorrect username or email, and password"); +export async function loginAccount(req, res, next) { + try { + const { password, email } = req.body; + const findByEmail = await userModel.findOne({ email }).select("+password"); + if (!findByEmail) + throw new HttpException(404, "incorrect username or email, and password"); + + if (!(await findByEmail.isPasswordMatch(password))) + throw new HttpException(404, "incorrect username or email, and password"); - if (!(await findByEmail.isPasswordMatch(password))) - throw new HttpException(404, "incorrect username or email, and password"); + if (!findByEmail.isVerified) + throw new HttpException(403, "account is not verified"); - if (!findByEmail.isVerified) - throw new HttpException(403, "account is not verified"); + if (findByEmail.isBlocked) + throw new HttpException(403, "account is has been blocked"); - if (!findByEmail.isBlocked) - throw new HttpException(403, "account is has been blocked"); + const accessToken = jwt.sign({ value: email }, ACCESS_TOKEN, { + expiresIn: "30d", + }); - const accessToken = this.jwt.signJwt(user.username, "30d"); - return res - .status(200) - .send( - new HttpResponse("success", "account authenticated", { accessToken }) - ); + return res + .status(200) + .send( + new HttpResponse("success", "account authenticated", { accessToken }) + ); + } catch (err) { + next(err); + } } -async function verify(req, res, next) { +export async function verify(req, res, next) { + try { + const { otp } = req.body; + + const confirmedOtp = await otpModel.findOne({ otp }); + if (!confirmedOtp) throw new HttpException(400, "invalid otp"); + + const decoded = jwt.decode(confirmedOtp.key, { complete: true }); + const { exp, value } = decoded.payload; + + const currentTime = Math.floor(Date.now() / 1000); + if (currentTime >= exp) throw new HttpException(401, "otp has expired"); + const user = await userModel.findOne({ email: value }); + user.isVerified = true; + await user.save(); + + return res + .status(200) + .send(new HttpResponse("success", "account verified successfully")); + } catch (err) { + next(err); + } } +async function sendVerificationMail(email) { + const verificationToken = jwt.sign({ value: email }, ACCESS_TOKEN, { + expiresIn: "1h", + }); + + const OTP = randomstring.generate({ + length: 6, + charset: "numeric", + }); + + const addedOtp = await otpModel.create({ + key: verificationToken, + otp: OTP, + }); -async function resendVerificationMail(req, res, next) { + if (!addedOtp) throw new HttpException(500, "an error occurred"); + const emailTemplate = await emailTemplateReader(`verify.hbs`, { + otp: OTP, + }); + + await sendMail({ + to: email, + subject: "verify account", + html: emailTemplate, + }); } -async function sendResetPasswordMail(req, res, next) { +export async function resendVerificationMail(req, res, next) { + try { + const { email } = req.body; + + const foundEmail = await userModel.findOne({ email }); + if (!foundEmail) throw new HttpException(404, "email not found"); + + if (foundEmail.isVerified) throw new HttpException(403, "account verified"); + await sendVerificationMail(email); + + return res.status(200).send(new HttpResponse("success", "email resent")); + } catch (err) { + next(err); + } } -async function resetPassword(req, res, next) { +export async function sendResetPasswordMail(req, res, next) { + try { + const { email } = req.body; + const findByEmail = await userModel.findOne({ email }); + if (!findByEmail) throw new HttpException(404, "invalid email"); + + const verificationToken = jwt.sign({ value: email }, ACCESS_TOKEN, { + expiresIn: "1h", + }); + + const emailTemplate = await emailTemplateReader(`reset-password.hbs`, { + link: `${WEB_URL}/reset-password/${verificationToken}`, + }); + + await sendMail({ + to: email, + subject: "reset password", + html: emailTemplate, + }); + + return res + .status(200) + .send(new HttpResponse("success", "reset password mail sent")); + } catch (err) { + next(err); + } } -export { createAccount }; +export async function resetPassword(req, res, next) { + try { + const { token, confirmPassword, password } = req.body + + const decoded = jwt.decode(token, { complete: true }); + const { exp, value } = decoded.payload; + + const currentTime = Math.floor(Date.now() / 1000); + if (currentTime >= exp) throw new HttpException(401, "token has expired"); + + if (password !== confirmPassword) throw new HttpException(400, "password do not match"); + + const findByEmail = await userModel.findOne({ email: value }); + if (!findByEmail) throw new HttpException(404, "invalid email"); + + findByEmail.password = password; + await findByEmail.save(); + + return res + .status(200) + .send(new HttpResponse("success", "password reset")); + + } catch (err) { + next(err); + } +} diff --git a/src/models/otp.model.js b/src/models/otp.model.js new file mode 100644 index 0000000..0a83548 --- /dev/null +++ b/src/models/otp.model.js @@ -0,0 +1,23 @@ +import moment from "moment"; +import bcrypt from "bcrypt"; +import { Schema, model } from "mongoose"; + +const otpSchema = new Schema({ + otp: { + type: String, + required: true, + unique: true, + }, + key: { + type: String, + required: true, + unique: true, + }, + addedAt: { + type: Date, + default: () => moment().toDate(), + }, +}) + +const otpModel = model("Otp", otpSchema); +export default otpModel; \ No newline at end of file diff --git a/src/routes/user.route.js b/src/routes/user.route.js index 989be59..cc0b495 100644 --- a/src/routes/user.route.js +++ b/src/routes/user.route.js @@ -1,11 +1,60 @@ -import { createAccount } from "../controller/user.controller"; -import { Router } from "express" +import { + createAccount, + resendVerificationMail, + loginAccount, + resetPassword, + sendResetPasswordMail, + verify +} from "../controller/user.controller"; +import { Router } from "express"; import validatorMiddleware from "../middleware/validator.middleware"; import createAccountSchema from "../validator_schema/createAccountShema"; +import verifyAccountSchema from "../validator_schema/verifyAccountSchema"; +import loginSchema from "../validator_schema/loginSchema"; +import resetPasswordSchema from "../validator_schema/resetPasswordSchema"; +import resendVerificationSchema from "../validator_schema/resendVerificationSchema"; -const userRouter = Router({ mergeParams: true }) +const userRouter = Router({ mergeParams: true }); -userRouter.route("/create").post(validatorMiddleware(createAccountSchema, "body"), createAccount) +userRouter + .route("/create") + .post( + validatorMiddleware(createAccountSchema, "body"), + createAccount + ); +userRouter + .route("/verify") + .post( + validatorMiddleware(verifyAccountSchema, "body"), + verify + ); -export default userRouter \ No newline at end of file +userRouter + .route("/verify/resend") + .post( + validatorMiddleware(resendVerificationSchema, "body"), + resendVerificationMail + ); + +userRouter + .route("/login") + .post(validatorMiddleware(loginSchema, "body"), + loginAccount + ); + +userRouter + .route("/verify/reset-password") + .post( + validatorMiddleware(resendVerificationSchema, "body"), + sendResetPasswordMail + ); + +userRouter + .route("/reset-password") + .post( + validatorMiddleware(resetPasswordSchema, "body"), + resetPassword + ); + +export default userRouter; diff --git a/src/validator_schema/loginSchema.js b/src/validator_schema/loginSchema.js new file mode 100644 index 0000000..57d67b2 --- /dev/null +++ b/src/validator_schema/loginSchema.js @@ -0,0 +1,8 @@ +import Joi from "joi"; + +const loginSchema = Joi.object({ + email: Joi.string().email().required(), + password: Joi.string().required(), +}) + +export default loginSchema \ No newline at end of file diff --git a/src/validator_schema/resendVerificationSchema.js b/src/validator_schema/resendVerificationSchema.js new file mode 100644 index 0000000..8e798c0 --- /dev/null +++ b/src/validator_schema/resendVerificationSchema.js @@ -0,0 +1,7 @@ +import Joi from "joi"; + +const resendVerificationSchema = Joi.object({ + email: Joi.string().email().required(), +}) + +export default resendVerificationSchema \ No newline at end of file diff --git a/src/validator_schema/resetPasswordSchema.js b/src/validator_schema/resetPasswordSchema.js new file mode 100644 index 0000000..4b7af73 --- /dev/null +++ b/src/validator_schema/resetPasswordSchema.js @@ -0,0 +1,9 @@ +import Joi from "joi"; + +const resetPasswordSchema = Joi.object({ + token: Joi.string().required(), + confirmPassword: Joi.string().required(), + password: Joi.string().required(), +}) + +export default resetPasswordSchema \ No newline at end of file diff --git a/src/validator_schema/verifyAccountSchema.js b/src/validator_schema/verifyAccountSchema.js new file mode 100644 index 0000000..72b0d00 --- /dev/null +++ b/src/validator_schema/verifyAccountSchema.js @@ -0,0 +1,7 @@ +import Joi from "joi"; + +const verifyAccountSchema = Joi.object({ + otp: Joi.number().required(), +}) + +export default verifyAccountSchema \ No newline at end of file diff --git a/src/views/email/reset-password.hbs b/src/views/email/reset-password.hbs new file mode 100644 index 0000000..26577a6 --- /dev/null +++ b/src/views/email/reset-password.hbs @@ -0,0 +1,201 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+
+
+ + + +
+
+ +
+ + + + + + + + +
+ +

Gram

+ +
+ + + + + + + +
+ +
+

Reset password

+
+ + + +
+ + + + + + + + + + +
+ +
+
+ + +
+
+
+ + + +
+ + + + + \ No newline at end of file diff --git a/src/views/email/verify.hbs b/src/views/email/verify.hbs index 0bfacae..ce6029f 100644 --- a/src/views/email/verify.hbs +++ b/src/views/email/verify.hbs @@ -204,9 +204,7 @@ From a489d8fd91fd656082f3f2461d879c02fba9cfff Mon Sep 17 00:00:00 2001 From: JEDIBASIL Date: Wed, 1 Mar 2023 21:04:00 +0100 Subject: [PATCH 13/57] done with faq --- src/app.js | 4 ++- src/controller/faq.controller.js | 48 ++++++++++++++++++++++++++++++++ src/models/faq.model.js | 26 +++++++++++++++++ src/routes/faq.route.js | 9 ++++++ 4 files changed, 86 insertions(+), 1 deletion(-) create mode 100644 src/controller/faq.controller.js create mode 100644 src/models/faq.model.js create mode 100644 src/routes/faq.route.js diff --git a/src/app.js b/src/app.js index 3f95a77..86faaa8 100644 --- a/src/app.js +++ b/src/app.js @@ -1,5 +1,7 @@ import express from "express" import userRouter from "./routes/user.route" +import faqRouter from "./routes/faq.route" + import errorMiddleware from "./middleware/error.middleware" import { connectDB } from "./config" @@ -13,7 +15,7 @@ app.use(express.json()) // add routes here app.use(`${path}/user`, userRouter) - +app.use(`${path}/faq`, faqRouter) app.use(errorMiddleware) diff --git a/src/controller/faq.controller.js b/src/controller/faq.controller.js new file mode 100644 index 0000000..8a02faa --- /dev/null +++ b/src/controller/faq.controller.js @@ -0,0 +1,48 @@ +import faqModel from "../models/faq.model"; +import HttpException from "../exceptions/HttpException"; +import HttpResponse from "../response/HttpResponse"; + +export const createFaqs = async (req, res, next) => { + try { + const faq = await faqModel.create(req.body); + if (faq) + return res.status(200).send(new HttpResponse("success", "Faq created")); + } catch (err) { + next(err); + } +}; + +export const getAllFaqs = async (req, res, next) => { + try { + const faq = await faqModel.find(); + if (faq) + return res.status(200).send(new HttpResponse("success", "Faqs", faq)); + } catch (err) { + next(err); + } +}; + +export const deleteFaq = async (req, res, next) => { + try { + const { id } = req.params; + const faq = await faqModel.findById(id); + if (!faq) throw new HttpException(404, "faq not found"); + return res.status(200).send(new HttpResponse("success", "deleted faq")); + } catch (err) { + next(err); + } +}; + +export const updateFaq = async (req, res, next) => { + try { + const { question, answer } = req.body; + const { id } = req.params; + let faq = await faqModel.findById(id); + if (!faq) throw new HttpException(404, "faq not found"); + + await faq.update({ question, answer }); + return res.status(200).send(new HttpResponse("success", "faq updated")); + } catch (err) { + next(err); + } +}; diff --git a/src/models/faq.model.js b/src/models/faq.model.js new file mode 100644 index 0000000..34affe7 --- /dev/null +++ b/src/models/faq.model.js @@ -0,0 +1,26 @@ +import { Schema, model } from "mongoose" + +const faqSchema = new Schema({ + question: { + type: String, + required: true + }, + answer: { + type: String, + required: true + }, + + addedBy: { + type: String, + required: true + }, + + createdAt: { + type: Date, + default: () => moment().toDate(), + }, +}) + +const faqModel = model('Faqs', faqSchema); + +export default faqModel \ No newline at end of file diff --git a/src/routes/faq.route.js b/src/routes/faq.route.js new file mode 100644 index 0000000..cd8c9ee --- /dev/null +++ b/src/routes/faq.route.js @@ -0,0 +1,9 @@ +import { Router } from "express"; +import { createFaqs, deleteFaq, getAllFaqs, updateFaq } from "../controller/faq.controller" + +const faqRouter = Router({ mergeParams: true }) + +faqRouter.route("/").get(getAllFaqs).post(createFaqs) +faqRouter.route("/:id").put(updateFaq).delete(deleteFaq) + +export default faqRouter \ No newline at end of file From 3ec4717996c486e188b8982c069dccbe10e8bd7f Mon Sep 17 00:00:00 2001 From: hardeylarning Date: Thu, 2 Mar 2023 14:22:14 +0100 Subject: [PATCH 14/57] added input-validator function --- src/controller/admin.controller.js | 58 +++++++++++++++++++++++++++--- src/models/admin.model.js | 4 +-- src/utils/input-validator.js | 3 ++ src/utils/sendMail.js | 2 +- 4 files changed, 60 insertions(+), 7 deletions(-) create mode 100644 src/utils/input-validator.js diff --git a/src/controller/admin.controller.js b/src/controller/admin.controller.js index e3683a2..a19c9b2 100644 --- a/src/controller/admin.controller.js +++ b/src/controller/admin.controller.js @@ -9,15 +9,27 @@ import sendMail from "../utils/sendMail"; export const registerEmail = async (req, res, next) => { try { const {email} = req.body + if (validateEmail(email)) { + + const existingAdmin = await Admin.findOne({email}) + + if (existingAdmin) { + return res.json({ + status: "error", + message: "A registration link has been successfully sent to your email, kindly continue your registration from there.", + }); + } + const admin = await Admin.create({ email - }); + }); + let mailOptions = { - from: process.env.clientEmail, + from: process.env.MAIL_USER, to: email, subject: 'Admin account registration', - text: `Kindly follow this link in order to continue with your registration process as an admin! + text: `Dear ${email}, kindly follow this link in order to continue with your registration process as an admin! sign up` } await sendMail(mailOptions) @@ -33,10 +45,48 @@ export const registerEmail = async (req, res, next) => { // }); } else { + throw new HttpException(400,"Invaid Email!") } } catch (err) { next(err) } -} \ No newline at end of file +} + +export const updateAdminRecord = async (req, res, next) => { + try { + const {email, token} = req.query + + if (email && token) { + const admin = await Admin.findOne({email}) + if (!admin) { + return res.json({ + status: "error", + message: "Invalid email", + }); + } + + const existingAdmin = + + + + } + + else { + return res.json({ + status: "error", + message: "Invalid request", + }); + } + + + + + + } + catch (err) { + next(err) + } +} + diff --git a/src/models/admin.model.js b/src/models/admin.model.js index 6b8b14e..f780ead 100644 --- a/src/models/admin.model.js +++ b/src/models/admin.model.js @@ -11,7 +11,7 @@ const adminSchema = new mongoose.Schema({ }, username: { type: String, - unique: true, + // unique: true, lowercase: true, trim: true, }, @@ -47,5 +47,5 @@ adminSchema.methods.isPasswordMatch = async function (password) { return await bcrypt.compare(password, this.password); }; -const Admin = mongoose.model("Admin", adminSchema); +const Admin = mongoose.model("Admin2", adminSchema); export default Admin; diff --git a/src/utils/input-validator.js b/src/utils/input-validator.js new file mode 100644 index 0000000..4b85404 --- /dev/null +++ b/src/utils/input-validator.js @@ -0,0 +1,3 @@ +export const validateField = value => { + return value.trim().length >= 6 +} \ No newline at end of file diff --git a/src/utils/sendMail.js b/src/utils/sendMail.js index 93b8581..87e6cf4 100644 --- a/src/utils/sendMail.js +++ b/src/utils/sendMail.js @@ -15,7 +15,7 @@ async function sendMail(options) { console.log("mail sent") } catch (err) { if (err instanceof Error) { - console.log(err.message) + console.log("Error CATCHED"+err.message) throw new HttpException(500, "couldn't send mail") } } From 0e6cd5c2c204e23aa0c7380158ea1b291f2ef117 Mon Sep 17 00:00:00 2001 From: hardeylarning Date: Thu, 2 Mar 2023 15:08:39 +0100 Subject: [PATCH 15/57] updateAdminController added --- src/controller/admin.controller.js | 35 ++++++++++++++++++++++++++++-- src/utils/password-validator.js | 4 ++++ 2 files changed, 37 insertions(+), 2 deletions(-) create mode 100644 src/utils/password-validator.js diff --git a/src/controller/admin.controller.js b/src/controller/admin.controller.js index a19c9b2..f87a90c 100644 --- a/src/controller/admin.controller.js +++ b/src/controller/admin.controller.js @@ -3,7 +3,9 @@ import Admin from "../models/admin.model"; // import {adminModel} from "../models/admin.model"; // import adminModel from "../models/admin.model"; import { validateEmail } from "../utils/email-validator" +import { validateField } from "../utils/input-validator"; import generateToken from "../utils/jwt/generate-token"; +import { passwordValidator } from "../utils/password-validator"; import sendMail from "../utils/sendMail"; export const registerEmail = async (req, res, next) => { @@ -66,11 +68,40 @@ export const updateAdminRecord = async (req, res, next) => { message: "Invalid email", }); } - - const existingAdmin = + const {username, password} = req.body + + if (!validateField(username)) { + return res.json({ + status: "error", + message: "Username must not be less than 6 characters long", + }); + } + + const existingAdmin = await Admin.findOne({username}) + if (existingAdmin) { + return res.json({ + status: "error", + message: "Username has already been taken", + }); + } + + if (!passwordValidator(password)) { + return res.json({ + status: "error", + message: "Password must contain a number, a special character, an uppercase letter, and not less than 8 characters long", + }); + } + admin.username = username + admin.password = password + admin.isAccepted = true + await admin.save(); + return res.json({ + status: "success", + message: "Your account has been successfully created, you would be notified once your account is being activated.", + }); } else { diff --git a/src/utils/password-validator.js b/src/utils/password-validator.js new file mode 100644 index 0000000..352b726 --- /dev/null +++ b/src/utils/password-validator.js @@ -0,0 +1,4 @@ +export const passwordValidator = value => { + const pattern = /^(?=.*\d)(?=.*[!@#$%^&*])(?=.*[a-z])(?=.*[A-Z]).{8,}$/ + return pattern.test(value) +} \ No newline at end of file From 8244e594dbbac40e09cd549deabc4c8b157d1bb6 Mon Sep 17 00:00:00 2001 From: hardeylarning Date: Thu, 2 Mar 2023 15:12:45 +0100 Subject: [PATCH 16/57] admin-registration-continuation endpoint added --- src/controller/admin.controller.js | 6 +----- src/routes/admin.route.js | 3 ++- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/controller/admin.controller.js b/src/controller/admin.controller.js index f87a90c..21ec05b 100644 --- a/src/controller/admin.controller.js +++ b/src/controller/admin.controller.js @@ -32,7 +32,7 @@ export const registerEmail = async (req, res, next) => { to: email, subject: 'Admin account registration', text: `Dear ${email}, kindly follow this link in order to continue with your registration process as an admin! - sign up` + sign up` } await sendMail(mailOptions) @@ -111,10 +111,6 @@ export const updateAdminRecord = async (req, res, next) => { }); } - - - - } catch (err) { next(err) diff --git a/src/routes/admin.route.js b/src/routes/admin.route.js index 46ceb6a..186b135 100644 --- a/src/routes/admin.route.js +++ b/src/routes/admin.route.js @@ -1,9 +1,10 @@ import express from "express"; -import { registerEmail } from "../controller/admin.controller"; +import { registerEmail, updateAdminRecord } from "../controller/admin.controller"; const adminRouter = express.Router(); adminRouter.post("/admin-register-email", registerEmail) +adminRouter.put("/admin-registration-continuation", updateAdminRecord) export default adminRouter \ No newline at end of file From 34ebc9dc3a82fd9418d45ef320f6be2c2b1af2a2 Mon Sep 17 00:00:00 2001 From: hardeylarning Date: Thu, 2 Mar 2023 18:09:38 +0100 Subject: [PATCH 17/57] activateAdmin controller was added --- src/controller/admin.controller.js | 39 ++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/controller/admin.controller.js b/src/controller/admin.controller.js index 21ec05b..16c7655 100644 --- a/src/controller/admin.controller.js +++ b/src/controller/admin.controller.js @@ -117,3 +117,42 @@ export const updateAdminRecord = async (req, res, next) => { } } + +export const activateAdmin = async (req, res, next) => { + try { + const {id} = req.params.id + if (id) { + const admin = await Admin.findOne({id}) + if (!admin) { + return res.json({ + status: "error", + message: "No admin details found", + }); + } + if (admin.isVerified) { + return res.json({ + status: "error", + message: "Admin has already been verified", + }); + } + admin.isVerified = true + await admin.save() + return res.json({ + status: "success", + message: `${admin.username} account has now been activated.`, + }); + + } + else { + return res.json({ + status: "error", + message: "Invalid request", + }); + } + } + catch (err) { + next(err) + } +} + + From a106ec57789f0ddec4421dab74d8050dc804bab2 Mon Sep 17 00:00:00 2001 From: hardeylarning Date: Thu, 2 Mar 2023 18:17:07 +0100 Subject: [PATCH 18/57] registered activateAdmin to the route --- src/routes/admin.route.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/routes/admin.route.js b/src/routes/admin.route.js index 186b135..ba26377 100644 --- a/src/routes/admin.route.js +++ b/src/routes/admin.route.js @@ -1,10 +1,11 @@ import express from "express"; -import { registerEmail, updateAdminRecord } from "../controller/admin.controller"; +import { activateAdmin, registerEmail, updateAdminRecord } from "../controller/admin.controller"; const adminRouter = express.Router(); adminRouter.post("/admin-register-email", registerEmail) adminRouter.put("/admin-registration-continuation", updateAdminRecord) +adminRouter.put("/admin-activation/:id", activateAdmin) export default adminRouter \ No newline at end of file From 66fb60a85fb92afa39cf5b1e89c03faf41bc0bfd Mon Sep 17 00:00:00 2001 From: JEDIBASIL Date: Thu, 2 Mar 2023 22:46:01 +0100 Subject: [PATCH 19/57] added proper validation for phone number --- package-lock.json | 14 ++++++++++++++ package.json | 1 + src/validator_schema/createAccountShema.js | 9 ++++++++- 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index fa4c68b..ef36722 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "dotenv": "^16.0.3", "esm": "^3.2.25", "express": "^4.18.2", + "google-libphonenumber": "^3.2.32", "handlebars": "^4.7.7", "joi": "^17.8.3", "jsonwebtoken": "^9.0.0", @@ -1896,6 +1897,14 @@ "node": ">= 6" } }, + "node_modules/google-libphonenumber": { + "version": "3.2.32", + "resolved": "https://registry.npmjs.org/google-libphonenumber/-/google-libphonenumber-3.2.32.tgz", + "integrity": "sha512-mcNgakausov/B/eTgVeX8qc8IwWjRrupk9UzZZ/QDEvdh5fAjE7Aa211bkZpZj42zKkeS6MTT8avHUwjcLxuGQ==", + "engines": { + "node": ">=0.10" + } + }, "node_modules/handlebars": { "version": "4.7.7", "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", @@ -4834,6 +4843,11 @@ "is-glob": "^4.0.1" } }, + "google-libphonenumber": { + "version": "3.2.32", + "resolved": "https://registry.npmjs.org/google-libphonenumber/-/google-libphonenumber-3.2.32.tgz", + "integrity": "sha512-mcNgakausov/B/eTgVeX8qc8IwWjRrupk9UzZZ/QDEvdh5fAjE7Aa211bkZpZj42zKkeS6MTT8avHUwjcLxuGQ==" + }, "handlebars": { "version": "4.7.7", "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", diff --git a/package.json b/package.json index 9606b12..c49ee5a 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,7 @@ "dotenv": "^16.0.3", "esm": "^3.2.25", "express": "^4.18.2", + "google-libphonenumber": "^3.2.32", "handlebars": "^4.7.7", "joi": "^17.8.3", "jsonwebtoken": "^9.0.0", diff --git a/src/validator_schema/createAccountShema.js b/src/validator_schema/createAccountShema.js index 8ee0bb4..de074b6 100644 --- a/src/validator_schema/createAccountShema.js +++ b/src/validator_schema/createAccountShema.js @@ -1,10 +1,17 @@ import Joi from "joi"; +import { PhoneNumberUtil } from "google-libphonenumber" + + +const phoneUtil = PhoneNumberUtil.getInstance(); +import logger from "../utils/logger" const createAccountSchema = Joi.object({ username: Joi.string().required().min(3), email: Joi.string().email().required(), password: Joi.string().required(), - phoneNumber:Joi.number().required() + phoneNumber: Joi.string().pattern(/^\+?[1-9]\d{1,14}$/) // pattern for phone number validation + .required() + }) export default createAccountSchema \ No newline at end of file From 6f379f8301fa52a7c6c17f0b1a1fd0df606f4777 Mon Sep 17 00:00:00 2001 From: hardeylarning Date: Sat, 4 Mar 2023 08:12:31 +0100 Subject: [PATCH 20/57] fixed expection message --- src/controller/admin.controller.js | 84 +++++++++++++++--------------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/src/controller/admin.controller.js b/src/controller/admin.controller.js index 16c7655..ec170c6 100644 --- a/src/controller/admin.controller.js +++ b/src/controller/admin.controller.js @@ -17,10 +17,7 @@ export const registerEmail = async (req, res, next) => { const existingAdmin = await Admin.findOne({email}) if (existingAdmin) { - return res.json({ - status: "error", - message: "A registration link has been successfully sent to your email, kindly continue your registration from there.", - }); + throw new HttpException(400,"Email already exists, kindly login") } const admin = await Admin.create({ @@ -41,14 +38,10 @@ export const registerEmail = async (req, res, next) => { message: "A registration link has been successfully sent to your email, kindly continue your registration from there.", body: admin, }); - // return res.json({ - // status: "success", - // message: "A registration link has been successfully sent to your email, kindly continue your registration from there.", - // }); } else { - throw new HttpException(400,"Invaid Email!") + throw new HttpException(400,"Invalid Email!") } } catch (err) { @@ -63,34 +56,25 @@ export const updateAdminRecord = async (req, res, next) => { if (email && token) { const admin = await Admin.findOne({email}) if (!admin) { - return res.json({ - status: "error", - message: "Invalid email", - }); + throw new HttpException(400,"Invalid email") } const {username, password} = req.body if (!validateField(username)) { - return res.json({ - status: "error", - message: "Username must not be less than 6 characters long", - }); + throw new HttpException(400,"Username must not be less than 6 characters long") } const existingAdmin = await Admin.findOne({username}) if (existingAdmin) { - return res.json({ - status: "error", - message: "Username has already been taken", - }); + throw new HttpException(400,"Username has already been taken") } if (!passwordValidator(password)) { - return res.json({ - status: "error", - message: "Password must contain a number, a special character, an uppercase letter, and not less than 8 characters long", - }); + throw new HttpException( + 400, + "Password must contain a number, a special character, an uppercase letter, and not less than 8 characters long" + ) } admin.username = username @@ -105,10 +89,7 @@ export const updateAdminRecord = async (req, res, next) => { } else { - return res.json({ - status: "error", - message: "Invalid request", - }); + throw new HttpException(400,"Invalid request") } } @@ -117,23 +98,16 @@ export const updateAdminRecord = async (req, res, next) => { } } - export const activateAdmin = async (req, res, next) => { try { const {id} = req.params.id if (id) { const admin = await Admin.findOne({id}) if (!admin) { - return res.json({ - status: "error", - message: "No admin details found", - }); + throw new HttpException(400,"No admin details found") } if (admin.isVerified) { - return res.json({ - status: "error", - message: "Admin has already been verified", - }); + throw new HttpException(400,"Admin has already been verified") } admin.isVerified = true await admin.save() @@ -144,10 +118,7 @@ export const activateAdmin = async (req, res, next) => { } else { - return res.json({ - status: "error", - message: "Invalid request", - }); + throw new HttpException(400,"Invalid request") } } catch (err) { @@ -155,4 +126,33 @@ export const activateAdmin = async (req, res, next) => { } } +export const adminLogin = async (req, res, next) => { + try { + const {email, password} = req.body + + if (validateEmail(email)) { + const admin = await Admin.findOne({email}) + + if (!admin || !admin.isPasswordMatch(password)) { + throw new HttpException(400,"Incorrect Email or Password") + } + else if(!admin.isVerified) { + throw new HttpException(400,"Unverified account") + } + else { + return res.json({ + status: "success", + message: `Dear ${admin.username}, welcome to the admin dashboard`, + }); + } + } + else { + throw new HttpException(400,"Invalid Email!") + } + + } catch (err) { + next(err) + } +} + From 288ed6339941559a3989b62b53646d4fcc489b20 Mon Sep 17 00:00:00 2001 From: hardeylarning Date: Sat, 4 Mar 2023 08:32:40 +0100 Subject: [PATCH 21/57] added passwordHash and comparePassword function --- src/controller/admin.controller.js | 2 +- src/routes/admin.route.js | 3 ++- src/utils/password-validator.js | 13 +++++++++++++ 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/controller/admin.controller.js b/src/controller/admin.controller.js index ec170c6..4e059ae 100644 --- a/src/controller/admin.controller.js +++ b/src/controller/admin.controller.js @@ -130,7 +130,7 @@ export const adminLogin = async (req, res, next) => { try { const {email, password} = req.body - if (validateEmail(email)) { + if (email && password && validateEmail(email)) { const admin = await Admin.findOne({email}) if (!admin || !admin.isPasswordMatch(password)) { diff --git a/src/routes/admin.route.js b/src/routes/admin.route.js index ba26377..4eb2dab 100644 --- a/src/routes/admin.route.js +++ b/src/routes/admin.route.js @@ -1,11 +1,12 @@ import express from "express"; -import { activateAdmin, registerEmail, updateAdminRecord } from "../controller/admin.controller"; +import { activateAdmin, adminLogin, registerEmail, updateAdminRecord } from "../controller/admin.controller"; const adminRouter = express.Router(); adminRouter.post("/admin-register-email", registerEmail) adminRouter.put("/admin-registration-continuation", updateAdminRecord) adminRouter.put("/admin-activation/:id", activateAdmin) +adminRouter.post("/login", adminLogin) export default adminRouter \ No newline at end of file diff --git a/src/utils/password-validator.js b/src/utils/password-validator.js index 352b726..4fa1c48 100644 --- a/src/utils/password-validator.js +++ b/src/utils/password-validator.js @@ -1,4 +1,17 @@ +import bcrypt from 'bcrypt' + export const passwordValidator = value => { const pattern = /^(?=.*\d)(?=.*[!@#$%^&*])(?=.*[a-z])(?=.*[A-Z]).{8,}$/ return pattern.test(value) +} + +export const passwordHash = async password => { + const salt = await bcrypt.genSalt(10) + const hashPassword = await bcrypt.hash(password, salt) + return hashPassword +} + +export const comparePassword = async (password, hashedPassword) => { + const result = await bcrypt.compare(password, hashedPassword) + return result } \ No newline at end of file From d156fe3a42525930f89b49434ba3602a48d5f975 Mon Sep 17 00:00:00 2001 From: hardeylarning Date: Sat, 4 Mar 2023 11:21:03 +0100 Subject: [PATCH 22/57] added verify-token --- src/controller/admin.controller.js | 52 ++++++++++++++---------------- src/models/admin.model.js | 4 +-- src/routes/admin.route.js | 2 +- src/utils/jwt/verify-token.js | 8 +++++ 4 files changed, 36 insertions(+), 30 deletions(-) create mode 100644 src/utils/jwt/verify-token.js diff --git a/src/controller/admin.controller.js b/src/controller/admin.controller.js index 4e059ae..3ba0851 100644 --- a/src/controller/admin.controller.js +++ b/src/controller/admin.controller.js @@ -1,7 +1,5 @@ import HttpException from "../exceptions/HttpException" import Admin from "../models/admin.model"; -// import {adminModel} from "../models/admin.model"; -// import adminModel from "../models/admin.model"; import { validateEmail } from "../utils/email-validator" import { validateField } from "../utils/input-validator"; import generateToken from "../utils/jwt/generate-token"; @@ -9,34 +7,29 @@ import { passwordValidator } from "../utils/password-validator"; import sendMail from "../utils/sendMail"; export const registerEmail = async (req, res, next) => { + const {email} = req.body try { - const {email} = req.body - if (validateEmail(email)) { - const existingAdmin = await Admin.findOne({email}) - if (existingAdmin) { throw new HttpException(400,"Email already exists, kindly login") } - - const admin = await Admin.create({ - email - }); - - let mailOptions = { + const token = generateToken(email) + const mailOption = { from: process.env.MAIL_USER, to: email, subject: 'Admin account registration', text: `Dear ${email}, kindly follow this link in order to continue with your registration process as an admin! - sign up` + sign up` } - await sendMail(mailOptions) + await sendMail(mailOption) + const admin = await Admin.create({ + email + }); return res.json({ status: "success", message: "A registration link has been successfully sent to your email, kindly continue your registration from there.", - body: admin, }); } else { @@ -58,9 +51,8 @@ export const updateAdminRecord = async (req, res, next) => { if (!admin) { throw new HttpException(400,"Invalid email") } - const {username, password} = req.body - + if (!validateField(username)) { throw new HttpException(400,"Username must not be less than 6 characters long") } @@ -100,9 +92,9 @@ export const updateAdminRecord = async (req, res, next) => { export const activateAdmin = async (req, res, next) => { try { - const {id} = req.params.id + const id = req.params.id if (id) { - const admin = await Admin.findOne({id}) + const admin = await Admin.findOne({_id:id}) if (!admin) { throw new HttpException(400,"No admin details found") } @@ -133,18 +125,24 @@ export const adminLogin = async (req, res, next) => { if (email && password && validateEmail(email)) { const admin = await Admin.findOne({email}) - if (!admin || !admin.isPasswordMatch(password)) { + if (!admin) { throw new HttpException(400,"Incorrect Email or Password") } - else if(!admin.isVerified) { - throw new HttpException(400,"Unverified account") + + const passwordResult = await admin.isPasswordMatch(password) + console.log("Password Result: ", passwordResult); + if (!passwordResult) { + console.log("Inside here"); + throw new HttpException(400,"Incorrect Email or Password") } - else { - return res.json({ - status: "success", - message: `Dear ${admin.username}, welcome to the admin dashboard`, - }); + if(!admin.isVerified) { + throw new HttpException(400,"Unverified account") } + return res.json({ + status: "success", + message: `Dear ${admin.username}, welcome to the admin dashboard`, + }); + } else { throw new HttpException(400,"Invalid Email!") diff --git a/src/models/admin.model.js b/src/models/admin.model.js index f780ead..9737f38 100644 --- a/src/models/admin.model.js +++ b/src/models/admin.model.js @@ -17,7 +17,7 @@ const adminSchema = new mongoose.Schema({ }, password: { type: String, - select: false, + // select: false, }, isVerified: { type: Boolean, @@ -47,5 +47,5 @@ adminSchema.methods.isPasswordMatch = async function (password) { return await bcrypt.compare(password, this.password); }; -const Admin = mongoose.model("Admin2", adminSchema); +const Admin = mongoose.model("Admin3", adminSchema); export default Admin; diff --git a/src/routes/admin.route.js b/src/routes/admin.route.js index 4eb2dab..91ed25c 100644 --- a/src/routes/admin.route.js +++ b/src/routes/admin.route.js @@ -5,7 +5,7 @@ const adminRouter = express.Router(); adminRouter.post("/admin-register-email", registerEmail) adminRouter.put("/admin-registration-continuation", updateAdminRecord) -adminRouter.put("/admin-activation/:id", activateAdmin) +adminRouter.patch("/admin-activation/:id", activateAdmin) adminRouter.post("/login", adminLogin) diff --git a/src/utils/jwt/verify-token.js b/src/utils/jwt/verify-token.js new file mode 100644 index 0000000..8ec8bcf --- /dev/null +++ b/src/utils/jwt/verify-token.js @@ -0,0 +1,8 @@ +import jwt from "jsonwebtoken" + +export const verifyToken = token => { + return jwt.verify(token, process.env.JWT_KEY, (error, decoded) => { + if (error) return false + return decoded + }) +} \ No newline at end of file From eb2f47b943a74f5c0c690916e1789bcbecaf361c Mon Sep 17 00:00:00 2001 From: hardeylarning Date: Sat, 4 Mar 2023 11:42:50 +0100 Subject: [PATCH 23/57] fixed token comparison --- src/controller/admin.controller.js | 9 +++++++-- src/models/admin.model.js | 2 +- src/utils/jwt/verify-token.js | 7 +++++++ 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/controller/admin.controller.js b/src/controller/admin.controller.js index 3ba0851..84a091e 100644 --- a/src/controller/admin.controller.js +++ b/src/controller/admin.controller.js @@ -3,6 +3,7 @@ import Admin from "../models/admin.model"; import { validateEmail } from "../utils/email-validator" import { validateField } from "../utils/input-validator"; import generateToken from "../utils/jwt/generate-token"; +import { extractEmailFromToken } from "../utils/jwt/verify-token"; import { passwordValidator } from "../utils/password-validator"; import sendMail from "../utils/sendMail"; @@ -19,8 +20,8 @@ export const registerEmail = async (req, res, next) => { from: process.env.MAIL_USER, to: email, subject: 'Admin account registration', - text: `Dear ${email}, kindly follow this link in order to continue with your registration process as an admin! - sign up` + text: `Dear ${email}, kindly follow this link in order to continue with your registration process as an admin!\n + "http://localhost:8080/api/v1/admin/admin-registration-continuation?email=${email}&token=${token}"` } await sendMail(mailOption) const admin = await Admin.create({ @@ -30,6 +31,7 @@ export const registerEmail = async (req, res, next) => { return res.json({ status: "success", message: "A registration link has been successfully sent to your email, kindly continue your registration from there.", + token: token }); } else { @@ -47,6 +49,9 @@ export const updateAdminRecord = async (req, res, next) => { const {email, token} = req.query if (email && token) { + if (!extractEmailFromToken(token, email)) { + throw new HttpException(400,"Invalid or expired token") + } const admin = await Admin.findOne({email}) if (!admin) { throw new HttpException(400,"Invalid email") diff --git a/src/models/admin.model.js b/src/models/admin.model.js index 9737f38..8b4ab4d 100644 --- a/src/models/admin.model.js +++ b/src/models/admin.model.js @@ -47,5 +47,5 @@ adminSchema.methods.isPasswordMatch = async function (password) { return await bcrypt.compare(password, this.password); }; -const Admin = mongoose.model("Admin3", adminSchema); +const Admin = mongoose.model("Admin5", adminSchema); export default Admin; diff --git a/src/utils/jwt/verify-token.js b/src/utils/jwt/verify-token.js index 8ec8bcf..685ea7c 100644 --- a/src/utils/jwt/verify-token.js +++ b/src/utils/jwt/verify-token.js @@ -5,4 +5,11 @@ export const verifyToken = token => { if (error) return false return decoded }) +} + +export const extractEmailFromToken = (token, email) => { + const extractedEmail = verifyToken(token); + console.log("Extracted: ", extractedEmail); + console.log("Extracted Email: ", extractedEmail.email); + return extractedEmail.email == email } \ No newline at end of file From cf25b46dfdec0d7526a1f6a0f809257922e59ad7 Mon Sep 17 00:00:00 2001 From: Opeyemi0002 Date: Sat, 4 Mar 2023 15:47:43 +0100 Subject: [PATCH 24/57] blog --- .gitignore | 3 +- package.json | 6 ++++ src/app.js | 13 ++++---- src/auth/authentication.js | 21 ++++++++++++ src/auth/base.auth.js | 4 +-- src/auth/isAdmin.js | 0 src/controller/postcontroller.js | 26 +++++++++++++++ src/controller/user.controller.js | 3 +- src/models/blogmodel.js | 37 ++++++++++++++++++++++ src/models/user.model.js | 2 +- src/routes/postroute.js | 12 +++++++ src/routes/user.route.js | 7 ++-- src/validator_schema/createAccountShema.js | 7 ---- src/validator_schema/inputShema.js | 15 +++++++++ 14 files changed, 135 insertions(+), 21 deletions(-) create mode 100644 src/auth/authentication.js create mode 100644 src/auth/isAdmin.js create mode 100644 src/controller/postcontroller.js create mode 100644 src/models/blogmodel.js create mode 100644 src/routes/postroute.js delete mode 100644 src/validator_schema/createAccountShema.js create mode 100644 src/validator_schema/inputShema.js diff --git a/.gitignore b/.gitignore index 722f3f2..5dec8cc 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,5 @@ assets/uploads dist package-lock.json yarn-error.log -/tmp \ No newline at end of file +/tmp +check.js \ No newline at end of file diff --git a/package.json b/package.json index 1b94796..faa0966 100644 --- a/package.json +++ b/package.json @@ -1,4 +1,10 @@ { + "main": "index.js", + "repository": { + "type": "git", + "url": "git+https://github.com/Ayagigs/stakepro.git" + }, + "license": "ISC", "dependencies": { "bcrypt": "^5.1.0", "cors": "^2.8.5", diff --git a/src/app.js b/src/app.js index f56bc5f..aeeca9f 100644 --- a/src/app.js +++ b/src/app.js @@ -1,16 +1,17 @@ import express from "express" import userRouter from "./routes/user.route" import errorMiddleware from "./middleware/error.middleware" -const app = express() +import postRouter from "./routes/postroute.js"; +const app = express(); -const port = 8080 -const path = "/api/v1" +const port = 8080; +const path = "/api/v1"; -app.use(express.json()) +app.use(express.json()); // add routes here -app.use(`${path}/user`, userRouter) - +app.use(`${path}/user`, userRouter); +app.use (`${path}/post`, postRouter); app.use(errorMiddleware) diff --git a/src/auth/authentication.js b/src/auth/authentication.js new file mode 100644 index 0000000..abd1d22 --- /dev/null +++ b/src/auth/authentication.js @@ -0,0 +1,21 @@ + +import jwt from "jsonwebtoken" +import { ACCESS_TOKEN } from "../config" + +const checkToken = async (req, res, next) => { + const authHeader = req.headers['authorization'] + const token = authHeader && authHeader.split(' ')[1] + if (!token) { + return res.status(401).json({ error: 'Unauthorized' }) + } + try { + const decodedToken = jwt.verify(token, ACCESS_TOKEN) + + req.userAuth =decodedToken.id + + next() + } catch (err) { + return res.status(401).json({ error: 'Unauthorized' }) + } +} +export default checkToken; diff --git a/src/auth/base.auth.js b/src/auth/base.auth.js index bde3b65..9dbd10e 100644 --- a/src/auth/base.auth.js +++ b/src/auth/base.auth.js @@ -7,7 +7,7 @@ const hasToken = async (req, res, next) => { const token = autHeader && autHeader.split(" ")[1] if (!token) throw new HttpException(401, "no auth key") const verifiedToken = jwt.verify(ACCESS_TOKEN) - return verifiedToken + return verifiedToken; } -export default hasToken \ No newline at end of file +export default hasToken; \ No newline at end of file diff --git a/src/auth/isAdmin.js b/src/auth/isAdmin.js new file mode 100644 index 0000000..e69de29 diff --git a/src/controller/postcontroller.js b/src/controller/postcontroller.js new file mode 100644 index 0000000..36fbd15 --- /dev/null +++ b/src/controller/postcontroller.js @@ -0,0 +1,26 @@ +import Post from "../models/blogmodel.js"; +import userModel from "../models/user.model.js"; + +export const writeBlog = async (req,res) => { + const {displayname, title, content} = req.body; + try{ + const blogPoster = await userModel.findById(req.userAuth); + + await Post.create({ + displayname, + title, + content, + user:blogPoster._id + }); + res.status(201).json({ + message: "your blog has been posted successfully" + }); + + + + + + }catch(error) { + res.json(error.message); + } +} \ No newline at end of file diff --git a/src/controller/user.controller.js b/src/controller/user.controller.js index fbf3b6c..d2f5c33 100644 --- a/src/controller/user.controller.js +++ b/src/controller/user.controller.js @@ -6,4 +6,5 @@ const createAccount = async (req, res, next) => { } } -export { createAccount } \ No newline at end of file +export { createAccount } + diff --git a/src/models/blogmodel.js b/src/models/blogmodel.js new file mode 100644 index 0000000..06e0929 --- /dev/null +++ b/src/models/blogmodel.js @@ -0,0 +1,37 @@ +import mongoose from "mongoose"; + +const blogSchema = new mongoose.Schema({ + displayname:{ + type:String, + required:[true,"include your displayname"] + }, + title:{ + type:String, + required:[true,"include title"], + trim:true + }, + content:{ + type:String, + required:[true,"write content"] + }, + postviews: [{ + type:mongoose.Schema.Types.ObjectId, + ref: "User" + }], + + user: [{ + type:mongoose.Schema.Types.ObjectId, + ref: "User" + }], + photo: [{ + type:String, + + }] +}, { + timestamps:true, + toJSON:{virtuals:true} +}) + +const Post = mongoose.model('Post', blogSchema); + +export default Post; \ No newline at end of file diff --git a/src/models/user.model.js b/src/models/user.model.js index 29b3221..8bd61b0 100644 --- a/src/models/user.model.js +++ b/src/models/user.model.js @@ -1,6 +1,6 @@ import moment from 'moment'; import bcrypt from "bcrypt" -import { Schema } from 'mongoose'; +import { Schema, model } from 'mongoose'; const userSchema = new Schema({ email: { diff --git a/src/routes/postroute.js b/src/routes/postroute.js new file mode 100644 index 0000000..654650b --- /dev/null +++ b/src/routes/postroute.js @@ -0,0 +1,12 @@ +import express from "express"; +import { writeBlog } from "../controller/postcontroller.js"; +import validatorMiddleware from "../middleware/validator.middleware.js"; +import {writeBlogSchema} from "../validator_schema/inputShema.js"; +import checkToken from "../auth/authentication.js"; + +const postRouter = express.Router(); + +// create Blog +postRouter.route("/create-blog").post(checkToken, validatorMiddleware(writeBlogSchema, "body"), writeBlog); + +export default postRouter; diff --git a/src/routes/user.route.js b/src/routes/user.route.js index 989be59..0f28b36 100644 --- a/src/routes/user.route.js +++ b/src/routes/user.route.js @@ -1,11 +1,12 @@ -import { createAccount } from "../controller/user.controller"; +import { createAccount} from "../controller/user.controller"; import { Router } from "express" import validatorMiddleware from "../middleware/validator.middleware"; -import createAccountSchema from "../validator_schema/createAccountShema"; +import {createAccountSchema} from "../validator_schema/inputShema.js"; + const userRouter = Router({ mergeParams: true }) -userRouter.route("/create").post(validatorMiddleware(createAccountSchema, "body"), createAccount) +userRouter.route("/create").post(validatorMiddleware(createAccountSchema, "body"), createAccount); export default userRouter \ No newline at end of file diff --git a/src/validator_schema/createAccountShema.js b/src/validator_schema/createAccountShema.js deleted file mode 100644 index c3e0759..0000000 --- a/src/validator_schema/createAccountShema.js +++ /dev/null @@ -1,7 +0,0 @@ -import Joi from "joi"; - -const createAccountSchema = Joi.object({ - email: Joi.string().email().required(), - password: Joi.string().required() -}) -export default createAccountSchema \ No newline at end of file diff --git a/src/validator_schema/inputShema.js b/src/validator_schema/inputShema.js new file mode 100644 index 0000000..e1468a8 --- /dev/null +++ b/src/validator_schema/inputShema.js @@ -0,0 +1,15 @@ +import Joi from "joi"; + +export const createAccountSchema = Joi.object({ + firstname: Joi.string().required(), + lastname: Joi.string().required(), + age:Joi.number().required(), + email: Joi.string().email().required(), + password: Joi.string().required() +}); + +export const writeBlogSchema = Joi.object({ + displayname: Joi.string().required(), + title: Joi.string().required(), + content: Joi.string().required() +}); From 3d229e6268304c7f93ba2eb50f1fff57e75a7240 Mon Sep 17 00:00:00 2001 From: JEDIBASIL Date: Sat, 4 Mar 2023 19:07:33 +0100 Subject: [PATCH 25/57] added google auth --- .vscode/settings.json | 2 +- package-lock.json | 166 +++++++++++++++++++-- package.json | 4 +- src/app.js | 4 + src/config/index.js | 2 + src/models/user.model.js | 6 +- src/routes/user.route.js | 11 ++ src/strategy/google.strategy.js | 49 ++++++ src/validator_schema/createAccountShema.js | 4 - 9 files changed, 224 insertions(+), 24 deletions(-) create mode 100644 src/strategy/google.strategy.js diff --git a/.vscode/settings.json b/.vscode/settings.json index 28a51d5..a1c4910 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,3 @@ { - "compile-hero.disable-compile-files-on-did-save-code": false + "compile-hero.disable-compile-files-on-did-save-code": true } \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index ef36722..9248a6f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,6 @@ "dotenv": "^16.0.3", "esm": "^3.2.25", "express": "^4.18.2", - "google-libphonenumber": "^3.2.32", "handlebars": "^4.7.7", "joi": "^17.8.3", "jsonwebtoken": "^9.0.0", @@ -18,6 +17,9 @@ "mongoose": "^6.9.1", "nodemailer": "^6.9.1", "nodemon": "^2.0.20", + "passport": "^0.6.0", + "passport-google-oauth2": "^0.2.0", + "passport-google-oauth20": "^2.0.0", "randomstring": "^1.2.3", "uuid": "^9.0.0", "winston": "^3.8.2" @@ -1316,6 +1318,14 @@ } ] }, + "node_modules/base64url": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/base64url/-/base64url-3.0.1.tgz", + "integrity": "sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/bcrypt": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.1.0.tgz", @@ -1897,14 +1907,6 @@ "node": ">= 6" } }, - "node_modules/google-libphonenumber": { - "version": "3.2.32", - "resolved": "https://registry.npmjs.org/google-libphonenumber/-/google-libphonenumber-3.2.32.tgz", - "integrity": "sha512-mcNgakausov/B/eTgVeX8qc8IwWjRrupk9UzZZ/QDEvdh5fAjE7Aa211bkZpZj42zKkeS6MTT8avHUwjcLxuGQ==", - "engines": { - "node": ">=0.10" - } - }, "node_modules/handlebars": { "version": "4.7.7", "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", @@ -2620,6 +2622,11 @@ "set-blocking": "^2.0.0" } }, + "node_modules/oauth": { + "version": "0.9.15", + "resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz", + "integrity": "sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA==" + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -2671,6 +2678,69 @@ "node": ">= 0.8" } }, + "node_modules/passport": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/passport/-/passport-0.6.0.tgz", + "integrity": "sha512-0fe+p3ZnrWRW74fe8+SvCyf4a3Pb2/h7gFkQ8yTJpAO50gDzlfjZUZTO1k5Eg9kUct22OxHLqDZoKUWRHOh9ug==", + "dependencies": { + "passport-strategy": "1.x.x", + "pause": "0.0.1", + "utils-merge": "^1.0.1" + }, + "engines": { + "node": ">= 0.4.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jaredhanson" + } + }, + "node_modules/passport-google-oauth2": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/passport-google-oauth2/-/passport-google-oauth2-0.2.0.tgz", + "integrity": "sha512-62EdPtbfVdc55nIXi0p1WOa/fFMM8v/M8uQGnbcXA4OexZWCnfsEi3wo2buag+Is5oqpuHzOtI64JpHk0Xi5RQ==", + "dependencies": { + "passport-oauth2": "^1.1.2" + } + }, + "node_modules/passport-google-oauth20": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/passport-google-oauth20/-/passport-google-oauth20-2.0.0.tgz", + "integrity": "sha512-KSk6IJ15RoxuGq7D1UKK/8qKhNfzbLeLrG3gkLZ7p4A6DBCcv7xpyQwuXtWdpyR0+E0mwkpjY1VfPOhxQrKzdQ==", + "dependencies": { + "passport-oauth2": "1.x.x" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/passport-oauth2": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.6.1.tgz", + "integrity": "sha512-ZbV43Hq9d/SBSYQ22GOiglFsjsD1YY/qdiptA+8ej+9C1dL1TVB+mBE5kDH/D4AJo50+2i8f4bx0vg4/yDDZCQ==", + "dependencies": { + "base64url": "3.x.x", + "oauth": "0.9.x", + "passport-strategy": "1.x.x", + "uid2": "0.0.x", + "utils-merge": "1.x.x" + }, + "engines": { + "node": ">= 0.4.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jaredhanson" + } + }, + "node_modules/passport-strategy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", + "integrity": "sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==", + "engines": { + "node": ">= 0.4.0" + } + }, "node_modules/path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", @@ -2684,6 +2754,11 @@ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" }, + "node_modules/pause": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", + "integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg==" + }, "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", @@ -3189,6 +3264,11 @@ "node": ">=0.8.0" } }, + "node_modules/uid2": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/uid2/-/uid2-0.0.4.tgz", + "integrity": "sha512-IevTus0SbGwQzYh3+fRsAMTVVPOoIVufzacXcHPmdlle1jUpq7BRL+mw3dgeLanvGZdwwbWhRV6XrcFNdBmjWA==" + }, "node_modules/undefsafe": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", @@ -4398,6 +4478,11 @@ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" }, + "base64url": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/base64url/-/base64url-3.0.1.tgz", + "integrity": "sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==" + }, "bcrypt": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.1.0.tgz", @@ -4843,11 +4928,6 @@ "is-glob": "^4.0.1" } }, - "google-libphonenumber": { - "version": "3.2.32", - "resolved": "https://registry.npmjs.org/google-libphonenumber/-/google-libphonenumber-3.2.32.tgz", - "integrity": "sha512-mcNgakausov/B/eTgVeX8qc8IwWjRrupk9UzZZ/QDEvdh5fAjE7Aa211bkZpZj42zKkeS6MTT8avHUwjcLxuGQ==" - }, "handlebars": { "version": "4.7.7", "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", @@ -5380,6 +5460,11 @@ "set-blocking": "^2.0.0" } }, + "oauth": { + "version": "0.9.15", + "resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz", + "integrity": "sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA==" + }, "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -5419,6 +5504,49 @@ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" }, + "passport": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/passport/-/passport-0.6.0.tgz", + "integrity": "sha512-0fe+p3ZnrWRW74fe8+SvCyf4a3Pb2/h7gFkQ8yTJpAO50gDzlfjZUZTO1k5Eg9kUct22OxHLqDZoKUWRHOh9ug==", + "requires": { + "passport-strategy": "1.x.x", + "pause": "0.0.1", + "utils-merge": "^1.0.1" + } + }, + "passport-google-oauth2": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/passport-google-oauth2/-/passport-google-oauth2-0.2.0.tgz", + "integrity": "sha512-62EdPtbfVdc55nIXi0p1WOa/fFMM8v/M8uQGnbcXA4OexZWCnfsEi3wo2buag+Is5oqpuHzOtI64JpHk0Xi5RQ==", + "requires": { + "passport-oauth2": "^1.1.2" + } + }, + "passport-google-oauth20": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/passport-google-oauth20/-/passport-google-oauth20-2.0.0.tgz", + "integrity": "sha512-KSk6IJ15RoxuGq7D1UKK/8qKhNfzbLeLrG3gkLZ7p4A6DBCcv7xpyQwuXtWdpyR0+E0mwkpjY1VfPOhxQrKzdQ==", + "requires": { + "passport-oauth2": "1.x.x" + } + }, + "passport-oauth2": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.6.1.tgz", + "integrity": "sha512-ZbV43Hq9d/SBSYQ22GOiglFsjsD1YY/qdiptA+8ej+9C1dL1TVB+mBE5kDH/D4AJo50+2i8f4bx0vg4/yDDZCQ==", + "requires": { + "base64url": "3.x.x", + "oauth": "0.9.x", + "passport-strategy": "1.x.x", + "uid2": "0.0.x", + "utils-merge": "1.x.x" + } + }, + "passport-strategy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", + "integrity": "sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==" + }, "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", @@ -5429,6 +5557,11 @@ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" }, + "pause": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", + "integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg==" + }, "picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", @@ -5801,6 +5934,11 @@ "integrity": "sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==", "optional": true }, + "uid2": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/uid2/-/uid2-0.0.4.tgz", + "integrity": "sha512-IevTus0SbGwQzYh3+fRsAMTVVPOoIVufzacXcHPmdlle1jUpq7BRL+mw3dgeLanvGZdwwbWhRV6XrcFNdBmjWA==" + }, "undefsafe": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", diff --git a/package.json b/package.json index c49ee5a..1704fc3 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,6 @@ "dotenv": "^16.0.3", "esm": "^3.2.25", "express": "^4.18.2", - "google-libphonenumber": "^3.2.32", "handlebars": "^4.7.7", "joi": "^17.8.3", "jsonwebtoken": "^9.0.0", @@ -13,6 +12,9 @@ "mongoose": "^6.9.1", "nodemailer": "^6.9.1", "nodemon": "^2.0.20", + "passport": "^0.6.0", + "passport-google-oauth2": "^0.2.0", + "passport-google-oauth20": "^2.0.0", "randomstring": "^1.2.3", "uuid": "^9.0.0", "winston": "^3.8.2" diff --git a/src/app.js b/src/app.js index 3f95a77..ff6baea 100644 --- a/src/app.js +++ b/src/app.js @@ -2,6 +2,8 @@ import express from "express" import userRouter from "./routes/user.route" import errorMiddleware from "./middleware/error.middleware" import { connectDB } from "./config" +import passport from "passport"; +require("./strategy/google.strategy") const app = express() @@ -10,6 +12,8 @@ const path = "/api/v1" connectDB() app.use(express.json()) +app.use(passport.initialize()); + // add routes here app.use(`${path}/user`, userRouter) diff --git a/src/config/index.js b/src/config/index.js index 1c277d4..35b9b6e 100644 --- a/src/config/index.js +++ b/src/config/index.js @@ -34,4 +34,6 @@ export const { MAIL_PASS, MAIL_USER, WEB_URL, + GOOGLE_CLIENT_ID, + GOOGLE_CLIENT_SECRET, } = process.env; \ No newline at end of file diff --git a/src/models/user.model.js b/src/models/user.model.js index b2b5f0f..7f814fe 100644 --- a/src/models/user.model.js +++ b/src/models/user.model.js @@ -9,21 +9,19 @@ const userSchema = new Schema({ unique: true, lowercase: true }, + googleId:String, username: { type: String, required: true, - unique: true, lowercase: true, trim: true }, password: { type: String, - required: true, select: false, }, phoneNumber: { - type: Number, - required: true, + type: String, }, isVerified: { type: Boolean, diff --git a/src/routes/user.route.js b/src/routes/user.route.js index cc0b495..c2f523f 100644 --- a/src/routes/user.route.js +++ b/src/routes/user.route.js @@ -13,9 +13,11 @@ import verifyAccountSchema from "../validator_schema/verifyAccountSchema"; import loginSchema from "../validator_schema/loginSchema"; import resetPasswordSchema from "../validator_schema/resetPasswordSchema"; import resendVerificationSchema from "../validator_schema/resendVerificationSchema"; +import passport from "passport" const userRouter = Router({ mergeParams: true }); + userRouter .route("/create") .post( @@ -23,6 +25,15 @@ userRouter createAccount ); +userRouter + .route("/auth/google") + .get(passport.authenticate('google', { scope: ['profile', 'email', 'https://www.googleapis.com/auth/user.phonenumbers.read'], session: false })); + +userRouter.route('/auth/google/callback') + .get(passport.authenticate('google', { failureRedirect: '/login', session: false }), (req, res) => { + res.redirect('http://127.0.0.1:1420/'); + }); + userRouter .route("/verify") .post( diff --git a/src/strategy/google.strategy.js b/src/strategy/google.strategy.js new file mode 100644 index 0000000..fbe9eb7 --- /dev/null +++ b/src/strategy/google.strategy.js @@ -0,0 +1,49 @@ +import passport from "passport"; +import { Strategy as GoogleStrategy } from "passport-google-oauth20"; +import { GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET } from "../config"; +import userModel from "../models/user.model"; + +passport.use( + new GoogleStrategy( + { + clientID: GOOGLE_CLIENT_ID, + clientSecret: GOOGLE_CLIENT_SECRET, + callbackURL: "http://localhost:8080/api/v1/user/auth/google/callback", + passReqToCallback: true, + }, + async (req, accessToken, refreshToken, profile, done) => { + try { + const googleuser = { + googleId: profile.id, + username: profile._json.email.replace(/@.+/, ""), + email: profile._json.email, + }; + + let user = await userModel.findOne({ + $or: [{ username: googleuser.username }, { email: googleuser.email }], + }); + if (!user) user = await userModel.create(googleuser); + else { + user = await userModel.findOneAndUpdate( + { googleId: googleuser.googleId }, + googleuser, + { new: true } + ); + } + return done(null, profile); + } catch (error) { + return done(error); + } + } + ) +); + +passport.serializeUser((user, done) => { + done(null, user.id); +}); + +passport.deserializeUser((id, done) => { + userModel.findById(id, (err, user) => { + done(err, user); + }); +}); diff --git a/src/validator_schema/createAccountShema.js b/src/validator_schema/createAccountShema.js index de074b6..205c455 100644 --- a/src/validator_schema/createAccountShema.js +++ b/src/validator_schema/createAccountShema.js @@ -1,10 +1,6 @@ import Joi from "joi"; -import { PhoneNumberUtil } from "google-libphonenumber" -const phoneUtil = PhoneNumberUtil.getInstance(); -import logger from "../utils/logger" - const createAccountSchema = Joi.object({ username: Joi.string().required().min(3), email: Joi.string().email().required(), From 5828f6fb8ff33cbd794aed9c2b978aa9c521c37b Mon Sep 17 00:00:00 2001 From: JEDIBASIL Date: Sat, 4 Mar 2023 19:26:09 +0100 Subject: [PATCH 26/57] refactored user auth --- src/controller/user.controller.js | 8 -------- src/validator_schema/createAccountShema.js | 4 ++-- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/src/controller/user.controller.js b/src/controller/user.controller.js index 1963672..f7d47ae 100644 --- a/src/controller/user.controller.js +++ b/src/controller/user.controller.js @@ -14,14 +14,6 @@ export async function createAccount(req, res, next) { const emailTaken = await userModel.findOne({ email: data.email }); if (emailTaken) throw new HttpException(409, "email taken"); - const userTaken = await userModel.findOne({ username: data.username }); - if (userTaken) throw new HttpException(409, "username taken"); - - const phoneNumberTaken = await userModel.findOne({ - phoneNumber: data.phoneNumber, - }); - if (phoneNumberTaken) throw new HttpException(409, "phone number taken"); - const newAccount = await userModel.create({ ...data }); if (!newAccount) throw new HttpException(500, "an error occurred"); diff --git a/src/validator_schema/createAccountShema.js b/src/validator_schema/createAccountShema.js index 205c455..98f6f86 100644 --- a/src/validator_schema/createAccountShema.js +++ b/src/validator_schema/createAccountShema.js @@ -5,8 +5,8 @@ const createAccountSchema = Joi.object({ username: Joi.string().required().min(3), email: Joi.string().email().required(), password: Joi.string().required(), - phoneNumber: Joi.string().pattern(/^\+?[1-9]\d{1,14}$/) // pattern for phone number validation - .required() + // phoneNumber: Joi.string().pattern(/^\+?[1-9]\d{1,14}$/) // pattern for phone number validation + // .required() }) From 61d35085a09dacf0de0f14ac4629a8de29acdd8e Mon Sep 17 00:00:00 2001 From: JEDIBASIL Date: Sun, 5 Mar 2023 17:04:02 +0100 Subject: [PATCH 27/57] updated code --- src/app.js | 1 - src/controller/user.controller.js | 4 +- src/routes/user.route.js | 14 +- .../google.strategy.js | 19 +-- thunder-collection_Stakepro_postman.json | 144 ++++++++++++++++++ 5 files changed, 162 insertions(+), 20 deletions(-) rename src/{strategy => strategies}/google.strategy.js (75%) create mode 100644 thunder-collection_Stakepro_postman.json diff --git a/src/app.js b/src/app.js index ff6baea..ca7e3c1 100644 --- a/src/app.js +++ b/src/app.js @@ -3,7 +3,6 @@ import userRouter from "./routes/user.route" import errorMiddleware from "./middleware/error.middleware" import { connectDB } from "./config" import passport from "passport"; -require("./strategy/google.strategy") const app = express() diff --git a/src/controller/user.controller.js b/src/controller/user.controller.js index f7d47ae..2506845 100644 --- a/src/controller/user.controller.js +++ b/src/controller/user.controller.js @@ -43,7 +43,7 @@ export async function loginAccount(req, res, next) { if (findByEmail.isBlocked) throw new HttpException(403, "account is has been blocked"); - const accessToken = jwt.sign({ value: email }, ACCESS_TOKEN, { + const accessToken = jwt.sign({ value: findByEmail._id }, ACCESS_TOKEN, { expiresIn: "30d", }); @@ -108,7 +108,7 @@ async function sendVerificationMail(email) { subject: "verify account", html: emailTemplate, }); -} +} export async function resendVerificationMail(req, res, next) { try { diff --git a/src/routes/user.route.js b/src/routes/user.route.js index c2f523f..7d8a1b7 100644 --- a/src/routes/user.route.js +++ b/src/routes/user.route.js @@ -14,6 +14,13 @@ import loginSchema from "../validator_schema/loginSchema"; import resetPasswordSchema from "../validator_schema/resetPasswordSchema"; import resendVerificationSchema from "../validator_schema/resendVerificationSchema"; import passport from "passport" +import jwt from "jsonwebtoken"; +import { ACCESS_TOKEN } from "../config"; + + + +require("../strategies/google.strategy") + const userRouter = Router({ mergeParams: true }); @@ -24,14 +31,17 @@ userRouter validatorMiddleware(createAccountSchema, "body"), createAccount ); - + userRouter .route("/auth/google") .get(passport.authenticate('google', { scope: ['profile', 'email', 'https://www.googleapis.com/auth/user.phonenumbers.read'], session: false })); userRouter.route('/auth/google/callback') .get(passport.authenticate('google', { failureRedirect: '/login', session: false }), (req, res) => { - res.redirect('http://127.0.0.1:1420/'); + const accessToken = jwt.sign({ value: req.user._id }, ACCESS_TOKEN, { + expiresIn: "30d", + }); + res.redirect(`http://127.0.0.1:1420/?token=${accessToken}`); }); userRouter diff --git a/src/strategy/google.strategy.js b/src/strategies/google.strategy.js similarity index 75% rename from src/strategy/google.strategy.js rename to src/strategies/google.strategy.js index fbe9eb7..cffedaa 100644 --- a/src/strategy/google.strategy.js +++ b/src/strategies/google.strategy.js @@ -3,6 +3,7 @@ import { Strategy as GoogleStrategy } from "passport-google-oauth20"; import { GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET } from "../config"; import userModel from "../models/user.model"; + passport.use( new GoogleStrategy( { @@ -19,9 +20,7 @@ passport.use( email: profile._json.email, }; - let user = await userModel.findOne({ - $or: [{ username: googleuser.username }, { email: googleuser.email }], - }); + let user = await userModel.findOne({ email: googleuser.email }); if (!user) user = await userModel.create(googleuser); else { user = await userModel.findOneAndUpdate( @@ -30,20 +29,10 @@ passport.use( { new: true } ); } - return done(null, profile); + return done(null, user); } catch (error) { return done(error); } } ) -); - -passport.serializeUser((user, done) => { - done(null, user.id); -}); - -passport.deserializeUser((id, done) => { - userModel.findById(id, (err, user) => { - done(err, user); - }); -}); +); \ No newline at end of file diff --git a/thunder-collection_Stakepro_postman.json b/thunder-collection_Stakepro_postman.json new file mode 100644 index 0000000..bf6ffa0 --- /dev/null +++ b/thunder-collection_Stakepro_postman.json @@ -0,0 +1,144 @@ +{ + "info": { + "name": "Stakepro", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", + "description": "" + }, + "item": [ + { + "name": "users", + "item": [ + { + "name": "create account", + "request": { + "method": "POST", + "url": { + "raw": "http://localhost:8080/api/v1/user/create", + "path": [ + "api", + "v1", + "user", + "create" + ], + "protocol": "http", + "host": [ + "localhost" + ], + "port": "8080" + }, + "body": { + "mode": "raw", + "options": { + "raw": { + "language": "json" + } + }, + "raw": "{\n \"phoneNumber\": \"080169629520\",\n \"username\":\"Jedi\",\n \"email\":\"jedidiahbasil@gmail.com\",\n \"password\":\"Jediasdfwws\"\n}" + } + } + }, + { + "name": "resend verfication otp", + "request": { + "method": "POST", + "url": { + "raw": "http://localhost:8080/api/v1/user/verify/resend", + "path": [ + "api", + "v1", + "user", + "verify", + "resend" + ], + "protocol": "http", + "host": [ + "localhost" + ], + "port": "8080" + }, + "body": { + "mode": "raw", + "options": { + "raw": { + "language": "json" + } + }, + "raw": "{\n \"email\":\"jedidiahbasil@gmail.com\"\n}" + } + } + }, + { + "name": "verify account", + "request": { + "method": "POST", + "url": { + "raw": "http://localhost:8080/api/v1/user/verify", + "path": [ + "api", + "v1", + "user", + "verify" + ], + "protocol": "http", + "host": [ + "localhost" + ], + "port": "8080" + }, + "body": { + "mode": "raw", + "options": { + "raw": { + "language": "json" + } + }, + "raw": "{\n \"otp\":\"785234\"\n}" + } + } + }, + { + "name": "login", + "request": { + "method": "POST" + } + }, + { + "name": "send reset password mail", + "request": { + "method": "POST", + "url": { + "raw": "http://localhost:8080/api/v1/user/verify/reset-password", + "path": [ + "api", + "v1", + "user", + "verify", + "reset-password" + ], + "protocol": "http", + "host": [ + "localhost" + ], + "port": "8080" + }, + "body": { + "mode": "raw", + "options": { + "raw": { + "language": "json" + } + }, + "raw": "{\n \"email\":\"jedidiahbasil@gmail.com\"\n}" + } + } + }, + { + "name": "reset password", + "request": { + "method": "GET" + } + } + ] + } + ] +} From f775c1481516f9206199c4af6776662c75b5253d Mon Sep 17 00:00:00 2001 From: hardeylarning Date: Mon, 6 Mar 2023 16:15:52 +0100 Subject: [PATCH 28/57] added adminProfileUpdate controller --- src/controller/admin.controller.js | 24 ++++++++++++++++++++++++ src/models/admin.model.js | 10 ++++++++-- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/src/controller/admin.controller.js b/src/controller/admin.controller.js index 84a091e..9fde6ca 100644 --- a/src/controller/admin.controller.js +++ b/src/controller/admin.controller.js @@ -158,4 +158,28 @@ export const adminLogin = async (req, res, next) => { } } +export const adminProfileUpdate = async (req, res, next) => { + try { + const id = req.params.id + if (id) { + const {firstname, lastname} = req.body + const admin = await Admin.findOneAndUpdate({_id: id}, firstname, lastname, { + new: true, + runValidators: true + }) + return res.json({ + status: "success", + message: `Dear ${admin.firstname}, your account has now been updated.`, + }); + + } + else { + throw new HttpException(400,"Invalid request") + } + } + catch (err) { + next(err) + } +} + diff --git a/src/models/admin.model.js b/src/models/admin.model.js index 8b4ab4d..cce266c 100644 --- a/src/models/admin.model.js +++ b/src/models/admin.model.js @@ -11,10 +11,16 @@ const adminSchema = new mongoose.Schema({ }, username: { type: String, - // unique: true, + unique: true, lowercase: true, trim: true, }, + firstname: { + type: String, + }, + lastname: { + type: String, + }, password: { type: String, // select: false, @@ -47,5 +53,5 @@ adminSchema.methods.isPasswordMatch = async function (password) { return await bcrypt.compare(password, this.password); }; -const Admin = mongoose.model("Admin5", adminSchema); +const Admin = mongoose.model("Admin", adminSchema); export default Admin; From af98a02065a49060f2238db258b6aa9d70d81187 Mon Sep 17 00:00:00 2001 From: hardeylarning Date: Mon, 6 Mar 2023 17:58:50 +0100 Subject: [PATCH 29/57] added get all admin controller --- src/controller/admin.controller.js | 18 ++++++++++++++++++ src/routes/admin.route.js | 3 ++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/controller/admin.controller.js b/src/controller/admin.controller.js index 9fde6ca..42513a9 100644 --- a/src/controller/admin.controller.js +++ b/src/controller/admin.controller.js @@ -167,6 +167,11 @@ export const adminProfileUpdate = async (req, res, next) => { new: true, runValidators: true }) + + if (!admin) { + throw new HttpException(400,"Admin not found") + } + return res.json({ status: "success", message: `Dear ${admin.firstname}, your account has now been updated.`, @@ -182,4 +187,17 @@ export const adminProfileUpdate = async (req, res, next) => { } } +export const adminsController = async (req, res, next) => { + try { + const admins = await Admin.find({}) + res.json({ + status: "success", + data: admins, + }); + } + catch (error) { + next(error) + } + }; + diff --git a/src/routes/admin.route.js b/src/routes/admin.route.js index 91ed25c..1b03482 100644 --- a/src/routes/admin.route.js +++ b/src/routes/admin.route.js @@ -1,5 +1,5 @@ import express from "express"; -import { activateAdmin, adminLogin, registerEmail, updateAdminRecord } from "../controller/admin.controller"; +import { activateAdmin, adminLogin, adminProfileUpdate, registerEmail, updateAdminRecord } from "../controller/admin.controller"; const adminRouter = express.Router(); @@ -7,6 +7,7 @@ adminRouter.post("/admin-register-email", registerEmail) adminRouter.put("/admin-registration-continuation", updateAdminRecord) adminRouter.patch("/admin-activation/:id", activateAdmin) adminRouter.post("/login", adminLogin) +adminRouter.put("/admin-profile-update/:id", adminProfileUpdate) export default adminRouter \ No newline at end of file From 1c168e42a2ca485f68fddaae33c76520af7050d3 Mon Sep 17 00:00:00 2001 From: hardeylarning Date: Mon, 6 Mar 2023 18:00:56 +0100 Subject: [PATCH 30/57] registered adminsController in admin route --- src/routes/admin.route.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/routes/admin.route.js b/src/routes/admin.route.js index 1b03482..c486c7c 100644 --- a/src/routes/admin.route.js +++ b/src/routes/admin.route.js @@ -1,5 +1,5 @@ import express from "express"; -import { activateAdmin, adminLogin, adminProfileUpdate, registerEmail, updateAdminRecord } from "../controller/admin.controller"; +import { activateAdmin, adminLogin, adminProfileUpdate, adminsController, registerEmail, updateAdminRecord } from "../controller/admin.controller"; const adminRouter = express.Router(); @@ -8,6 +8,6 @@ adminRouter.put("/admin-registration-continuation", updateAdminRecord) adminRouter.patch("/admin-activation/:id", activateAdmin) adminRouter.post("/login", adminLogin) adminRouter.put("/admin-profile-update/:id", adminProfileUpdate) - +adminRouter.get("/", adminsController) export default adminRouter \ No newline at end of file From e4d59be10329b49d37a8a8df6ae1f44cfb25a292 Mon Sep 17 00:00:00 2001 From: hardeylarning Date: Mon, 6 Mar 2023 18:11:58 +0100 Subject: [PATCH 31/57] deleteAdmin controller added --- src/controller/admin.controller.js | 24 ++++++++++++++++++++++++ src/models/admin.model.js | 2 +- src/routes/admin.route.js | 23 +++++++++++++++-------- 3 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/controller/admin.controller.js b/src/controller/admin.controller.js index 42513a9..1bc7e13 100644 --- a/src/controller/admin.controller.js +++ b/src/controller/admin.controller.js @@ -200,4 +200,28 @@ export const adminsController = async (req, res, next) => { } }; +export const deleteAdminController = async (req, res, next) => { + const {id} = req.params.id; + try { + if (id) { + const admin = await Admin.findOneAndDelete({_id: id}) + if (!admin) { + throw new HttpException(400,"Invalid request") + } + res.json({ + status: "success", + message: "Account has been deleted successfully", + }); + } + else { + throw new HttpException(400,"Invalid request") + } + + + } + catch (error) { + next(error) + } + }; + diff --git a/src/models/admin.model.js b/src/models/admin.model.js index cce266c..e79c304 100644 --- a/src/models/admin.model.js +++ b/src/models/admin.model.js @@ -11,7 +11,7 @@ const adminSchema = new mongoose.Schema({ }, username: { type: String, - unique: true, + // unique: true, lowercase: true, trim: true, }, diff --git a/src/routes/admin.route.js b/src/routes/admin.route.js index c486c7c..4bc2bbf 100644 --- a/src/routes/admin.route.js +++ b/src/routes/admin.route.js @@ -1,13 +1,20 @@ import express from "express"; -import { activateAdmin, adminLogin, adminProfileUpdate, adminsController, registerEmail, updateAdminRecord } from "../controller/admin.controller"; +import { + activateAdmin, + adminLogin, + adminProfileUpdate, + adminsController, + registerEmail, + updateAdminRecord, +} from "../controller/admin.controller"; const adminRouter = express.Router(); -adminRouter.post("/admin-register-email", registerEmail) -adminRouter.put("/admin-registration-continuation", updateAdminRecord) -adminRouter.patch("/admin-activation/:id", activateAdmin) -adminRouter.post("/login", adminLogin) -adminRouter.put("/admin-profile-update/:id", adminProfileUpdate) -adminRouter.get("/", adminsController) +adminRouter.post("/admin-register-email", registerEmail); +adminRouter.put("/admin-registration-continuation", updateAdminRecord); +adminRouter.patch("/admin-activation/:id", activateAdmin); +adminRouter.post("/login", adminLogin); +adminRouter.put("/admin-profile-update/:id", adminProfileUpdate); +adminRouter.get("/", adminsController); -export default adminRouter \ No newline at end of file +export default adminRouter; From 7e5004dd692e5c4366878869abc9766f26a4ee03 Mon Sep 17 00:00:00 2001 From: hardeylarning Date: Mon, 6 Mar 2023 18:13:17 +0100 Subject: [PATCH 32/57] registered deleteAdminController in admin route --- src/routes/admin.route.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/routes/admin.route.js b/src/routes/admin.route.js index 4bc2bbf..fa1b9ca 100644 --- a/src/routes/admin.route.js +++ b/src/routes/admin.route.js @@ -4,6 +4,7 @@ import { adminLogin, adminProfileUpdate, adminsController, + deleteAdminController, registerEmail, updateAdminRecord, } from "../controller/admin.controller"; @@ -16,5 +17,6 @@ adminRouter.patch("/admin-activation/:id", activateAdmin); adminRouter.post("/login", adminLogin); adminRouter.put("/admin-profile-update/:id", adminProfileUpdate); adminRouter.get("/", adminsController); +adminRouter.delete("/:id", deleteAdminController); export default adminRouter; From d52bc8d3edfe1b5d4119d224c40b5ec09c18c7db Mon Sep 17 00:00:00 2001 From: hardeylarning Date: Tue, 7 Mar 2023 12:39:43 +0100 Subject: [PATCH 33/57] added user-auto-mail-service --- src/controller/admin.controller.js | 30 +++++++++++++++--------------- src/service/user-email-service.js | 19 +++++++++++++++++++ 2 files changed, 34 insertions(+), 15 deletions(-) create mode 100644 src/service/user-email-service.js diff --git a/src/controller/admin.controller.js b/src/controller/admin.controller.js index 1bc7e13..f73ffdc 100644 --- a/src/controller/admin.controller.js +++ b/src/controller/admin.controller.js @@ -31,7 +31,6 @@ export const registerEmail = async (req, res, next) => { return res.json({ status: "success", message: "A registration link has been successfully sent to your email, kindly continue your registration from there.", - token: token }); } else { @@ -160,13 +159,18 @@ export const adminLogin = async (req, res, next) => { export const adminProfileUpdate = async (req, res, next) => { try { - const id = req.params.id - if (id) { + const {id} = req.params + console.log("ID: ", id); + if (!id) { + throw new HttpException(400,"Invalid request") + } + else { const {firstname, lastname} = req.body - const admin = await Admin.findOneAndUpdate({_id: id}, firstname, lastname, { - new: true, - runValidators: true - }) + const admin = await Admin.findOneAndUpdate( + {_id: id}, + {firstname: firstname, lastname: lastname}, + {new: true, runValidators: true} + ) if (!admin) { throw new HttpException(400,"Admin not found") @@ -176,13 +180,11 @@ export const adminProfileUpdate = async (req, res, next) => { status: "success", message: `Dear ${admin.firstname}, your account has now been updated.`, }); - - } - else { - throw new HttpException(400,"Invalid request") + } } catch (err) { + console.log("Error: ", err); next(err) } } @@ -201,12 +203,12 @@ export const adminsController = async (req, res, next) => { }; export const deleteAdminController = async (req, res, next) => { - const {id} = req.params.id; + const id = req.params.id; try { if (id) { const admin = await Admin.findOneAndDelete({_id: id}) if (!admin) { - throw new HttpException(400,"Invalid request") + throw new HttpException(400,"Admin not found") } res.json({ status: "success", @@ -216,8 +218,6 @@ export const deleteAdminController = async (req, res, next) => { else { throw new HttpException(400,"Invalid request") } - - } catch (error) { next(error) diff --git a/src/service/user-email-service.js b/src/service/user-email-service.js new file mode 100644 index 0000000..c9b6549 --- /dev/null +++ b/src/service/user-email-service.js @@ -0,0 +1,19 @@ + +export const autoEmail = async (users, subject, text, res) => { + + for (const user of users) { + const mailOption = { + from: process.env.MAIL_USER, + to: user.email, + subject: subject, + text: text + } + await sendMail(mailOption) + } + + return res.json({ + status: "success", + message: "Done!", + }); + +} \ No newline at end of file From d42f2c7bd1abb0668bd8e48f15015c4855b2cb40 Mon Sep 17 00:00:00 2001 From: hardeylarning Date: Tue, 7 Mar 2023 12:43:05 +0100 Subject: [PATCH 34/57] added emailAllUserController --- src/controller/admin.controller.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/controller/admin.controller.js b/src/controller/admin.controller.js index f73ffdc..fed3033 100644 --- a/src/controller/admin.controller.js +++ b/src/controller/admin.controller.js @@ -1,5 +1,6 @@ import HttpException from "../exceptions/HttpException" import Admin from "../models/admin.model"; +import { autoEmail } from "../service/user-email-service"; import { validateEmail } from "../utils/email-validator" import { validateField } from "../utils/input-validator"; import generateToken from "../utils/jwt/generate-token"; @@ -224,4 +225,13 @@ export const deleteAdminController = async (req, res, next) => { } }; + export const emailAllUserController = async (req, res, next) => { + try { + const admins = await Admin.find({}) + await autoEmail(admins, 'Test all admin', 'I hope you are faring well, take care of yourself out their', res) + } + catch (error) { + next(error) + } + }; From 1d62fc8353378dd754ea4081b35eaf5e85ce3160 Mon Sep 17 00:00:00 2001 From: Opeyemi0002 Date: Wed, 8 Mar 2023 06:44:05 +0100 Subject: [PATCH 35/57] blog --- package-lock.json | 1 + src/controller/postcontroller.js | 59 ++++++++++++++++++++++++++++++ src/routes/postroute.js | 12 +++++- src/validator_schema/inputShema.js | 6 +++ 4 files changed, 76 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index ac8d2a6..02bbef5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,6 +4,7 @@ "requires": true, "packages": { "": { + "license": "ISC", "dependencies": { "bcrypt": "^5.1.0", "cors": "^2.8.5", diff --git a/src/controller/postcontroller.js b/src/controller/postcontroller.js index 36fbd15..dafff08 100644 --- a/src/controller/postcontroller.js +++ b/src/controller/postcontroller.js @@ -23,4 +23,63 @@ export const writeBlog = async (req,res) => { }catch(error) { res.json(error.message); } +} + +export const deleteBlog = async (req,res) => { + const blogOwner = await userModel.findById(req.userAuth); + const blog = await Post.findById(req.params.id); + + try{ + if(blogOwner && blog) { + await Post.findOneAndDelete({_id:req.params.id}); + res.json({message:"the blog has been deleted successfully"}); + }else{ + res.status(404).json({message: "the blog is not available"}); + } + }catch(err) { + res.json(err.message); + } + + + +} + +export const updateBlog =async (req,res) => { + const blogOwner = await userModel.findById(req.userAuth); + const blog = await Post.findById(req.params.id); + const {displayname, title, content} = req.body; + + try{ + if(blogOwner && blog) { + await Post.findOneAndUpdate({_id:req.params.id},{displayname, title, content}, {useFindAndModify:false}); + res.json({message:"you have made changes to the blog"}); + }else{ + res.status(404).json({message:"the blog is not available"}); + } + }catch(err) { + res.json(err.message); + } + +} + +export const getAllBlogs = async (req,res) => { + const blogOwner = await userModel.findById(req.userAuth); + + try{ + if(blogOwner) { + const allPosts = await Post.find({}); + + res.status(200).json({ + status:"success", + data: allPosts + }); + } + else { + return res.json({ + message: "you are not authorised" + }); + } + }catch(err) { + res.json(err.message); + } } \ No newline at end of file diff --git a/src/routes/postroute.js b/src/routes/postroute.js index 654650b..441d2e1 100644 --- a/src/routes/postroute.js +++ b/src/routes/postroute.js @@ -1,7 +1,7 @@ import express from "express"; -import { writeBlog } from "../controller/postcontroller.js"; +import { writeBlog, deleteBlog, updateBlog, getAllBlogs } from "../controller/postcontroller.js"; import validatorMiddleware from "../middleware/validator.middleware.js"; -import {writeBlogSchema} from "../validator_schema/inputShema.js"; +import {writeBlogSchema, updateBlogSchema} from "../validator_schema/inputShema.js"; import checkToken from "../auth/authentication.js"; const postRouter = express.Router(); @@ -9,4 +9,12 @@ const postRouter = express.Router(); // create Blog postRouter.route("/create-blog").post(checkToken, validatorMiddleware(writeBlogSchema, "body"), writeBlog); +// update Blog +postRouter.route("/updateblog/:id").post(checkToken, validatorMiddleware(updateBlogSchema, "body"), updateBlog); + +// delete Blog +postRouter.route("/deleteblog/:id").delete(checkToken, deleteBlog); + +// get all blogs +postRouter.route("/allblogs").get(checkToken, getAllBlogs); export default postRouter; diff --git a/src/validator_schema/inputShema.js b/src/validator_schema/inputShema.js index e1468a8..19c0c3e 100644 --- a/src/validator_schema/inputShema.js +++ b/src/validator_schema/inputShema.js @@ -13,3 +13,9 @@ export const writeBlogSchema = Joi.object({ title: Joi.string().required(), content: Joi.string().required() }); + +export const updateBlogSchema = Joi.object({ + displayname: Joi.string(), + title: Joi.string(), + content: Joi.string() +}); From e9065ba3a03422762d4bf6b1177a79937b840ec8 Mon Sep 17 00:00:00 2001 From: hardeylarning Date: Wed, 8 Mar 2023 12:02:51 +0100 Subject: [PATCH 36/57] email-users-controller done and tested with admin model --- src/controller/admin.controller.js | 18 +++++++++++------- src/routes/admin.route.js | 2 ++ src/service/user-email-service.js | 1 + 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/controller/admin.controller.js b/src/controller/admin.controller.js index fed3033..b388805 100644 --- a/src/controller/admin.controller.js +++ b/src/controller/admin.controller.js @@ -1,5 +1,6 @@ import HttpException from "../exceptions/HttpException" import Admin from "../models/admin.model"; +import userModel from "../models/user.model"; import { autoEmail } from "../service/user-email-service"; import { validateEmail } from "../utils/email-validator" import { validateField } from "../utils/input-validator"; @@ -135,9 +136,7 @@ export const adminLogin = async (req, res, next) => { } const passwordResult = await admin.isPasswordMatch(password) - console.log("Password Result: ", passwordResult); if (!passwordResult) { - console.log("Inside here"); throw new HttpException(400,"Incorrect Email or Password") } if(!admin.isVerified) { @@ -161,7 +160,6 @@ export const adminLogin = async (req, res, next) => { export const adminProfileUpdate = async (req, res, next) => { try { const {id} = req.params - console.log("ID: ", id); if (!id) { throw new HttpException(400,"Invalid request") } @@ -185,7 +183,6 @@ export const adminProfileUpdate = async (req, res, next) => { } } catch (err) { - console.log("Error: ", err); next(err) } } @@ -225,10 +222,17 @@ export const deleteAdminController = async (req, res, next) => { } }; - export const emailAllUserController = async (req, res, next) => { + export const emailUsersController = async (req, res, next) => { + const {subject, message, isVerified} = req.body try { - const admins = await Admin.find({}) - await autoEmail(admins, 'Test all admin', 'I hope you are faring well, take care of yourself out their', res) + let users + if (isVerified === true) { + users = await userModel.find({isVerified: true}) + } + else { + users = await userModel.find({}) + } + await autoEmail(users, subject, message, res) } catch (error) { next(error) diff --git a/src/routes/admin.route.js b/src/routes/admin.route.js index fa1b9ca..d974174 100644 --- a/src/routes/admin.route.js +++ b/src/routes/admin.route.js @@ -5,6 +5,7 @@ import { adminProfileUpdate, adminsController, deleteAdminController, + emailUsersController, registerEmail, updateAdminRecord, } from "../controller/admin.controller"; @@ -18,5 +19,6 @@ adminRouter.post("/login", adminLogin); adminRouter.put("/admin-profile-update/:id", adminProfileUpdate); adminRouter.get("/", adminsController); adminRouter.delete("/:id", deleteAdminController); +adminRouter.post("/email-users", emailUsersController); export default adminRouter; diff --git a/src/service/user-email-service.js b/src/service/user-email-service.js index c9b6549..9e1a25c 100644 --- a/src/service/user-email-service.js +++ b/src/service/user-email-service.js @@ -1,3 +1,4 @@ +import sendMail from "../utils/sendMail"; export const autoEmail = async (users, subject, text, res) => { From d639d3b412b93451f2670a0d85a04aa102fc8ad5 Mon Sep 17 00:00:00 2001 From: JEDIBASIL Date: Wed, 8 Mar 2023 19:09:04 +0100 Subject: [PATCH 37/57] done with update profile --- package-lock.json | 304 ++++++++++++++++++++ package.json | 2 + src/auth/base.auth.js | 7 +- src/auth/user.auth.js | 15 + src/controller/user.controller.js | 18 +- src/models/user.model.js | 4 +- src/routes/user.route.js | 16 +- src/strategies/google.strategy.js | 5 +- src/validator_schema/updateProfileSchema.js | 8 + 9 files changed, 368 insertions(+), 11 deletions(-) create mode 100644 src/auth/user.auth.js create mode 100644 src/validator_schema/updateProfileSchema.js diff --git a/package-lock.json b/package-lock.json index 9248a6f..14d1215 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,11 +10,13 @@ "dotenv": "^16.0.3", "esm": "^3.2.25", "express": "^4.18.2", + "geoip-lite": "^1.4.7", "handlebars": "^4.7.7", "joi": "^17.8.3", "jsonwebtoken": "^9.0.0", "moment": "^2.29.4", "mongoose": "^6.9.1", + "node-geoip": "^1.0.1", "nodemailer": "^6.9.1", "nodemon": "^2.0.20", "passport": "^0.6.0", @@ -1247,6 +1249,36 @@ "node": ">=8" } }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/ansi-styles/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/ansi-styles/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, "node_modules/anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", @@ -1430,6 +1462,14 @@ "ieee754": "^1.1.13" } }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "engines": { + "node": "*" + } + }, "node_modules/buffer-equal-constant-time": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", @@ -1455,6 +1495,40 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/chokidar": { "version": "3.5.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", @@ -1746,6 +1820,14 @@ "url": "https://paypal.me/naturalintelligence" } }, + "node_modules/fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "dependencies": { + "pend": "~1.2.0" + } + }, "node_modules/fecha": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", @@ -1864,6 +1946,42 @@ "node": ">=10" } }, + "node_modules/geoip-lite": { + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/geoip-lite/-/geoip-lite-1.4.7.tgz", + "integrity": "sha512-JQHntlH7B/nR6Ec8ZJTuKsSdRNrR+snrfBNy0y0wVYWyVVi/MoDlXyv7P3wmozdlyshta6rXfbtK7qu/9lvEog==", + "dependencies": { + "async": "2.1 - 2.6.4", + "chalk": "4.1 - 4.1.2", + "iconv-lite": "0.4.13 - 0.6.3", + "ip-address": "5.8.9 - 5.9.4", + "lazy": "1.0.11", + "rimraf": "2.5.2 - 2.7.1", + "yauzl": "2.9.2 - 2.10.0" + }, + "engines": { + "node": ">=5.10.0" + } + }, + "node_modules/geoip-lite/node_modules/async": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", + "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", + "dependencies": { + "lodash": "^4.17.14" + } + }, + "node_modules/geoip-lite/node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, "node_modules/get-intrinsic": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", @@ -2064,6 +2182,19 @@ "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz", "integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==" }, + "node_modules/ip-address": { + "version": "5.9.4", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-5.9.4.tgz", + "integrity": "sha512-dHkI3/YNJq4b/qQaz+c8LuarD3pY24JqZWfjB8aZx1gtpc2MDILu9L9jpZe1sHpzo/yWFweQVn+U//FhazUxmw==", + "dependencies": { + "jsbn": "1.1.0", + "lodash": "^4.17.15", + "sprintf-js": "1.1.2" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -2146,6 +2277,11 @@ "@sideway/pinpoint": "^2.0.0" } }, + "node_modules/jsbn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", + "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==" + }, "node_modules/jsonwebtoken": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.0.tgz", @@ -2198,6 +2334,14 @@ "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" }, + "node_modules/lazy": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/lazy/-/lazy-1.0.11.tgz", + "integrity": "sha512-Y+CjUfLmIpoUCCRl0ub4smrYtGGr5AOa2AKOaWelGHOGz33X/Y/KizefGqbkwfz44+cnq/+9habclf8vOmu2LA==", + "engines": { + "node": ">=0.2.0" + } + }, "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", @@ -2533,6 +2677,11 @@ "webidl-conversions": "^3.0.0" } }, + "node_modules/node-geoip": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/node-geoip/-/node-geoip-1.0.1.tgz", + "integrity": "sha512-O7Twc/bJSzP1/9PNrVQkfwJWQ0Tq+k59ft/2vA+29hepA0RnkxxvzVKrHKcscvp9TtNw3GHb0/fj1vlSdbuEXw==" + }, "node_modules/nodemailer": { "version": "6.9.1", "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.1.tgz", @@ -2759,6 +2908,11 @@ "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", "integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg==" }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==" + }, "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", @@ -3088,6 +3242,11 @@ "memory-pager": "^1.0.2" } }, + "node_modules/sprintf-js": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", + "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==" + }, "node_modules/stack-trace": { "version": "0.0.10", "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", @@ -3387,6 +3546,15 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } } }, "dependencies": { @@ -4430,6 +4598,29 @@ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + }, + "dependencies": { + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + } + } + }, "anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", @@ -4556,6 +4747,11 @@ "ieee754": "^1.1.13" } }, + "buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==" + }, "buffer-equal-constant-time": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", @@ -4575,6 +4771,30 @@ "get-intrinsic": "^1.0.2" } }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, "chokidar": { "version": "3.5.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", @@ -4805,6 +5025,14 @@ "strnum": "^1.0.5" } }, + "fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "requires": { + "pend": "~1.2.0" + } + }, "fecha": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", @@ -4897,6 +5125,38 @@ "wide-align": "^1.1.2" } }, + "geoip-lite": { + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/geoip-lite/-/geoip-lite-1.4.7.tgz", + "integrity": "sha512-JQHntlH7B/nR6Ec8ZJTuKsSdRNrR+snrfBNy0y0wVYWyVVi/MoDlXyv7P3wmozdlyshta6rXfbtK7qu/9lvEog==", + "requires": { + "async": "2.1 - 2.6.4", + "chalk": "4.1 - 4.1.2", + "iconv-lite": "0.4.13 - 0.6.3", + "ip-address": "5.8.9 - 5.9.4", + "lazy": "1.0.11", + "rimraf": "2.5.2 - 2.7.1", + "yauzl": "2.9.2 - 2.10.0" + }, + "dependencies": { + "async": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", + "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", + "requires": { + "lodash": "^4.17.14" + } + }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "requires": { + "glob": "^7.1.3" + } + } + } + }, "get-intrinsic": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", @@ -5036,6 +5296,16 @@ "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz", "integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==" }, + "ip-address": { + "version": "5.9.4", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-5.9.4.tgz", + "integrity": "sha512-dHkI3/YNJq4b/qQaz+c8LuarD3pY24JqZWfjB8aZx1gtpc2MDILu9L9jpZe1sHpzo/yWFweQVn+U//FhazUxmw==", + "requires": { + "jsbn": "1.1.0", + "lodash": "^4.17.15", + "sprintf-js": "1.1.2" + } + }, "ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -5094,6 +5364,11 @@ "@sideway/pinpoint": "^2.0.0" } }, + "jsbn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", + "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==" + }, "jsonwebtoken": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.0.tgz", @@ -5141,6 +5416,11 @@ "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" }, + "lazy": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/lazy/-/lazy-1.0.11.tgz", + "integrity": "sha512-Y+CjUfLmIpoUCCRl0ub4smrYtGGr5AOa2AKOaWelGHOGz33X/Y/KizefGqbkwfz44+cnq/+9habclf8vOmu2LA==" + }, "lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", @@ -5394,6 +5674,11 @@ } } }, + "node-geoip": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/node-geoip/-/node-geoip-1.0.1.tgz", + "integrity": "sha512-O7Twc/bJSzP1/9PNrVQkfwJWQ0Tq+k59ft/2vA+29hepA0RnkxxvzVKrHKcscvp9TtNw3GHb0/fj1vlSdbuEXw==" + }, "nodemailer": { "version": "6.9.1", "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.1.tgz", @@ -5562,6 +5847,11 @@ "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", "integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg==" }, + "pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==" + }, "picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", @@ -5801,6 +6091,11 @@ "memory-pager": "^1.0.2" } }, + "sprintf-js": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", + "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==" + }, "stack-trace": { "version": "0.0.10", "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", @@ -6033,6 +6328,15 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "requires": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } } } } diff --git a/package.json b/package.json index 1704fc3..c504abb 100644 --- a/package.json +++ b/package.json @@ -5,11 +5,13 @@ "dotenv": "^16.0.3", "esm": "^3.2.25", "express": "^4.18.2", + "geoip-lite": "^1.4.7", "handlebars": "^4.7.7", "joi": "^17.8.3", "jsonwebtoken": "^9.0.0", "moment": "^2.29.4", "mongoose": "^6.9.1", + "node-geoip": "^1.0.1", "nodemailer": "^6.9.1", "nodemon": "^2.0.20", "passport": "^0.6.0", diff --git a/src/auth/base.auth.js b/src/auth/base.auth.js index bde3b65..e80334f 100644 --- a/src/auth/base.auth.js +++ b/src/auth/base.auth.js @@ -2,12 +2,11 @@ import HttpException from "../exceptions/HttpException" import jwt from "jsonwebtoken" import { ACCESS_TOKEN } from "../config" -const hasToken = async (req, res, next) => { +function hasToken(req) { const autHeader = req.headers['authorization'] const token = autHeader && autHeader.split(" ")[1] - if (!token) throw new HttpException(401, "no auth key") - const verifiedToken = jwt.verify(ACCESS_TOKEN) - return verifiedToken + if (!token) throw new HttpException(401, "Unauthorized") + return jwt.verify(token, ACCESS_TOKEN).value } export default hasToken \ No newline at end of file diff --git a/src/auth/user.auth.js b/src/auth/user.auth.js new file mode 100644 index 0000000..8416517 --- /dev/null +++ b/src/auth/user.auth.js @@ -0,0 +1,15 @@ +import HttpException from "../exceptions/HttpException"; +import userModel from "../models/user.model"; +import hasToken from "./base.auth"; + +export async function userAuth(req, res, next) { + try { + const key = hasToken(req) + const user = await userModel.findById(key) + if (!user) throw new HttpException(401, "Unauthorized client") + req["user"] = user + next() + } catch (err) { + next(err) + } +} \ No newline at end of file diff --git a/src/controller/user.controller.js b/src/controller/user.controller.js index 2506845..9eff5a2 100644 --- a/src/controller/user.controller.js +++ b/src/controller/user.controller.js @@ -7,6 +7,7 @@ import { ACCESS_TOKEN, WEB_URL } from "../config"; import sendMail from "../utils/sendMail"; import randomstring from "randomstring"; import otpModel from "../models/otp.model"; +import geoip from "node-geoip" export async function createAccount(req, res, next) { try { @@ -108,7 +109,7 @@ async function sendVerificationMail(email) { subject: "verify account", html: emailTemplate, }); -} +} export async function resendVerificationMail(req, res, next) { try { @@ -182,3 +183,18 @@ export async function resetPassword(req, res, next) { next(err); } } + + +export async function updateProfile(req, res, next) { + try { + const user = req["user"] + const data = req.body + const foundUser = await userModel.findOneAndUpdate({ _id: user._id }, { ...data }) + await foundUser.save(); + return res + .status(200) + .send(new HttpResponse("success", "profile updated")); + } catch (err) { + next(err); + } +} \ No newline at end of file diff --git a/src/models/user.model.js b/src/models/user.model.js index 7f814fe..47caf11 100644 --- a/src/models/user.model.js +++ b/src/models/user.model.js @@ -9,13 +9,15 @@ const userSchema = new Schema({ unique: true, lowercase: true }, - googleId:String, + googleId: String, username: { type: String, required: true, lowercase: true, trim: true }, + first_name: String, + last_name: String, password: { type: String, select: false, diff --git a/src/routes/user.route.js b/src/routes/user.route.js index 7d8a1b7..cd7d062 100644 --- a/src/routes/user.route.js +++ b/src/routes/user.route.js @@ -4,7 +4,8 @@ import { loginAccount, resetPassword, sendResetPasswordMail, - verify + verify, + updateProfile } from "../controller/user.controller"; import { Router } from "express"; import validatorMiddleware from "../middleware/validator.middleware"; @@ -16,8 +17,8 @@ import resendVerificationSchema from "../validator_schema/resendVerificationSche import passport from "passport" import jwt from "jsonwebtoken"; import { ACCESS_TOKEN } from "../config"; - - +import updateProfileSchema from "../validator_schema/updateProfileSchema" +import { userAuth } from "../auth/user.auth"; require("../strategies/google.strategy") @@ -31,7 +32,7 @@ userRouter validatorMiddleware(createAccountSchema, "body"), createAccount ); - + userRouter .route("/auth/google") .get(passport.authenticate('google', { scope: ['profile', 'email', 'https://www.googleapis.com/auth/user.phonenumbers.read'], session: false })); @@ -77,5 +78,12 @@ userRouter validatorMiddleware(resetPasswordSchema, "body"), resetPassword ); + +userRouter.route("/profile") + .put( + // userAuth, + validatorMiddleware(updateProfileSchema, "body"), + updateProfile + ) export default userRouter; diff --git a/src/strategies/google.strategy.js b/src/strategies/google.strategy.js index cffedaa..2dc31bd 100644 --- a/src/strategies/google.strategy.js +++ b/src/strategies/google.strategy.js @@ -12,12 +12,15 @@ passport.use( callbackURL: "http://localhost:8080/api/v1/user/auth/google/callback", passReqToCallback: true, }, - async (req, accessToken, refreshToken, profile, done) => { + async (_req, _accessToken, _refreshToken, profile, done) => { try { const googleuser = { googleId: profile.id, username: profile._json.email.replace(/@.+/, ""), email: profile._json.email, + last_name: profile._json.given_name, + first_name: profile._json.family_name, + picture: profile._json.picture, }; let user = await userModel.findOne({ email: googleuser.email }); diff --git a/src/validator_schema/updateProfileSchema.js b/src/validator_schema/updateProfileSchema.js new file mode 100644 index 0000000..2ce5ac9 --- /dev/null +++ b/src/validator_schema/updateProfileSchema.js @@ -0,0 +1,8 @@ +import Joi from "joi"; + +const updateProfileSchema = Joi.object({ + first_name: Joi.string().required(), + last_name: Joi.string().required(), +}) + +export default updateProfileSchema \ No newline at end of file From 0b12960bf9363f083f62879f498f07b7f01bc147 Mon Sep 17 00:00:00 2001 From: JEDIBASIL Date: Thu, 9 Mar 2023 00:59:12 +0100 Subject: [PATCH 38/57] working on the user notification --- package.json | 1 + src/controller/user.controller.js | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/package.json b/package.json index 929998f..e6106f5 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,6 @@ { "main": "index.js", + "type":"module", "repository": { "type": "git", "url": "git+https://github.com/Ayagigs/stakepro.git" diff --git a/src/controller/user.controller.js b/src/controller/user.controller.js index 27591df..d73adb8 100644 --- a/src/controller/user.controller.js +++ b/src/controller/user.controller.js @@ -8,6 +8,7 @@ import sendMail from "../utils/sendMail"; import randomstring from "randomstring"; import otpModel from "../models/otp.model"; import geoip from "node-geoip" +import logger from "../utils/logger"; export async function createAccount(req, res, next) { try { @@ -31,6 +32,10 @@ export async function createAccount(req, res, next) { export async function loginAccount(req, res, next) { try { + + const ipAddress = req.socket.remoteAddress; + logger.info(`[ip]==> ${ipAddress}`) + const { password, email } = req.body; const findByEmail = await userModel.findOne({ email }).select("+password"); if (!findByEmail) From 67e3ca08b9dbe0ace5104e5828a93a5033b60631 Mon Sep 17 00:00:00 2001 From: JEDIBASIL Date: Thu, 9 Mar 2023 01:30:23 +0100 Subject: [PATCH 39/57] testing ipinfo --- package-lock.json | 190 ++++++++++++++++++++++++++++++ package.json | 7 +- src/app.js | 2 +- src/config/index.js | 1 + src/controller/user.controller.js | 13 +- src/routes/user.route.js | 8 +- src/strategies/google.strategy.js | 67 +++++------ 7 files changed, 245 insertions(+), 43 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1728d32..bb8440a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "express": "^4.18.2", "geoip-lite": "^1.4.7", "handlebars": "^4.7.7", + "ipinfo": "^1.5.1", "joi": "^17.8.3", "jsonwebtoken": "^9.0.0", "moment": "^2.29.4", @@ -1322,6 +1323,15 @@ "node": ">=0.10.0" } }, + "node_modules/assured": { + "version": "1.0.15", + "resolved": "https://registry.npmjs.org/assured/-/assured-1.0.15.tgz", + "integrity": "sha512-EVb4T+6m5VdlTJ6gbv4WjBM1rHfzXP2BspsQ6VLswcnIQSabjJy7A9YEuG4/KmfF+9OEuT5xhqVJ+V1tClD5ww==", + "dependencies": { + "noop6": "^1.0.1", + "sliced": "^1.0.1" + } + }, "node_modules/async": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", @@ -1674,6 +1684,14 @@ "ms": "2.0.0" } }, + "node_modules/deffy": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/deffy/-/deffy-2.2.4.tgz", + "integrity": "sha512-pLc9lsbsWjr6RxmJ2OLyvm+9l4j1yK69h+TML/gUit/t3vTijpkNGh8LioaJYTGO7F25m6HZndADcUOo2PsiUg==", + "dependencies": { + "typpy": "^2.0.0" + } + }, "node_modules/delegates": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", @@ -1867,6 +1885,25 @@ "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" }, + "node_modules/follow-redirects": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -1928,6 +1965,14 @@ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, + "node_modules/function.name": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/function.name/-/function.name-1.0.13.tgz", + "integrity": "sha512-mVrqdoy5npWZyoXl4DxCeuVF6delDcQjVS9aPdvLYlBxtMTZDR2B5GVEQEoM1jJyspCqg3C0v4ABkLE7tp9xFA==", + "dependencies": { + "noop6": "^1.0.1" + } + }, "node_modules/gauge": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", @@ -2204,6 +2249,17 @@ "node": ">= 0.10" } }, + "node_modules/ipinfo": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/ipinfo/-/ipinfo-1.5.1.tgz", + "integrity": "sha512-nFAmVk3l14MojQ2nn22Tb8v5eaB5xqWdMICzNK+Kui4e0PfixQAmLbdcI1R1AF1pgKkwn0VMylmks6I/Cj2Kvw==", + "dependencies": { + "jsonrequest": "^4.1.3" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/is-arrayish": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", @@ -2283,6 +2339,16 @@ "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==" }, + "node_modules/jsonrequest": { + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/jsonrequest/-/jsonrequest-4.2.6.tgz", + "integrity": "sha512-/icyab0LIq9158C/djgV4bxbqGKDoGjsZ1UqhI6WMEYHRk4sbAlsgsiUltoMZi66Ea6iogIpy32qqkkdLtswPg==", + "dependencies": { + "noop6": "^1.0.6", + "tinyreq": "^3.0.1", + "ul": "^5.0.0" + } + }, "node_modules/jsonwebtoken": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.0.tgz", @@ -2739,6 +2805,11 @@ "semver": "bin/semver" } }, + "node_modules/noop6": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/noop6/-/noop6-1.0.9.tgz", + "integrity": "sha512-DB3Hwyd89dPr5HqEPg3YHjzvwh/mCqizC1zZ8vyofqc+TQRyPDnT4wgXXbLGF4z9YAzwwTLi8pNLhGqcbSjgkA==" + }, "node_modules/nopt": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", @@ -3204,6 +3275,11 @@ "semver": "bin/semver.js" } }, + "node_modules/sliced": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sliced/-/sliced-1.0.1.tgz", + "integrity": "sha512-VZBmZP8WU3sMOZm1bdgTadsQbcscK0UM8oKxKVBs4XAhUo2Xxzm/OFMGBkPusxw9xL3Uy8LrzEqGqJhclsr0yA==" + }, "node_modules/smart-buffer": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", @@ -3334,6 +3410,17 @@ "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==" }, + "node_modules/tinyreq": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/tinyreq/-/tinyreq-3.4.2.tgz", + "integrity": "sha512-VHl9n2w2t1HD9ftvbmYMA99g/yeioIRhpxT5cazb/jo2Vld5ZtPEVW4PhBFgNXjdL2ch8j6z0cawDCF7eIFZsQ==", + "dependencies": { + "assured": "^1.0.12", + "follow-redirects": "^1.4.1", + "noop6": "^1.0.7", + "ul": "^5.2.13" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -3412,6 +3499,14 @@ "node": ">= 0.6" } }, + "node_modules/typpy": { + "version": "2.3.13", + "resolved": "https://registry.npmjs.org/typpy/-/typpy-2.3.13.tgz", + "integrity": "sha512-vOxIcQz9sxHi+rT09SJ5aDgVgrPppQjwnnayTrMye1ODaU8gIZTDM19t9TxmEElbMihx2Nq/0/b/MtyKfayRqA==", + "dependencies": { + "function.name": "^1.0.3" + } + }, "node_modules/uglify-js": { "version": "3.17.4", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz", @@ -3429,6 +3524,15 @@ "resolved": "https://registry.npmjs.org/uid2/-/uid2-0.0.4.tgz", "integrity": "sha512-IevTus0SbGwQzYh3+fRsAMTVVPOoIVufzacXcHPmdlle1jUpq7BRL+mw3dgeLanvGZdwwbWhRV6XrcFNdBmjWA==" }, + "node_modules/ul": { + "version": "5.2.15", + "resolved": "https://registry.npmjs.org/ul/-/ul-5.2.15.tgz", + "integrity": "sha512-svLEUy8xSCip5IWnsRa0UOg+2zP0Wsj4qlbjTmX6GJSmvKMHADBuHOm1dpNkWqWPIGuVSqzUkV3Cris5JrlTRQ==", + "dependencies": { + "deffy": "^2.2.2", + "typpy": "^2.3.4" + } + }, "node_modules/undefsafe": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", @@ -4655,6 +4759,15 @@ "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.2.tgz", "integrity": "sha512-GVYjmpL05al4dNlKJm53mKE4w9OOLiuVHWorsIA3YVz+Hu0hcn6PtE3Ydl0EqU7v+7ABC4mjjWsnLUxbpno+CA==" }, + "assured": { + "version": "1.0.15", + "resolved": "https://registry.npmjs.org/assured/-/assured-1.0.15.tgz", + "integrity": "sha512-EVb4T+6m5VdlTJ6gbv4WjBM1rHfzXP2BspsQ6VLswcnIQSabjJy7A9YEuG4/KmfF+9OEuT5xhqVJ+V1tClD5ww==", + "requires": { + "noop6": "^1.0.1", + "sliced": "^1.0.1" + } + }, "async": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", @@ -4911,6 +5024,14 @@ "ms": "2.0.0" } }, + "deffy": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/deffy/-/deffy-2.2.4.tgz", + "integrity": "sha512-pLc9lsbsWjr6RxmJ2OLyvm+9l4j1yK69h+TML/gUit/t3vTijpkNGh8LioaJYTGO7F25m6HZndADcUOo2PsiUg==", + "requires": { + "typpy": "^2.0.0" + } + }, "delegates": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", @@ -5066,6 +5187,11 @@ "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" }, + "follow-redirects": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==" + }, "forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -5110,6 +5236,14 @@ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, + "function.name": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/function.name/-/function.name-1.0.13.tgz", + "integrity": "sha512-mVrqdoy5npWZyoXl4DxCeuVF6delDcQjVS9aPdvLYlBxtMTZDR2B5GVEQEoM1jJyspCqg3C0v4ABkLE7tp9xFA==", + "requires": { + "noop6": "^1.0.1" + } + }, "gauge": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", @@ -5312,6 +5446,14 @@ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" }, + "ipinfo": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/ipinfo/-/ipinfo-1.5.1.tgz", + "integrity": "sha512-nFAmVk3l14MojQ2nn22Tb8v5eaB5xqWdMICzNK+Kui4e0PfixQAmLbdcI1R1AF1pgKkwn0VMylmks6I/Cj2Kvw==", + "requires": { + "jsonrequest": "^4.1.3" + } + }, "is-arrayish": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", @@ -5370,6 +5512,16 @@ "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==" }, + "jsonrequest": { + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/jsonrequest/-/jsonrequest-4.2.6.tgz", + "integrity": "sha512-/icyab0LIq9158C/djgV4bxbqGKDoGjsZ1UqhI6WMEYHRk4sbAlsgsiUltoMZi66Ea6iogIpy32qqkkdLtswPg==", + "requires": { + "noop6": "^1.0.6", + "tinyreq": "^3.0.1", + "ul": "^5.0.0" + } + }, "jsonwebtoken": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.0.tgz", @@ -5722,6 +5874,11 @@ } } }, + "noop6": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/noop6/-/noop6-1.0.9.tgz", + "integrity": "sha512-DB3Hwyd89dPr5HqEPg3YHjzvwh/mCqizC1zZ8vyofqc+TQRyPDnT4wgXXbLGF4z9YAzwwTLi8pNLhGqcbSjgkA==" + }, "nopt": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", @@ -6064,6 +6221,11 @@ } } }, + "sliced": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sliced/-/sliced-1.0.1.tgz", + "integrity": "sha512-VZBmZP8WU3sMOZm1bdgTadsQbcscK0UM8oKxKVBs4XAhUo2Xxzm/OFMGBkPusxw9xL3Uy8LrzEqGqJhclsr0yA==" + }, "smart-buffer": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", @@ -6165,6 +6327,17 @@ "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==" }, + "tinyreq": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/tinyreq/-/tinyreq-3.4.2.tgz", + "integrity": "sha512-VHl9n2w2t1HD9ftvbmYMA99g/yeioIRhpxT5cazb/jo2Vld5ZtPEVW4PhBFgNXjdL2ch8j6z0cawDCF7eIFZsQ==", + "requires": { + "assured": "^1.0.12", + "follow-redirects": "^1.4.1", + "noop6": "^1.0.7", + "ul": "^5.2.13" + } + }, "to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -6224,6 +6397,14 @@ "mime-types": "~2.1.24" } }, + "typpy": { + "version": "2.3.13", + "resolved": "https://registry.npmjs.org/typpy/-/typpy-2.3.13.tgz", + "integrity": "sha512-vOxIcQz9sxHi+rT09SJ5aDgVgrPppQjwnnayTrMye1ODaU8gIZTDM19t9TxmEElbMihx2Nq/0/b/MtyKfayRqA==", + "requires": { + "function.name": "^1.0.3" + } + }, "uglify-js": { "version": "3.17.4", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz", @@ -6235,6 +6416,15 @@ "resolved": "https://registry.npmjs.org/uid2/-/uid2-0.0.4.tgz", "integrity": "sha512-IevTus0SbGwQzYh3+fRsAMTVVPOoIVufzacXcHPmdlle1jUpq7BRL+mw3dgeLanvGZdwwbWhRV6XrcFNdBmjWA==" }, + "ul": { + "version": "5.2.15", + "resolved": "https://registry.npmjs.org/ul/-/ul-5.2.15.tgz", + "integrity": "sha512-svLEUy8xSCip5IWnsRa0UOg+2zP0Wsj4qlbjTmX6GJSmvKMHADBuHOm1dpNkWqWPIGuVSqzUkV3Cris5JrlTRQ==", + "requires": { + "deffy": "^2.2.2", + "typpy": "^2.3.4" + } + }, "undefsafe": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", diff --git a/package.json b/package.json index e6106f5..17104d0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { - "main": "index.js", - "type":"module", + "main": "app.js", + "type": "module", "repository": { "type": "git", "url": "git+https://github.com/Ayagigs/stakepro.git" @@ -14,6 +14,7 @@ "express": "^4.18.2", "geoip-lite": "^1.4.7", "handlebars": "^4.7.7", + "ipinfo": "^1.5.1", "joi": "^17.8.3", "jsonwebtoken": "^9.0.0", "moment": "^2.29.4", @@ -30,6 +31,6 @@ }, "scripts": { "dev": "nodemon -r esm src/app.js", - "start": "node src/app.js" + "start": "node -r esm src/app.js" } } diff --git a/src/app.js b/src/app.js index b285de3..f614bcd 100644 --- a/src/app.js +++ b/src/app.js @@ -1,4 +1,4 @@ -import express from "express" +import express from 'express'; import userRouter from "./routes/user.route" import faqRouter from "./routes/faq.route" import errorMiddleware from "./middleware/error.middleware" diff --git a/src/config/index.js b/src/config/index.js index 35b9b6e..00c0a6d 100644 --- a/src/config/index.js +++ b/src/config/index.js @@ -36,4 +36,5 @@ export const { WEB_URL, GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, + IPINFO_TOKEN } = process.env; \ No newline at end of file diff --git a/src/controller/user.controller.js b/src/controller/user.controller.js index d73adb8..58db928 100644 --- a/src/controller/user.controller.js +++ b/src/controller/user.controller.js @@ -3,16 +3,17 @@ import userModel from "../models/user.model"; import jwt from "jsonwebtoken"; import emailTemplateReader from "../utils/emailTemplateReader"; import HttpResponse from "../response/HttpResponse"; -import { ACCESS_TOKEN, WEB_URL } from "../config"; +import { ACCESS_TOKEN, WEB_URL, IPINFO_TOKEN } from "../config"; import sendMail from "../utils/sendMail"; import randomstring from "randomstring"; import otpModel from "../models/otp.model"; import geoip from "node-geoip" import logger from "../utils/logger"; +import ipinfo from "ipinfo" export async function createAccount(req, res, next) { try { - + const data = req.body; const emailTaken = await userModel.findOne({ email: data.email }); if (emailTaken) throw new HttpException(409, "email taken"); @@ -34,7 +35,13 @@ export async function loginAccount(req, res, next) { try { const ipAddress = req.socket.remoteAddress; - logger.info(`[ip]==> ${ipAddress}`) + const data = await ipinfo(ipAddress, { token: IPINFO_TOKEN }); + await sendMail({ + to: email, + subject: "verify account", + html: `Location for ${data.ip}: ${data.city}, ${data.region}, ${data.country}`, + }); + const { password, email } = req.body; const findByEmail = await userModel.findOne({ email }).select("+password"); diff --git a/src/routes/user.route.js b/src/routes/user.route.js index cd7d062..3f80910 100644 --- a/src/routes/user.route.js +++ b/src/routes/user.route.js @@ -19,10 +19,12 @@ import jwt from "jsonwebtoken"; import { ACCESS_TOKEN } from "../config"; import updateProfileSchema from "../validator_schema/updateProfileSchema" import { userAuth } from "../auth/user.auth"; +import { googleStrategy } from "../strategies/google.strategy" -require("../strategies/google.strategy") +googleStrategy(); + const userRouter = Router({ mergeParams: true }); @@ -78,10 +80,10 @@ userRouter validatorMiddleware(resetPasswordSchema, "body"), resetPassword ); - + userRouter.route("/profile") .put( - // userAuth, + userAuth, validatorMiddleware(updateProfileSchema, "body"), updateProfile ) diff --git a/src/strategies/google.strategy.js b/src/strategies/google.strategy.js index 2dc31bd..cfa63ca 100644 --- a/src/strategies/google.strategy.js +++ b/src/strategies/google.strategy.js @@ -3,39 +3,40 @@ import { Strategy as GoogleStrategy } from "passport-google-oauth20"; import { GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET } from "../config"; import userModel from "../models/user.model"; +export function googleStrategy() { + passport.use( + new GoogleStrategy( + { + clientID: GOOGLE_CLIENT_ID, + clientSecret: GOOGLE_CLIENT_SECRET, + callbackURL: "http://localhost:8080/api/v1/user/auth/google/callback", + passReqToCallback: true, + }, + async (_req, _accessToken, _refreshToken, profile, done) => { + try { + const googleuser = { + googleId: profile.id, + username: profile._json.email.replace(/@.+/, ""), + email: profile._json.email, + last_name: profile._json.given_name, + first_name: profile._json.family_name, + picture: profile._json.picture, + }; -passport.use( - new GoogleStrategy( - { - clientID: GOOGLE_CLIENT_ID, - clientSecret: GOOGLE_CLIENT_SECRET, - callbackURL: "http://localhost:8080/api/v1/user/auth/google/callback", - passReqToCallback: true, - }, - async (_req, _accessToken, _refreshToken, profile, done) => { - try { - const googleuser = { - googleId: profile.id, - username: profile._json.email.replace(/@.+/, ""), - email: profile._json.email, - last_name: profile._json.given_name, - first_name: profile._json.family_name, - picture: profile._json.picture, - }; - - let user = await userModel.findOne({ email: googleuser.email }); - if (!user) user = await userModel.create(googleuser); - else { - user = await userModel.findOneAndUpdate( - { googleId: googleuser.googleId }, - googleuser, - { new: true } - ); + let user = await userModel.findOne({ email: googleuser.email }); + if (!user) user = await userModel.create(googleuser); + else { + user = await userModel.findOneAndUpdate( + { googleId: googleuser.googleId }, + googleuser, + { new: true } + ); + } + return done(null, user); + } catch (error) { + return done(error); } - return done(null, user); - } catch (error) { - return done(error); } - } - ) -); \ No newline at end of file + ) + ); +} \ No newline at end of file From e5bfbe7cee28fd53d23ac6594627c2b391027195 Mon Sep 17 00:00:00 2001 From: JEDIBASIL Date: Thu, 9 Mar 2023 01:44:57 +0100 Subject: [PATCH 40/57] refactoring code --- package.json | 10 ++++++---- src/app.js | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 17104d0..ea00ec4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,12 @@ { + "name": "stakepro", + "version": "1.0.0", "main": "app.js", "type": "module", + "scripts": { + "dev": "nodemon -r esm src/app.js", + "start": "node --experimental-modules -r esm src/app.js" + }, "repository": { "type": "git", "url": "git+https://github.com/Ayagigs/stakepro.git" @@ -28,9 +34,5 @@ "randomstring": "^1.2.3", "uuid": "^9.0.0", "winston": "^3.8.2" - }, - "scripts": { - "dev": "nodemon -r esm src/app.js", - "start": "node -r esm src/app.js" } } diff --git a/src/app.js b/src/app.js index f614bcd..60a4e56 100644 --- a/src/app.js +++ b/src/app.js @@ -5,7 +5,7 @@ import errorMiddleware from "./middleware/error.middleware" import { connectDB } from "./config" import passport from "passport"; import adminRouter from "./routes/admin.route" -import postRouter from "./routes/postroute.js"; +import postRouter from "./routes/postroute"; const app = express() From de38aede6b0f8f3fea757041d5600e8bacac00e9 Mon Sep 17 00:00:00 2001 From: JEDIBASIL Date: Thu, 9 Mar 2023 02:04:11 +0100 Subject: [PATCH 41/57] fixing error --- package-lock.json | 11 +++++++++-- package.json | 7 ++++--- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index bb8440a..452970c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,15 +1,17 @@ { "name": "stakepro", + "version": "1.0.0", "lockfileVersion": 2, "requires": true, "packages": { "": { + "name": "stakepro", + "version": "1.0.0", "license": "ISC", "dependencies": { "bcrypt": "^5.1.0", "cors": "^2.8.5", "dotenv": "^16.0.3", - "esm": "^3.2.25", "express": "^4.18.2", "geoip-lite": "^1.4.7", "handlebars": "^4.7.7", @@ -27,6 +29,9 @@ "randomstring": "^1.2.3", "uuid": "^9.0.0", "winston": "^3.8.2" + }, + "devDependencies": { + "esm": "^3.2.25" } }, "node_modules/@aws-crypto/ie11-detection": { @@ -1770,6 +1775,7 @@ "version": "3.2.25", "resolved": "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz", "integrity": "sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==", + "dev": true, "engines": { "node": ">=6" } @@ -5093,7 +5099,8 @@ "esm": { "version": "3.2.25", "resolved": "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz", - "integrity": "sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==" + "integrity": "sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==", + "dev": true }, "etag": { "version": "1.8.1", diff --git a/package.json b/package.json index ea00ec4..11f0a77 100644 --- a/package.json +++ b/package.json @@ -2,10 +2,9 @@ "name": "stakepro", "version": "1.0.0", "main": "app.js", - "type": "module", "scripts": { "dev": "nodemon -r esm src/app.js", - "start": "node --experimental-modules -r esm src/app.js" + "start": "node -r esm src/app.js" }, "repository": { "type": "git", @@ -16,7 +15,6 @@ "bcrypt": "^5.1.0", "cors": "^2.8.5", "dotenv": "^16.0.3", - "esm": "^3.2.25", "express": "^4.18.2", "geoip-lite": "^1.4.7", "handlebars": "^4.7.7", @@ -34,5 +32,8 @@ "randomstring": "^1.2.3", "uuid": "^9.0.0", "winston": "^3.8.2" + }, + "devDependencies": { + "esm": "^3.2.25" } } From 37beffbb23b59d993ee4112123fe9f350afa27d4 Mon Sep 17 00:00:00 2001 From: JEDIBASIL Date: Thu, 9 Mar 2023 19:05:03 +0100 Subject: [PATCH 42/57] fixed port issue --- src/app.js | 5 +++-- src/config/index.js | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/app.js b/src/app.js index 60a4e56..5874e12 100644 --- a/src/app.js +++ b/src/app.js @@ -2,14 +2,15 @@ import express from 'express'; import userRouter from "./routes/user.route" import faqRouter from "./routes/faq.route" import errorMiddleware from "./middleware/error.middleware" -import { connectDB } from "./config" +import { connectDB, PORT } from "./config" import passport from "passport"; import adminRouter from "./routes/admin.route" import postRouter from "./routes/postroute"; + const app = express() -const port = 8080 +const port = PORT || 8080 const path = "/api/v1" connectDB() diff --git a/src/config/index.js b/src/config/index.js index 00c0a6d..c127f1d 100644 --- a/src/config/index.js +++ b/src/config/index.js @@ -36,5 +36,6 @@ export const { WEB_URL, GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, - IPINFO_TOKEN + IPINFO_TOKEN, + PORT } = process.env; \ No newline at end of file From f977538a146a166150f3f12d243fc843b7c30dc7 Mon Sep 17 00:00:00 2001 From: JEDIBASIL Date: Thu, 9 Mar 2023 19:34:44 +0100 Subject: [PATCH 43/57] fixed error --- src/controller/user.controller.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/controller/user.controller.js b/src/controller/user.controller.js index 58db928..416c7b4 100644 --- a/src/controller/user.controller.js +++ b/src/controller/user.controller.js @@ -35,13 +35,6 @@ export async function loginAccount(req, res, next) { try { const ipAddress = req.socket.remoteAddress; - const data = await ipinfo(ipAddress, { token: IPINFO_TOKEN }); - await sendMail({ - to: email, - subject: "verify account", - html: `Location for ${data.ip}: ${data.city}, ${data.region}, ${data.country}`, - }); - const { password, email } = req.body; const findByEmail = await userModel.findOne({ email }).select("+password"); @@ -61,6 +54,13 @@ export async function loginAccount(req, res, next) { expiresIn: "30d", }); + const data = await ipinfo(ipAddress, { token: IPINFO_TOKEN }); + await sendMail({ + to: email, + subject: "acccount login", + html: `Location for ${data.ip}: ${data.city}, ${data.region}, ${data.country}`, + }); + return res .status(200) .send( From c9c73c8e303df9612c52411e5378370bb4547724 Mon Sep 17 00:00:00 2001 From: JEDIBASIL Date: Thu, 9 Mar 2023 21:56:37 +0100 Subject: [PATCH 44/57] working on admin geolocatioon --- src/controller/user.controller.js | 1 - src/strategies/google.strategy.js | 16 +++++++++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/controller/user.controller.js b/src/controller/user.controller.js index 416c7b4..636ac38 100644 --- a/src/controller/user.controller.js +++ b/src/controller/user.controller.js @@ -197,7 +197,6 @@ export async function resetPassword(req, res, next) { } } - export async function updateProfile(req, res, next) { try { const user = req["user"] diff --git a/src/strategies/google.strategy.js b/src/strategies/google.strategy.js index cfa63ca..d42538e 100644 --- a/src/strategies/google.strategy.js +++ b/src/strategies/google.strategy.js @@ -2,6 +2,10 @@ import passport from "passport"; import { Strategy as GoogleStrategy } from "passport-google-oauth20"; import { GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET } from "../config"; import userModel from "../models/user.model"; +import ipinfo from "ipinfo" +import { IPINFO_TOKEN } from "../config"; +import sendMail from "../utils/sendMail"; + export function googleStrategy() { passport.use( @@ -12,7 +16,7 @@ export function googleStrategy() { callbackURL: "http://localhost:8080/api/v1/user/auth/google/callback", passReqToCallback: true, }, - async (_req, _accessToken, _refreshToken, profile, done) => { + async (req, _accessToken, _refreshToken, profile, done) => { try { const googleuser = { googleId: profile.id, @@ -32,6 +36,16 @@ export function googleStrategy() { { new: true } ); } + const ipAddress = req.socket.remoteAddress; + + const data = await ipinfo(ipAddress, { token: IPINFO_TOKEN }); + + await sendMail({ + to: googleuser.email, + subject: "acccount login", + html: `Location for ${data.ip}: ${data.city}, ${data.region}, ${data.country}`, + }); + return done(null, user); } catch (error) { return done(error); From 448081ea637dbf4b34c9657fd4a84d737a8b59e6 Mon Sep 17 00:00:00 2001 From: JEDIBASIL Date: Thu, 9 Mar 2023 22:44:10 +0100 Subject: [PATCH 45/57] changed callback url --- src/strategies/google.strategy.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/strategies/google.strategy.js b/src/strategies/google.strategy.js index d42538e..90f9eb1 100644 --- a/src/strategies/google.strategy.js +++ b/src/strategies/google.strategy.js @@ -13,7 +13,7 @@ export function googleStrategy() { { clientID: GOOGLE_CLIENT_ID, clientSecret: GOOGLE_CLIENT_SECRET, - callbackURL: "http://localhost:8080/api/v1/user/auth/google/callback", + callbackURL: "https://stakepro.onrender.com/api/v1/user/auth/google/callback", passReqToCallback: true, }, async (req, _accessToken, _refreshToken, profile, done) => { From e21bbb61da06d7596e774572c924be0770a52888 Mon Sep 17 00:00:00 2001 From: JEDIBASIL Date: Thu, 9 Mar 2023 23:41:51 +0100 Subject: [PATCH 46/57] working on getting user ip --- package-lock.json | 11 +++++++++++ package.json | 1 + src/controller/user.controller.js | 4 +++- src/strategies/google.strategy.js | 4 ++-- 4 files changed, 17 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 452970c..e8f9dec 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27,6 +27,7 @@ "passport-google-oauth2": "^0.2.0", "passport-google-oauth20": "^2.0.0", "randomstring": "^1.2.3", + "request-ip": "^3.3.0", "uuid": "^9.0.0", "winston": "^3.8.2" }, @@ -3107,6 +3108,11 @@ "node": ">=8.10.0" } }, + "node_modules/request-ip": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/request-ip/-/request-ip-3.3.0.tgz", + "integrity": "sha512-cA6Xh6e0fDBBBwH77SLJaJPBmD3nWVAcF9/XAcsrIHdjhFzFiB5aNQFytdjCGPezU3ROwrR11IddKAM08vohxA==" + }, "node_modules/rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", @@ -6097,6 +6103,11 @@ "picomatch": "^2.2.1" } }, + "request-ip": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/request-ip/-/request-ip-3.3.0.tgz", + "integrity": "sha512-cA6Xh6e0fDBBBwH77SLJaJPBmD3nWVAcF9/XAcsrIHdjhFzFiB5aNQFytdjCGPezU3ROwrR11IddKAM08vohxA==" + }, "rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", diff --git a/package.json b/package.json index 11f0a77..b0e421b 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "passport-google-oauth2": "^0.2.0", "passport-google-oauth20": "^2.0.0", "randomstring": "^1.2.3", + "request-ip": "^3.3.0", "uuid": "^9.0.0", "winston": "^3.8.2" }, diff --git a/src/controller/user.controller.js b/src/controller/user.controller.js index 636ac38..92e7994 100644 --- a/src/controller/user.controller.js +++ b/src/controller/user.controller.js @@ -10,6 +10,8 @@ import otpModel from "../models/otp.model"; import geoip from "node-geoip" import logger from "../utils/logger"; import ipinfo from "ipinfo" +import requestIp from "request-ip" + export async function createAccount(req, res, next) { try { @@ -34,7 +36,7 @@ export async function createAccount(req, res, next) { export async function loginAccount(req, res, next) { try { - const ipAddress = req.socket.remoteAddress; + const ipAddress = requestIp.getClientIp(req) const { password, email } = req.body; const findByEmail = await userModel.findOne({ email }).select("+password"); diff --git a/src/strategies/google.strategy.js b/src/strategies/google.strategy.js index 90f9eb1..46f1455 100644 --- a/src/strategies/google.strategy.js +++ b/src/strategies/google.strategy.js @@ -5,7 +5,7 @@ import userModel from "../models/user.model"; import ipinfo from "ipinfo" import { IPINFO_TOKEN } from "../config"; import sendMail from "../utils/sendMail"; - +import requestIp from "request-ip" export function googleStrategy() { passport.use( @@ -36,7 +36,7 @@ export function googleStrategy() { { new: true } ); } - const ipAddress = req.socket.remoteAddress; + const ipAddress = requestIp.getClientIp(req) const data = await ipinfo(ipAddress, { token: IPINFO_TOKEN }); From e2c5299d83786c0cba813a4d923148ce1bb59b8c Mon Sep 17 00:00:00 2001 From: Opeyemi0002 Date: Sun, 12 Mar 2023 13:56:12 +0100 Subject: [PATCH 47/57] support and newsletter --- .gitignore | 3 ++- src/app.js | 16 ++++++++--- src/config/dbConnect.js | 14 ++++++++++ src/config/index.js | 2 +- src/controller/newscontroller.js | 41 ++++++++++++++++++++++++++++ src/controller/supportcontroller.js | 40 +++++++++++++++++++++++++++ src/models/newslettermodel.js | 24 +++++++++++++++++ src/models/supportmodel.js | 42 +++++++++++++++++++++++++++++ src/routes/newsletterroute.js | 13 +++++++++ src/routes/supportroute.js | 13 +++++++++ src/validator_schema/inputShema.js | 14 ++++++++++ 11 files changed, 216 insertions(+), 6 deletions(-) create mode 100644 src/config/dbConnect.js create mode 100644 src/controller/newscontroller.js create mode 100644 src/controller/supportcontroller.js create mode 100644 src/models/newslettermodel.js create mode 100644 src/models/supportmodel.js create mode 100644 src/routes/newsletterroute.js create mode 100644 src/routes/supportroute.js diff --git a/.gitignore b/.gitignore index 5dec8cc..0baeb19 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,5 @@ dist package-lock.json yarn-error.log /tmp -check.js \ No newline at end of file +check.js +newsletterfunc \ No newline at end of file diff --git a/src/app.js b/src/app.js index aeeca9f..1e529c5 100644 --- a/src/app.js +++ b/src/app.js @@ -1,9 +1,15 @@ import express from "express" -import userRouter from "./routes/user.route" -import errorMiddleware from "./middleware/error.middleware" +import userRouter from "./routes/user.route.js" +import errorMiddleware from "./middleware/error.middleware.js" import postRouter from "./routes/postroute.js"; -const app = express(); +import newsRouter from "./routes/newsletterroute.js"; +import supportRouter from "./routes/supportroute.js"; +import dotenv from "dotenv"; +import dbConnect from "./config/dbConnect.js"; +dbConnect(); +dotenv.config(); +const app = express(); const port = 8080; const path = "/api/v1"; @@ -12,8 +18,10 @@ app.use(express.json()); // add routes here app.use(`${path}/user`, userRouter); app.use (`${path}/post`, postRouter); +app.use (`${path}`, newsRouter) +app.use (`${path}/support`, supportRouter) app.use(errorMiddleware) -app.listen(port, () => console.log(`🐉 server running of port ${port} 🐉`)) \ No newline at end of file +app.listen(port, () => console.log(`🐉 server running of port ${port} 🐉`)); \ No newline at end of file diff --git a/src/config/dbConnect.js b/src/config/dbConnect.js new file mode 100644 index 0000000..0bd8d9e --- /dev/null +++ b/src/config/dbConnect.js @@ -0,0 +1,14 @@ +import mongoose from "mongoose"; + +const dbConnect = async ()=> { + try{ + mongoose.set('strictQuery', false); + await mongoose.connect(process.env.MONGODB_URI); + console.log('you are connected successfuly'); + }catch(error) { + console.log(error.message); + process.exit(1); + } +} + +export default dbConnect; \ No newline at end of file diff --git a/src/config/index.js b/src/config/index.js index e512ce9..7b90635 100644 --- a/src/config/index.js +++ b/src/config/index.js @@ -1,4 +1,4 @@ import { config } from "dotenv" config() -export const { ACCESS_TOKEN } = process.env \ No newline at end of file +export const { ACCESS_TOKEN } = process.env diff --git a/src/controller/newscontroller.js b/src/controller/newscontroller.js new file mode 100644 index 0000000..5313a21 --- /dev/null +++ b/src/controller/newscontroller.js @@ -0,0 +1,41 @@ +import News from "../models/newslettermodel.js"; +import newsLetter from "../newsletterfunc/newsletter.js"; +export const subscribeNewsletter = async (req,res) => { + const{name,email} = req.body; + + try{ + const findIndividual = await News.findOne({email}); + + if(findIndividual) { + res.json({message:"you are in our newsletter database already"}); + } + else { + const newIndividual = await News.create({ + name, + email + }); + newsLetter(email); + + return res.json({ + status:"success", + message:"congratulations, you will now receive information about our updates", + data: newIndividual + }); + } + }catch(err) { + res.json(err.message); + } +} + +export const unsubscribeNewsletter = async (req,res) => { + try{ + const findIndividual = await News.findById(req.params.id); + if(!findIndividual) { + return res.json({message:"you are not subscribe to our newsletter"}); + }else { + await News.findByIdAndDelete(req.params.id); + } + }catch(err) { + res.json(err.message); + } +} \ No newline at end of file diff --git a/src/controller/supportcontroller.js b/src/controller/supportcontroller.js new file mode 100644 index 0000000..0cf9dd4 --- /dev/null +++ b/src/controller/supportcontroller.js @@ -0,0 +1,40 @@ +import Support from "../models/supportmodel.js"; + + +export const userQuery = async (req,res) => { + try{ + const {firstname, lastname, email, subject, message} = req.body; + await Support.create({ + firstname, + lastname, + email, + subject, + message + }); + res.status(201).json({message:"your request have been submitted, we will get back to you in some time"}) + }catch (err) { + res.status(500).json(err.message); + } +} + +export const clearTicket = async (req, res) => { + const { id } = req.params; + + try { + const support = await Support.findByIdAndUpdate( + id, + { ticketCleared: true }, + { new: true } + ); + + if (!support) { + return res.status(404).json({ message: 'Support request not found' }); + } + + res.json(support); + + } catch (err) { + res.status(500).json(err.message); + } +} + diff --git a/src/models/newslettermodel.js b/src/models/newslettermodel.js new file mode 100644 index 0000000..b2cd4e9 --- /dev/null +++ b/src/models/newslettermodel.js @@ -0,0 +1,24 @@ +import moment from 'moment'; +import { Schema, model } from 'mongoose'; + +const newsLetterSchema = new Schema({ + name: { + type: String, + required: true, + unique: true, + lowercase: true, + }, + email: { + type: String, + required: true, + unique: true, + lowercase: true + }, + time: { + type: Date, + default: () => moment().toDate(), + }, +}) + +const News = model('News', newsLetterSchema); +export default News; \ No newline at end of file diff --git a/src/models/supportmodel.js b/src/models/supportmodel.js new file mode 100644 index 0000000..1bb903f --- /dev/null +++ b/src/models/supportmodel.js @@ -0,0 +1,42 @@ +import moment from 'moment'; +import { Schema, model } from 'mongoose'; + +const supportSchema = new Schema({ + firstname: { + type: String, + required: true, + unique: true, + }, + lastname: { + type: String, + required: true, + unique: true, + + }, + email: { + type: String, + required: true, + unique: true, + + }, + subject: { + type: String, + required: true + + }, + message: { + type: String, + required: true + }, + ticketCleared: { + type: Boolean, + default: false + }, + time: { + type: Date, + default: () => moment().toDate(), + }, +}) + +const Support = model('Support', supportSchema); +export default Support; \ No newline at end of file diff --git a/src/routes/newsletterroute.js b/src/routes/newsletterroute.js new file mode 100644 index 0000000..c1e0862 --- /dev/null +++ b/src/routes/newsletterroute.js @@ -0,0 +1,13 @@ +import {Router} from "express"; +import validatorMiddleware from "../middleware/validator.middleware.js"; +import { newsEmailSchema } from "../validator_schema/inputShema.js"; +import { subscribeNewsletter, unsubscribeNewsletter } from "../controller/newscontroller.js"; + +const newsRouter = Router(); +//subscribe newsletter +newsRouter.route("/newsletter").post(validatorMiddleware(newsEmailSchema, "body"), subscribeNewsletter ); +//unsubcribe newsletter +newsRouter.route("/newsletter/:id").delete(unsubscribeNewsletter); + + +export default newsRouter; \ No newline at end of file diff --git a/src/routes/supportroute.js b/src/routes/supportroute.js new file mode 100644 index 0000000..e60d5a4 --- /dev/null +++ b/src/routes/supportroute.js @@ -0,0 +1,13 @@ +import {Router} from "express"; +import validatorMiddleware from "../middleware/validator.middleware.js"; +import { supportSchema } from "../validator_schema/inputShema.js"; +import { userQuery, clearTicket } from "../controller/supportcontroller.js"; + +const supportRouter = Router(); +//subscribe newsletter +supportRouter.route("/ticket").post(validatorMiddleware(supportSchema, "body"), userQuery ); +//unsubcribe newsletter +supportRouter.route("/:id").patch(clearTicket); + + +export default supportRouter; \ No newline at end of file diff --git a/src/validator_schema/inputShema.js b/src/validator_schema/inputShema.js index 19c0c3e..373d58b 100644 --- a/src/validator_schema/inputShema.js +++ b/src/validator_schema/inputShema.js @@ -19,3 +19,17 @@ export const updateBlogSchema = Joi.object({ title: Joi.string(), content: Joi.string() }); + +export const newsEmailSchema = Joi.object({ + name:Joi.string(), + email:Joi.string().email() +}) + +export const supportSchema = Joi.object({ + firstname: Joi.string().required(), + lastname: Joi.string().required(), + email: Joi.string().email().required(), + subject: Joi.string().required(), + message:Joi.string().required() + +}); From 66f6eeff4633bbe7f02481f02cb7912cacfc940d Mon Sep 17 00:00:00 2001 From: JEDIBASIL Date: Tue, 14 Mar 2023 11:54:05 +0100 Subject: [PATCH 48/57] done with kyc --- package-lock.json | 2175 ++++++++++++++++++- package.json | 4 + src/auth/user.auth.js | 3 + src/config/index.js | 25 +- src/controller/user.controller.js | 117 +- src/models/otp.model.js | 4 - src/models/user.model.js | 16 + src/routes/user.route.js | 46 +- src/strategies/google.strategy.js | 1 + src/validator_schema/createAccountShema.js | 1 - src/validator_schema/kycCredentialSchema.js | 15 + src/validator_schema/kycOtpSchema.js | 8 + src/validator_schema/verifyAccountSchema.js | 2 +- src/validator_schema/verifyKycOtpSchema.js | 7 + 14 files changed, 2405 insertions(+), 19 deletions(-) create mode 100644 src/validator_schema/kycCredentialSchema.js create mode 100644 src/validator_schema/kycOtpSchema.js create mode 100644 src/validator_schema/verifyKycOtpSchema.js diff --git a/package-lock.json b/package-lock.json index e8f9dec..d72f091 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "license": "ISC", "dependencies": { "bcrypt": "^5.1.0", + "cloudinary": "^1.35.0", "cors": "^2.8.5", "dotenv": "^16.0.3", "express": "^4.18.2", @@ -20,6 +21,9 @@ "jsonwebtoken": "^9.0.0", "moment": "^2.29.4", "mongoose": "^6.9.1", + "multer": "^1.4.5-lts.1", + "multer-storage-cloudinary": "^4.0.0", + "nexmo": "^2.9.1", "node-geoip": "^1.0.1", "nodemailer": "^6.9.1", "nodemon": "^2.0.20", @@ -1176,6 +1180,15 @@ "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==" }, + "node_modules/@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "optional": true, + "engines": { + "node": ">= 6" + } + }, "node_modules/@types/node": { "version": "18.13.0", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.13.0.tgz", @@ -1217,6 +1230,27 @@ "node": ">= 0.6" } }, + "node_modules/acorn": { + "version": "8.8.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", + "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "optional": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "optional": true, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/agent-base": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", @@ -1249,6 +1283,21 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -1299,6 +1348,11 @@ "node": ">= 8" } }, + "node_modules/append-field": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", + "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==" + }, "node_modules/aproba": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", @@ -1329,6 +1383,22 @@ "node": ">=0.10.0" } }, + "node_modules/asn1": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", + "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", + "dependencies": { + "safer-buffer": "~2.1.0" + } + }, + "node_modules/assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", + "engines": { + "node": ">=0.8" + } + }, "node_modules/assured": { "version": "1.0.15", "resolved": "https://registry.npmjs.org/assured/-/assured-1.0.15.tgz", @@ -1338,11 +1408,41 @@ "sliced": "^1.0.1" } }, + "node_modules/ast-types": { + "version": "0.13.4", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz", + "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==", + "optional": true, + "dependencies": { + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/async": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==" }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", + "engines": { + "node": "*" + } + }, + "node_modules/aws4": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.12.0.tgz", + "integrity": "sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==" + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -1388,6 +1488,14 @@ "node": ">= 10.0.0" } }, + "node_modules/bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", + "dependencies": { + "tweetnacl": "^0.14.3" + } + }, "node_modules/binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", @@ -1492,6 +1600,22 @@ "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + }, + "node_modules/busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "dependencies": { + "streamsearch": "^1.1.0" + }, + "engines": { + "node": ">=10.16.0" + } + }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -1512,6 +1636,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==" + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -1580,6 +1709,31 @@ "node": ">=10" } }, + "node_modules/cloudinary": { + "version": "1.35.0", + "resolved": "https://registry.npmjs.org/cloudinary/-/cloudinary-1.35.0.tgz", + "integrity": "sha512-0aRLVOaVsoiqDCNkSzlPGR/oYOlE3AGrtwFTPx7xlZCPyKNzqeD9t9ENmPwzHn3yn/afAvy4h1sqb2yPyJOAow==", + "dependencies": { + "cloudinary-core": "^2.10.2", + "core-js": "^3.6.5", + "lodash": "^4.17.21", + "q": "^1.5.1" + }, + "engines": { + "node": ">=0.6" + }, + "optionalDependencies": { + "proxy-agent": "^5.0.0" + } + }, + "node_modules/cloudinary-core": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/cloudinary-core/-/cloudinary-core-2.13.0.tgz", + "integrity": "sha512-Nt0Q5I2FtenmJghtC4YZ3MZZbGg1wLm84SsxcuVwZ83OyJqG9CNIGp86CiI6iDv3QobaqBUpOT7vg+HqY5HxEA==", + "peerDependencies": { + "lodash": ">=4.0" + } + }, "node_modules/color": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", @@ -1628,11 +1782,68 @@ "text-hex": "1.0.x" } }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, + "node_modules/concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "engines": [ + "node >= 0.8" + ], + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/concat-stream/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, + "node_modules/concat-stream/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/concat-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/concat-stream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/console-control-strings": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", @@ -1670,6 +1881,21 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, + "node_modules/core-js": { + "version": "3.29.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.29.0.tgz", + "integrity": "sha512-VG23vuEisJNkGl6XQmFJd3rEG/so/CNatqeE+7uZAwTSwFeB/qaO0be8xZYUNWprJ/GIwL8aMt9cj1kvbpTZhg==", + "hasInstallScript": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + }, "node_modules/cors": { "version": "2.8.5", "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", @@ -1682,6 +1908,26 @@ "node": ">= 0.10" } }, + "node_modules/dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", + "dependencies": { + "assert-plus": "^1.0.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/data-uri-to-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-3.0.1.tgz", + "integrity": "sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og==", + "optional": true, + "engines": { + "node": ">= 6" + } + }, "node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -1690,6 +1936,12 @@ "ms": "2.0.0" } }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "optional": true + }, "node_modules/deffy": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/deffy/-/deffy-2.2.4.tgz", @@ -1698,6 +1950,29 @@ "typpy": "^2.0.0" } }, + "node_modules/degenerator": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-3.0.2.tgz", + "integrity": "sha512-c0mef3SNQo56t6urUU6tdQAs+ThoD0o9B9MJ8HEt7NQcGEILCRFqQb7ZbP9JAv+QF1Ky5plydhMR/IrqWDm+TQ==", + "optional": true, + "dependencies": { + "ast-types": "^0.13.2", + "escodegen": "^1.8.1", + "esprima": "^4.0.0", + "vm2": "^3.9.8" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/delegates": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", @@ -1736,6 +2011,20 @@ "node": ">=12" } }, + "node_modules/ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", + "dependencies": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "node_modules/ecc-jsbn/node_modules/jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==" + }, "node_modules/ecdsa-sig-formatter": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", @@ -1772,6 +2061,28 @@ "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" }, + "node_modules/escodegen": { + "version": "1.14.3", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz", + "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==", + "optional": true, + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^4.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=4.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, "node_modules/esm": { "version": "3.2.25", "resolved": "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz", @@ -1781,6 +2092,37 @@ "node": ">=6" } }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "optional": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "optional": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", @@ -1830,6 +2172,35 @@ "node": ">= 0.10.0" } }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "node_modules/extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", + "engines": [ + "node >=0.6.0" + ] + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "optional": true + }, "node_modules/fast-xml-parser": { "version": "4.0.11", "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.0.11.tgz", @@ -1859,6 +2230,15 @@ "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==" }, + "node_modules/file-uri-to-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-2.0.0.tgz", + "integrity": "sha512-hjPFI8oE/2iQPVe4gbrJ73Pp+Xfub2+WI2LlXDbsaJBwT5wuMh35WNWVYYTpnz895shtwfyutMFLFywpQAFdLg==", + "optional": true, + "engines": { + "node": ">= 6" + } + }, "node_modules/fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -1911,6 +2291,27 @@ } } }, + "node_modules/forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", + "engines": { + "node": "*" + } + }, + "node_modules/form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 0.12" + } + }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -1927,6 +2328,20 @@ "node": ">= 0.6" } }, + "node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "optional": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, "node_modules/fs-minipass": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", @@ -1967,6 +2382,37 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, + "node_modules/ftp": { + "version": "0.3.10", + "resolved": "https://registry.npmjs.org/ftp/-/ftp-0.3.10.tgz", + "integrity": "sha512-faFVML1aBx2UoDStmLwv2Wptt4vw5x03xxX172nhA5Y5HBshW5JweqQ2W4xL4dezQTG8inJsuYcpPHHU3X5OTQ==", + "optional": true, + "dependencies": { + "readable-stream": "1.1.x", + "xregexp": "2.0.0" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/ftp/node_modules/readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==", + "optional": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "node_modules/ftp/node_modules/string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==", + "optional": true + }, "node_modules/function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", @@ -2048,6 +2494,54 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-uri": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-3.0.2.tgz", + "integrity": "sha512-+5s0SJbGoyiJTZZ2JTpFPLMPSch72KEqGOTvQsBqg0RBWvwhWUSYZFAtz3TPW0GXJuLBJPts1E241iHg+VRfhg==", + "optional": true, + "dependencies": { + "@tootallnate/once": "1", + "data-uri-to-buffer": "3", + "debug": "4", + "file-uri-to-path": "2", + "fs-extra": "^8.1.0", + "ftp": "^0.3.10" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/get-uri/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "optional": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/get-uri/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "optional": true + }, + "node_modules/getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", + "dependencies": { + "assert-plus": "^1.0.0" + } + }, "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -2078,6 +2572,12 @@ "node": ">= 6" } }, + "node_modules/graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "optional": true + }, "node_modules/handlebars": { "version": "4.7.7", "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", @@ -2098,6 +2598,27 @@ "uglify-js": "^3.1.4" } }, + "node_modules/har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==", + "engines": { + "node": ">=4" + } + }, + "node_modules/har-validator": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "deprecated": "this library is no longer supported", + "dependencies": { + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -2148,6 +2669,57 @@ "node": ">= 0.8" } }, + "node_modules/http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "optional": true, + "dependencies": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/http-proxy-agent/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "optional": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/http-proxy-agent/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "optional": true + }, + "node_modules/http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==", + "dependencies": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + }, + "engines": { + "node": ">=0.8", + "npm": ">=1.3.7" + } + }, "node_modules/https-proxy-agent": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", @@ -2329,6 +2901,22 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" + }, + "node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", + "optional": true + }, + "node_modules/isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==" + }, "node_modules/joi": { "version": "17.8.3", "resolved": "https://registry.npmjs.org/joi/-/joi-17.8.3.tgz", @@ -2346,6 +2934,30 @@ "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==" }, + "node_modules/json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==" + }, + "node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "optional": true, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, "node_modules/jsonrequest": { "version": "4.2.6", "resolved": "https://registry.npmjs.org/jsonrequest/-/jsonrequest-4.2.6.tgz", @@ -2376,6 +2988,20 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, + "node_modules/jsprim": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", + "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", + "dependencies": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.4.0", + "verror": "1.10.0" + }, + "engines": { + "node": ">=0.6.0" + } + }, "node_modules/jwa": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", @@ -2416,11 +3042,59 @@ "node": ">=0.2.0" } }, + "node_modules/levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", + "optional": true, + "dependencies": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" + }, "node_modules/logform": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/logform/-/logform-2.5.1.tgz", @@ -2695,6 +3369,42 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, + "node_modules/multer": { + "version": "1.4.5-lts.1", + "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.5-lts.1.tgz", + "integrity": "sha512-ywPWvcDMeH+z9gQq5qYHCCy+ethsk4goepZ45GLD63fOu0YcNecQxi64nDs3qluZB+murG3/D4dJ7+dGctcCQQ==", + "dependencies": { + "append-field": "^1.0.0", + "busboy": "^1.0.0", + "concat-stream": "^1.5.2", + "mkdirp": "^0.5.4", + "object-assign": "^4.1.1", + "type-is": "^1.6.4", + "xtend": "^4.0.0" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/multer-storage-cloudinary": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/multer-storage-cloudinary/-/multer-storage-cloudinary-4.0.0.tgz", + "integrity": "sha512-25lm9R6o5dWrHLqLvygNX+kBOxprzpmZdnVKH4+r68WcfCt8XV6xfQaMuAg+kUE5Xmr8mJNA4gE0AcBj9FJyWA==", + "peerDependencies": { + "cloudinary": "^1.21.0" + } + }, + "node_modules/multer/node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, "node_modules/negotiator": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", @@ -2708,6 +3418,65 @@ "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" }, + "node_modules/netmask": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz", + "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==", + "optional": true, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/nexmo": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/nexmo/-/nexmo-2.9.1.tgz", + "integrity": "sha512-FxmJ2Ou5qi4BH6hSo24OiFwPVYPl08uyXv13KybmkAbjh9W+kpaPK2wwsg1Dz39rrgIyAhWAq+4cqMvmk1UjrQ==", + "dependencies": { + "jsonwebtoken": "^8.4.0", + "request": "^2.88.0", + "uuid": "^2.0.2" + } + }, + "node_modules/nexmo/node_modules/jsonwebtoken": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", + "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==", + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^5.6.0" + }, + "engines": { + "node": ">=4", + "npm": ">=1.4.28" + } + }, + "node_modules/nexmo/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/nexmo/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/nexmo/node_modules/uuid": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.3.tgz", + "integrity": "sha512-FULf7fayPdpASncVy4DLh3xydlXEJJpvIELjYjNeQWYUZ9pclcpvCZSr2gkmN2FrrGcI7G/cJsIEwk5/8vfXpg==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details." + }, "node_modules/node-addon-api": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", @@ -2855,6 +3624,14 @@ "resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz", "integrity": "sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA==" }, + "node_modules/oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "engines": { + "node": "*" + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -2898,6 +3675,86 @@ "fn.name": "1.x.x" } }, + "node_modules/optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "optional": true, + "dependencies": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/pac-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-5.0.0.tgz", + "integrity": "sha512-CcFG3ZtnxO8McDigozwE3AqAw15zDvGH+OjXO4kzf7IkEKkQ4gxQ+3sdF50WmhQ4P/bVusXcqNE2S3XrNURwzQ==", + "optional": true, + "dependencies": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4", + "get-uri": "3", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "5", + "pac-resolver": "^5.0.0", + "raw-body": "^2.2.0", + "socks-proxy-agent": "5" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/pac-proxy-agent/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "optional": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/pac-proxy-agent/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "optional": true + }, + "node_modules/pac-resolver": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-5.0.1.tgz", + "integrity": "sha512-cy7u00ko2KVgBAjuhevqpPeHIkCIqPe1v24cydhWjmeuzaBfmUWFCZJ1iAh5TuVzVZoUzXIW7K8sMYOZ84uZ9Q==", + "optional": true, + "dependencies": { + "degenerator": "^3.0.2", + "ip": "^1.1.5", + "netmask": "^2.0.2" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/pac-resolver/node_modules/ip": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.8.tgz", + "integrity": "sha512-PuExPYUiu6qMBQb4l06ecm6T6ujzhmh+MeJcW9wa89PoAz5pvd4zPgN5WJV104mb6S2T1AwNIAaB70JNrLQWhg==", + "optional": true + }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -2992,6 +3849,11 @@ "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==" }, + "node_modules/performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==" + }, "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", @@ -3003,6 +3865,20 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", + "optional": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -3015,6 +3891,74 @@ "node": ">= 0.10" } }, + "node_modules/proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-5.0.0.tgz", + "integrity": "sha512-gkH7BkvLVkSfX9Dk27W6TyNOWWZWRilRfk1XxGNWOYJ2TuedAv1yFpCaU9QSBmBe716XOTNpYNOzhysyw8xn7g==", + "optional": true, + "dependencies": { + "agent-base": "^6.0.0", + "debug": "4", + "http-proxy-agent": "^4.0.0", + "https-proxy-agent": "^5.0.0", + "lru-cache": "^5.1.1", + "pac-proxy-agent": "^5.0.0", + "proxy-from-env": "^1.0.0", + "socks-proxy-agent": "^5.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/proxy-agent/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "optional": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/proxy-agent/node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "optional": true, + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/proxy-agent/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "optional": true + }, + "node_modules/proxy-agent/node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "optional": true + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "optional": true + }, + "node_modules/psl": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==" + }, "node_modules/pstree.remy": { "version": "1.1.8", "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", @@ -3028,6 +3972,15 @@ "node": ">=6" } }, + "node_modules/q": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", + "integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==", + "engines": { + "node": ">=0.6.0", + "teleport": ">=0.2.0" + } + }, "node_modules/qs": { "version": "6.11.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", @@ -3108,11 +4061,59 @@ "node": ">=8.10.0" } }, + "node_modules/request": { + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142", + "dependencies": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/request-ip": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/request-ip/-/request-ip-3.3.0.tgz", "integrity": "sha512-cA6Xh6e0fDBBBwH77SLJaJPBmD3nWVAcF9/XAcsrIHdjhFzFiB5aNQFytdjCGPezU3ROwrR11IddKAM08vohxA==" }, + "node_modules/request/node_modules/qs": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", + "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/request/node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "bin": { + "uuid": "bin/uuid" + } + }, "node_modules/rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", @@ -3314,6 +4315,43 @@ "npm": ">= 3.0.0" } }, + "node_modules/socks-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-5.0.1.tgz", + "integrity": "sha512-vZdmnjb9a2Tz6WEQVIurybSwElwPxMZaIc7PzqbJTrezcKNznv6giT7J7tZDZ1BojVaa1jvO/UiUdhDVB0ACoQ==", + "optional": true, + "dependencies": { + "agent-base": "^6.0.2", + "debug": "4", + "socks": "^2.3.3" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/socks-proxy-agent/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "optional": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socks-proxy-agent/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "optional": true + }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -3336,6 +4374,35 @@ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==" }, + "node_modules/sshpk": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz", + "integrity": "sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==", + "dependencies": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + }, + "bin": { + "sshpk-conv": "bin/sshpk-conv", + "sshpk-sign": "bin/sshpk-sign", + "sshpk-verify": "bin/sshpk-verify" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sshpk/node_modules/jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==" + }, "node_modules/stack-trace": { "version": "0.0.10", "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", @@ -3352,6 +4419,14 @@ "node": ">= 0.8" } }, + "node_modules/streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -3477,6 +4552,18 @@ "node": "*" } }, + "node_modules/tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "dependencies": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/tr46": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", @@ -3499,6 +4586,34 @@ "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", "optional": true }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==" + }, + "node_modules/type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", + "optional": true, + "dependencies": { + "prelude-ls": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", @@ -3511,6 +4626,11 @@ "node": ">= 0.6" } }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==" + }, "node_modules/typpy": { "version": "2.3.13", "resolved": "https://registry.npmjs.org/typpy/-/typpy-2.3.13.tgz", @@ -3550,6 +4670,15 @@ "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==" }, + "node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "optional": true, + "engines": { + "node": ">= 4.0.0" + } + }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -3558,6 +4687,14 @@ "node": ">= 0.8" } }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dependencies": { + "punycode": "^2.1.0" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -3587,6 +4724,40 @@ "node": ">= 0.8" } }, + "node_modules/verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", + "engines": [ + "node >=0.6.0" + ], + "dependencies": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "node_modules/verror/node_modules/core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==" + }, + "node_modules/vm2": { + "version": "3.9.14", + "resolved": "https://registry.npmjs.org/vm2/-/vm2-3.9.14.tgz", + "integrity": "sha512-HgvPHYHeQy8+QhzlFryvSteA4uQLBCOub02mgqdR+0bN/akRZ48TGB1v0aCv7ksyc0HXx16AZtMHKS38alc6TA==", + "optional": true, + "dependencies": { + "acorn": "^8.7.0", + "acorn-walk": "^8.2.0" + }, + "bin": { + "vm2": "bin/vm2" + }, + "engines": { + "node": ">=6.0" + } + }, "node_modules/webidl-conversions": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", @@ -3649,6 +4820,15 @@ "node": ">= 6.4.0" } }, + "node_modules/word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/wordwrap": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", @@ -3659,6 +4839,23 @@ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, + "node_modules/xregexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-2.0.0.tgz", + "integrity": "sha512-xl/50/Cf32VsGq/1R8jJE5ajH1yMCQkpmoS10QbFZWl2Oor4H0Me64Pu2yxvsRWK3m6soJbmGfzSR7BYmDcWAA==", + "optional": true, + "engines": { + "node": "*" + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "engines": { + "node": ">=0.4" + } + }, "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", @@ -4649,6 +5846,12 @@ "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==" }, + "@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "optional": true + }, "@types/node": { "version": "18.13.0", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.13.0.tgz", @@ -4687,6 +5890,18 @@ "negotiator": "0.6.3" } }, + "acorn": { + "version": "8.8.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", + "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "optional": true + }, + "acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "optional": true + }, "agent-base": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", @@ -4710,6 +5925,17 @@ } } }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, "ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -4747,6 +5973,11 @@ "picomatch": "^2.0.4" } }, + "append-field": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", + "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==" + }, "aproba": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", @@ -4771,6 +6002,19 @@ "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.2.tgz", "integrity": "sha512-GVYjmpL05al4dNlKJm53mKE4w9OOLiuVHWorsIA3YVz+Hu0hcn6PtE3Ydl0EqU7v+7ABC4mjjWsnLUxbpno+CA==" }, + "asn1": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", + "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==" + }, "assured": { "version": "1.0.15", "resolved": "https://registry.npmjs.org/assured/-/assured-1.0.15.tgz", @@ -4780,11 +6024,35 @@ "sliced": "^1.0.1" } }, + "ast-types": { + "version": "0.13.4", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz", + "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==", + "optional": true, + "requires": { + "tslib": "^2.0.1" + } + }, "async": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==" }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==" + }, + "aws4": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.12.0.tgz", + "integrity": "sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==" + }, "balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -4809,6 +6077,14 @@ "node-addon-api": "^5.0.0" } }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", + "requires": { + "tweetnacl": "^0.14.3" + } + }, "binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", @@ -4883,6 +6159,19 @@ "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" }, + "buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + }, + "busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "requires": { + "streamsearch": "^1.1.0" + } + }, "bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -4897,6 +6186,11 @@ "get-intrinsic": "^1.0.2" } }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==" + }, "chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -4941,6 +6235,24 @@ "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==" }, + "cloudinary": { + "version": "1.35.0", + "resolved": "https://registry.npmjs.org/cloudinary/-/cloudinary-1.35.0.tgz", + "integrity": "sha512-0aRLVOaVsoiqDCNkSzlPGR/oYOlE3AGrtwFTPx7xlZCPyKNzqeD9t9ENmPwzHn3yn/afAvy4h1sqb2yPyJOAow==", + "requires": { + "cloudinary-core": "^2.10.2", + "core-js": "^3.6.5", + "lodash": "^4.17.21", + "proxy-agent": "^5.0.0", + "q": "^1.5.1" + } + }, + "cloudinary-core": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/cloudinary-core/-/cloudinary-core-2.13.0.tgz", + "integrity": "sha512-Nt0Q5I2FtenmJghtC4YZ3MZZbGg1wLm84SsxcuVwZ83OyJqG9CNIGp86CiI6iDv3QobaqBUpOT7vg+HqY5HxEA==", + "requires": {} + }, "color": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", @@ -4986,11 +6298,64 @@ "text-hex": "1.0.x" } }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, + "readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, "console-control-strings": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", @@ -5019,6 +6384,16 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, + "core-js": { + "version": "3.29.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.29.0.tgz", + "integrity": "sha512-VG23vuEisJNkGl6XQmFJd3rEG/so/CNatqeE+7uZAwTSwFeB/qaO0be8xZYUNWprJ/GIwL8aMt9cj1kvbpTZhg==" + }, + "core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + }, "cors": { "version": "2.8.5", "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", @@ -5028,6 +6403,20 @@ "vary": "^1" } }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "data-uri-to-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-3.0.1.tgz", + "integrity": "sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og==", + "optional": true + }, "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -5036,6 +6425,12 @@ "ms": "2.0.0" } }, + "deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "optional": true + }, "deffy": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/deffy/-/deffy-2.2.4.tgz", @@ -5044,6 +6439,23 @@ "typpy": "^2.0.0" } }, + "degenerator": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-3.0.2.tgz", + "integrity": "sha512-c0mef3SNQo56t6urUU6tdQAs+ThoD0o9B9MJ8HEt7NQcGEILCRFqQb7ZbP9JAv+QF1Ky5plydhMR/IrqWDm+TQ==", + "optional": true, + "requires": { + "ast-types": "^0.13.2", + "escodegen": "^1.8.1", + "esprima": "^4.0.0", + "vm2": "^3.9.8" + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" + }, "delegates": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", @@ -5069,6 +6481,22 @@ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz", "integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==" }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + }, + "dependencies": { + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==" + } + } + }, "ecdsa-sig-formatter": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", @@ -5102,12 +6530,43 @@ "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" }, + "escodegen": { + "version": "1.14.3", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz", + "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==", + "optional": true, + "requires": { + "esprima": "^4.0.1", + "estraverse": "^4.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1", + "source-map": "~0.6.1" + } + }, "esm": { "version": "3.2.25", "resolved": "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz", "integrity": "sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==", "dev": true }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "optional": true + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "optional": true + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "optional": true + }, "etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", @@ -5151,6 +6610,32 @@ "vary": "~1.1.2" } }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==" + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "optional": true + }, "fast-xml-parser": { "version": "4.0.11", "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.0.11.tgz", @@ -5173,6 +6658,12 @@ "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==" }, + "file-uri-to-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-2.0.0.tgz", + "integrity": "sha512-hjPFI8oE/2iQPVe4gbrJ73Pp+Xfub2+WI2LlXDbsaJBwT5wuMh35WNWVYYTpnz895shtwfyutMFLFywpQAFdLg==", + "optional": true + }, "fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -5205,6 +6696,21 @@ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==" }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==" + }, + "form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, "forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -5215,6 +6721,17 @@ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==" }, + "fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "optional": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, "fs-minipass": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", @@ -5244,6 +6761,36 @@ "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", "optional": true }, + "ftp": { + "version": "0.3.10", + "resolved": "https://registry.npmjs.org/ftp/-/ftp-0.3.10.tgz", + "integrity": "sha512-faFVML1aBx2UoDStmLwv2Wptt4vw5x03xxX172nhA5Y5HBshW5JweqQ2W4xL4dezQTG8inJsuYcpPHHU3X5OTQ==", + "optional": true, + "requires": { + "readable-stream": "1.1.x", + "xregexp": "2.0.0" + }, + "dependencies": { + "readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==", + "optional": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==", + "optional": true + } + } + }, "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", @@ -5305,14 +6852,53 @@ } } }, - "get-intrinsic": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", - "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", + "get-intrinsic": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", + "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.3" + } + }, + "get-uri": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-3.0.2.tgz", + "integrity": "sha512-+5s0SJbGoyiJTZZ2JTpFPLMPSch72KEqGOTvQsBqg0RBWvwhWUSYZFAtz3TPW0GXJuLBJPts1E241iHg+VRfhg==", + "optional": true, + "requires": { + "@tootallnate/once": "1", + "data-uri-to-buffer": "3", + "debug": "4", + "file-uri-to-path": "2", + "fs-extra": "^8.1.0", + "ftp": "^0.3.10" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "optional": true, + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "optional": true + } + } + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", "requires": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.3" + "assert-plus": "^1.0.0" } }, "glob": { @@ -5336,6 +6922,12 @@ "is-glob": "^4.0.1" } }, + "graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "optional": true + }, "handlebars": { "version": "4.7.7", "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", @@ -5348,6 +6940,20 @@ "wordwrap": "^1.0.0" } }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==" + }, + "har-validator": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "requires": { + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + } + }, "has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -5383,6 +6989,44 @@ "toidentifier": "1.0.1" } }, + "http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "optional": true, + "requires": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "optional": true, + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "optional": true + } + } + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==", + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, "https-proxy-agent": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", @@ -5508,6 +7152,22 @@ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==" }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" + }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", + "optional": true + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==" + }, "joi": { "version": "17.8.3", "resolved": "https://registry.npmjs.org/joi/-/joi-17.8.3.tgz", @@ -5525,6 +7185,30 @@ "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==" }, + "json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==" + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==" + }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "optional": true, + "requires": { + "graceful-fs": "^4.1.6" + } + }, "jsonrequest": { "version": "4.2.6", "resolved": "https://registry.npmjs.org/jsonrequest/-/jsonrequest-4.2.6.tgz", @@ -5553,6 +7237,17 @@ } } }, + "jsprim": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", + "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.4.0", + "verror": "1.10.0" + } + }, "jwa": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", @@ -5587,11 +7282,56 @@ "resolved": "https://registry.npmjs.org/lazy/-/lazy-1.0.11.tgz", "integrity": "sha512-Y+CjUfLmIpoUCCRl0ub4smrYtGGr5AOa2AKOaWelGHOGz33X/Y/KizefGqbkwfz44+cnq/+9habclf8vOmu2LA==" }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", + "optional": true, + "requires": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + } + }, "lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, + "lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" + }, + "lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" + }, + "lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" + }, + "lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" + }, + "lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" + }, + "lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" + }, + "lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" + }, "logform": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/logform/-/logform-2.5.1.tgz", @@ -5796,6 +7536,36 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, + "multer": { + "version": "1.4.5-lts.1", + "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.5-lts.1.tgz", + "integrity": "sha512-ywPWvcDMeH+z9gQq5qYHCCy+ethsk4goepZ45GLD63fOu0YcNecQxi64nDs3qluZB+murG3/D4dJ7+dGctcCQQ==", + "requires": { + "append-field": "^1.0.0", + "busboy": "^1.0.0", + "concat-stream": "^1.5.2", + "mkdirp": "^0.5.4", + "object-assign": "^4.1.1", + "type-is": "^1.6.4", + "xtend": "^4.0.0" + }, + "dependencies": { + "mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "requires": { + "minimist": "^1.2.6" + } + } + } + }, + "multer-storage-cloudinary": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/multer-storage-cloudinary/-/multer-storage-cloudinary-4.0.0.tgz", + "integrity": "sha512-25lm9R6o5dWrHLqLvygNX+kBOxprzpmZdnVKH4+r68WcfCt8XV6xfQaMuAg+kUE5Xmr8mJNA4gE0AcBj9FJyWA==", + "requires": {} + }, "negotiator": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", @@ -5806,6 +7576,56 @@ "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" }, + "netmask": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz", + "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==", + "optional": true + }, + "nexmo": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/nexmo/-/nexmo-2.9.1.tgz", + "integrity": "sha512-FxmJ2Ou5qi4BH6hSo24OiFwPVYPl08uyXv13KybmkAbjh9W+kpaPK2wwsg1Dz39rrgIyAhWAq+4cqMvmk1UjrQ==", + "requires": { + "jsonwebtoken": "^8.4.0", + "request": "^2.88.0", + "uuid": "^2.0.2" + }, + "dependencies": { + "jsonwebtoken": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", + "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==", + "requires": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^5.6.0" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + }, + "uuid": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.3.tgz", + "integrity": "sha512-FULf7fayPdpASncVy4DLh3xydlXEJJpvIELjYjNeQWYUZ9pclcpvCZSr2gkmN2FrrGcI7G/cJsIEwk5/8vfXpg==" + } + } + }, "node-addon-api": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", @@ -5921,6 +7741,11 @@ "resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz", "integrity": "sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA==" }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" + }, "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -5955,6 +7780,73 @@ "fn.name": "1.x.x" } }, + "optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "optional": true, + "requires": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + } + }, + "pac-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-5.0.0.tgz", + "integrity": "sha512-CcFG3ZtnxO8McDigozwE3AqAw15zDvGH+OjXO4kzf7IkEKkQ4gxQ+3sdF50WmhQ4P/bVusXcqNE2S3XrNURwzQ==", + "optional": true, + "requires": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4", + "get-uri": "3", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "5", + "pac-resolver": "^5.0.0", + "raw-body": "^2.2.0", + "socks-proxy-agent": "5" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "optional": true, + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "optional": true + } + } + }, + "pac-resolver": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-5.0.1.tgz", + "integrity": "sha512-cy7u00ko2KVgBAjuhevqpPeHIkCIqPe1v24cydhWjmeuzaBfmUWFCZJ1iAh5TuVzVZoUzXIW7K8sMYOZ84uZ9Q==", + "optional": true, + "requires": { + "degenerator": "^3.0.2", + "ip": "^1.1.5", + "netmask": "^2.0.2" + }, + "dependencies": { + "ip": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.8.tgz", + "integrity": "sha512-PuExPYUiu6qMBQb4l06ecm6T6ujzhmh+MeJcW9wa89PoAz5pvd4zPgN5WJV104mb6S2T1AwNIAaB70JNrLQWhg==", + "optional": true + } + } + }, "parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -6023,11 +7915,27 @@ "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==" }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==" + }, "picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==" }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", + "optional": true + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, "proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -6037,6 +7945,65 @@ "ipaddr.js": "1.9.1" } }, + "proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-5.0.0.tgz", + "integrity": "sha512-gkH7BkvLVkSfX9Dk27W6TyNOWWZWRilRfk1XxGNWOYJ2TuedAv1yFpCaU9QSBmBe716XOTNpYNOzhysyw8xn7g==", + "optional": true, + "requires": { + "agent-base": "^6.0.0", + "debug": "4", + "http-proxy-agent": "^4.0.0", + "https-proxy-agent": "^5.0.0", + "lru-cache": "^5.1.1", + "pac-proxy-agent": "^5.0.0", + "proxy-from-env": "^1.0.0", + "socks-proxy-agent": "^5.0.0" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "optional": true, + "requires": { + "ms": "2.1.2" + } + }, + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "optional": true, + "requires": { + "yallist": "^3.0.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "optional": true + }, + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "optional": true + } + } + }, + "proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "optional": true + }, + "psl": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==" + }, "pstree.remy": { "version": "1.1.8", "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", @@ -6047,6 +8014,11 @@ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==" }, + "q": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", + "integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==" + }, "qs": { "version": "6.11.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", @@ -6103,6 +8075,45 @@ "picomatch": "^2.2.1" } }, + "request": { + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "dependencies": { + "qs": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", + "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==" + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" + } + } + }, "request-ip": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/request-ip/-/request-ip-3.3.0.tgz", @@ -6258,6 +8269,34 @@ "smart-buffer": "^4.2.0" } }, + "socks-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-5.0.1.tgz", + "integrity": "sha512-vZdmnjb9a2Tz6WEQVIurybSwElwPxMZaIc7PzqbJTrezcKNznv6giT7J7tZDZ1BojVaa1jvO/UiUdhDVB0ACoQ==", + "optional": true, + "requires": { + "agent-base": "^6.0.2", + "debug": "4", + "socks": "^2.3.3" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "optional": true, + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "optional": true + } + } + }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -6277,6 +8316,29 @@ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==" }, + "sshpk": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz", + "integrity": "sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==", + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + }, + "dependencies": { + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==" + } + } + }, "stack-trace": { "version": "0.0.10", "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", @@ -6287,6 +8349,11 @@ "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" }, + "streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==" + }, "string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -6387,6 +8454,15 @@ } } }, + "tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "requires": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + } + }, "tr46": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", @@ -6406,6 +8482,28 @@ "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", "optional": true }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==" + }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", + "optional": true, + "requires": { + "prelude-ls": "~1.1.2" + } + }, "type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", @@ -6415,6 +8513,11 @@ "mime-types": "~2.1.24" } }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==" + }, "typpy": { "version": "2.3.13", "resolved": "https://registry.npmjs.org/typpy/-/typpy-2.3.13.tgz", @@ -6448,11 +8551,25 @@ "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==" }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "optional": true + }, "unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==" }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "requires": { + "punycode": "^2.1.0" + } + }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -6473,6 +8590,33 @@ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==" }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + }, + "dependencies": { + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==" + } + } + }, + "vm2": { + "version": "3.9.14", + "resolved": "https://registry.npmjs.org/vm2/-/vm2-3.9.14.tgz", + "integrity": "sha512-HgvPHYHeQy8+QhzlFryvSteA4uQLBCOub02mgqdR+0bN/akRZ48TGB1v0aCv7ksyc0HXx16AZtMHKS38alc6TA==", + "optional": true, + "requires": { + "acorn": "^8.7.0", + "acorn-walk": "^8.2.0" + } + }, "webidl-conversions": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", @@ -6523,6 +8667,12 @@ "triple-beam": "^1.3.0" } }, + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "optional": true + }, "wordwrap": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", @@ -6533,6 +8683,17 @@ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, + "xregexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-2.0.0.tgz", + "integrity": "sha512-xl/50/Cf32VsGq/1R8jJE5ajH1yMCQkpmoS10QbFZWl2Oor4H0Me64Pu2yxvsRWK3m6soJbmGfzSR7BYmDcWAA==", + "optional": true + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" + }, "yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", diff --git a/package.json b/package.json index b0e421b..ca7f41c 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "license": "ISC", "dependencies": { "bcrypt": "^5.1.0", + "cloudinary": "^1.35.0", "cors": "^2.8.5", "dotenv": "^16.0.3", "express": "^4.18.2", @@ -23,6 +24,9 @@ "jsonwebtoken": "^9.0.0", "moment": "^2.29.4", "mongoose": "^6.9.1", + "multer": "^1.4.5-lts.1", + "multer-storage-cloudinary": "^4.0.0", + "nexmo": "^2.9.1", "node-geoip": "^1.0.1", "nodemailer": "^6.9.1", "nodemon": "^2.0.20", diff --git a/src/auth/user.auth.js b/src/auth/user.auth.js index 8416517..5f56d3e 100644 --- a/src/auth/user.auth.js +++ b/src/auth/user.auth.js @@ -7,6 +7,9 @@ export async function userAuth(req, res, next) { const key = hasToken(req) const user = await userModel.findById(key) if (!user) throw new HttpException(401, "Unauthorized client") + if (!user.isVerified) throw new HttpException(401, "email not verified") + if (user.isBlocked) throw new HttpException(401, "account blocked") + req["user"] = user next() } catch (err) { diff --git a/src/config/index.js b/src/config/index.js index c127f1d..7437e40 100644 --- a/src/config/index.js +++ b/src/config/index.js @@ -1,7 +1,10 @@ import { config } from "dotenv"; import { connect, set } from "mongoose"; import logger from "../utils/logger"; - +import { CloudinaryStorage } from 'multer-storage-cloudinary'; +import multer from 'multer'; +import { v2 as cloudinary } from "cloudinary" +import Nexmo from "nexmo" config(); @@ -27,6 +30,26 @@ export async function connectDB() { } }; +cloudinary.config({ + cloud_name: process.env.CLOUDINARY_NAME, + api_key: process.env.CLOUDINARY_API_KEY, + api_secret: process.env.CLOUDINARY_SECRET_KEY, +}) + +const storage = new CloudinaryStorage({ + cloudinary: cloudinary, + allowedFormats: ['jpg', 'jpeg', 'png', 'gif'], +}); + +const parser = multer({ storage: storage }); + +const nexmo = new Nexmo({ + apiKey: process.env.NEXMO_KEY, + apiSecret: process.env.NEXMO_SECRET, + }); + +export {cloudinary, parser as fileParser, nexmo } + export const { MONGODB_URI, ACCESS_TOKEN, diff --git a/src/controller/user.controller.js b/src/controller/user.controller.js index 92e7994..1c6d77e 100644 --- a/src/controller/user.controller.js +++ b/src/controller/user.controller.js @@ -3,7 +3,7 @@ import userModel from "../models/user.model"; import jwt from "jsonwebtoken"; import emailTemplateReader from "../utils/emailTemplateReader"; import HttpResponse from "../response/HttpResponse"; -import { ACCESS_TOKEN, WEB_URL, IPINFO_TOKEN } from "../config"; +import { ACCESS_TOKEN, WEB_URL, IPINFO_TOKEN, nexmo } from "../config"; import sendMail from "../utils/sendMail"; import randomstring from "randomstring"; import otpModel from "../models/otp.model"; @@ -104,7 +104,7 @@ async function sendVerificationMail(email) { }); const OTP = randomstring.generate({ - length: 6, + length: 4, charset: "numeric", }); @@ -199,6 +199,27 @@ export async function resetPassword(req, res, next) { } } +export async function getProfile(req, res, next) { + try { + const { email, username, first_name, last_name, phoneNumber, isVerified, isBlocked, address, isKycVerified } = req["user"] + return res + .status(200) + .send(new HttpResponse("success", `${email} profile`, { + email, + username, + first_name, + last_name, + phoneNumber, + isEmailVerified: isVerified, + isBlocked, + address, + isKycVerified + })); + } catch (err) { + next(err); + } +} + export async function updateProfile(req, res, next) { try { const user = req["user"] @@ -212,3 +233,95 @@ export async function updateProfile(req, res, next) { next(err); } } + +export async function uploadImg(req, res, next) { + try { + if (!req.file) throw new HttpException(400, "field is required") + return res.status(200).send(new HttpResponse("success", "image uploaded", { url: req.file.path })); + } catch (err) { + next(err) + } +} + +export async function kycOtp(req, res, next) { + try { + const { mobile } = req.body + + const verificationToken = jwt.sign({ value: mobile }, ACCESS_TOKEN, { + expiresIn: "5m", + }); + + const OTP = randomstring.generate({ + length: 4, + charset: "numeric", + }); + + nexmo.message.sendSms( + "Stakepro", + mobile, + `Verification otp\n ${OTP}`, + async (err, responseData) => { + try { + logger.error(err); + if (err) throw new HttpException(500, "otp could not be sent"); + logger.info("responseData==> " + responseData) + const addedOtp = await otpModel.create({ + key: verificationToken, + otp: OTP, + }); + if (!addedOtp) throw new HttpException(500, "otp could not be sent"); + return res.status(200).send(new HttpResponse("success", "otp resent")); + } catch (err) { + next(err) + } + } + ) + + } catch (err) { + next(err) + } +} + +export async function verifyKycOtp(req, res, next) { + try { + const { otp } = req.body; + const user = req["user"] + + const confirmedOtp = await otpModel.findOne({ otp }); + if (!confirmedOtp) throw new HttpException(400, "invalid otp"); + + const decoded = jwt.decode(confirmedOtp.key, { complete: true }); + const { exp, value } = decoded.payload; + + const currentTime = Math.floor(Date.now() / 1000); + if (currentTime >= exp) throw new HttpException(401, "otp has expired"); + + user.phoneNumber = value; + await user.save(); + + return res.status(200).send(new HttpResponse("success", "otp verified successfully")); + } catch (err) { + next(err) + } +} + +export async function kycCredentials(req, res, next) { + try { + const data = req.body + const { email, phoneNumber } = req["user"] + if (!phoneNumber) throw new HttpException(400, "verify phone number"); + + const updatedUser = await userModel.findOneAndUpdate( + { email }, + { ...data, isKycVerified: true }, + { new: true } + ); + if (updatedUser) return res.status(200).send(new HttpResponse("success", "done with kyc successfully")); + } catch (err) { + next(err) + } +} + +export async function logout(req, res, next) { + +} \ No newline at end of file diff --git a/src/models/otp.model.js b/src/models/otp.model.js index 0a83548..56e1c2e 100644 --- a/src/models/otp.model.js +++ b/src/models/otp.model.js @@ -5,13 +5,9 @@ import { Schema, model } from "mongoose"; const otpSchema = new Schema({ otp: { type: String, - required: true, - unique: true, }, key: { type: String, - required: true, - unique: true, }, addedAt: { type: Date, diff --git a/src/models/user.model.js b/src/models/user.model.js index 47caf11..6272d78 100644 --- a/src/models/user.model.js +++ b/src/models/user.model.js @@ -36,6 +36,22 @@ const userSchema = new Schema({ dob: { type: Date }, + address: { + postalCode: { + type: String, + }, + city: { + type: String, + }, + address: { + type: String, + } + }, + verifiedSelfie: String, + isKycVerified: { + type: Boolean, + default: false + }, joinedAt: { type: Date, default: () => moment().toDate(), diff --git a/src/routes/user.route.js b/src/routes/user.route.js index 3f80910..1014028 100644 --- a/src/routes/user.route.js +++ b/src/routes/user.route.js @@ -5,22 +5,29 @@ import { resetPassword, sendResetPasswordMail, verify, - updateProfile + updateProfile, + uploadImg, + kycOtp, + verifyKycOtp, + getProfile, + kycCredentials } from "../controller/user.controller"; import { Router } from "express"; import validatorMiddleware from "../middleware/validator.middleware"; import createAccountSchema from "../validator_schema/createAccountShema"; import verifyAccountSchema from "../validator_schema/verifyAccountSchema"; +import verifyKycOtpSchema from "../validator_schema/verifyKycOtpSchema"; import loginSchema from "../validator_schema/loginSchema"; import resetPasswordSchema from "../validator_schema/resetPasswordSchema"; import resendVerificationSchema from "../validator_schema/resendVerificationSchema"; +import kycCredentialSchema from "../validator_schema/kycCredentialSchema"; import passport from "passport" import jwt from "jsonwebtoken"; -import { ACCESS_TOKEN } from "../config"; +import { ACCESS_TOKEN, fileParser } from "../config"; import updateProfileSchema from "../validator_schema/updateProfileSchema" import { userAuth } from "../auth/user.auth"; import { googleStrategy } from "../strategies/google.strategy" - +import kycOtpSchema from "../validator_schema/kycOtpSchema"; googleStrategy(); @@ -81,6 +88,12 @@ userRouter resetPassword ); +userRouter.route("/profile") + .get( + userAuth, + getProfile + ) + userRouter.route("/profile") .put( userAuth, @@ -88,4 +101,31 @@ userRouter.route("/profile") updateProfile ) +userRouter.route("/kyc") + .post( + userAuth, + validatorMiddleware(kycCredentialSchema, "body"), + kycCredentials + ) + +userRouter.route("/kyc/photo") + .post( + fileParser.single("photo"), + uploadImg + ) + +userRouter.route("/kyc/otp") + .post( + userAuth, + validatorMiddleware(kycOtpSchema, "body"), + kycOtp + ) + +userRouter.route("/kyc/otp/verify") + .post( + userAuth, + validatorMiddleware(verifyKycOtpSchema, "body"), + verifyKycOtp + ) + export default userRouter; diff --git a/src/strategies/google.strategy.js b/src/strategies/google.strategy.js index 46f1455..ea3870f 100644 --- a/src/strategies/google.strategy.js +++ b/src/strategies/google.strategy.js @@ -25,6 +25,7 @@ export function googleStrategy() { last_name: profile._json.given_name, first_name: profile._json.family_name, picture: profile._json.picture, + isVerified:true }; let user = await userModel.findOne({ email: googleuser.email }); diff --git a/src/validator_schema/createAccountShema.js b/src/validator_schema/createAccountShema.js index 98f6f86..9a16927 100644 --- a/src/validator_schema/createAccountShema.js +++ b/src/validator_schema/createAccountShema.js @@ -7,7 +7,6 @@ const createAccountSchema = Joi.object({ password: Joi.string().required(), // phoneNumber: Joi.string().pattern(/^\+?[1-9]\d{1,14}$/) // pattern for phone number validation // .required() - }) export default createAccountSchema \ No newline at end of file diff --git a/src/validator_schema/kycCredentialSchema.js b/src/validator_schema/kycCredentialSchema.js new file mode 100644 index 0000000..db8b609 --- /dev/null +++ b/src/validator_schema/kycCredentialSchema.js @@ -0,0 +1,15 @@ +import Joi from 'joi'; + +const IMAGE_EXTENSION_REGEX = /\.(gif|jpe?g|tiff?|png|webp|bmp)$/i; + +const kycCredentialSchema = Joi.object({ + dob: Joi.date(), + address: Joi.object({ + postalCode: Joi.string().required(), + city: Joi.string().required(), + address: Joi.string().required() + }), + verifiedSelfie: Joi.string().regex(IMAGE_EXTENSION_REGEX).required() +}); + +export default kycCredentialSchema; diff --git a/src/validator_schema/kycOtpSchema.js b/src/validator_schema/kycOtpSchema.js new file mode 100644 index 0000000..9d66b96 --- /dev/null +++ b/src/validator_schema/kycOtpSchema.js @@ -0,0 +1,8 @@ +import Joi from "joi"; + + +const kycOtpSchema = Joi.object({ + mobile: Joi.string().pattern(/^\+?[1-9]\d{1,14}$/).required() +}) + +export default kycOtpSchema \ No newline at end of file diff --git a/src/validator_schema/verifyAccountSchema.js b/src/validator_schema/verifyAccountSchema.js index 72b0d00..2b3a0f7 100644 --- a/src/validator_schema/verifyAccountSchema.js +++ b/src/validator_schema/verifyAccountSchema.js @@ -1,7 +1,7 @@ import Joi from "joi"; const verifyAccountSchema = Joi.object({ - otp: Joi.number().required(), + otp: Joi.number().min(1000).max(9999).required(), }) export default verifyAccountSchema \ No newline at end of file diff --git a/src/validator_schema/verifyKycOtpSchema.js b/src/validator_schema/verifyKycOtpSchema.js new file mode 100644 index 0000000..f0d2571 --- /dev/null +++ b/src/validator_schema/verifyKycOtpSchema.js @@ -0,0 +1,7 @@ +import Joi from "joi"; + +const verifyKycOtpSchema = Joi.object({ + otp: Joi.number().min(1000).max(9999).required(), +}) + +export default verifyKycOtpSchema \ No newline at end of file From 05c7db022f4f5b3dc31efbafeb666ee41fcf3502 Mon Sep 17 00:00:00 2001 From: JEDIBASIL Date: Tue, 14 Mar 2023 13:41:20 +0100 Subject: [PATCH 49/57] refactoring code --- src/app.js | 8 +- ...controller.js => newsletter.controller.js} | 0 src/controller/post.controller.js | 77 +++++++++++++++++ src/controller/postcontroller.js | 85 ------------------- src/controller/support.controller.js | 40 +++++++++ src/controller/supportcontroller.js | 40 --------- src/controller/user.controller.js | 2 +- ...newsletterroute.js => newsletter.route.js} | 2 +- src/routes/{postroute.js => post.route.js} | 2 +- .../{supportroute.js => support.route.js} | 6 +- src/routes/user.route.js | 13 +-- src/validator_schema/authSchema.js | 30 +++++++ src/validator_schema/createAccountShema.js | 12 --- src/validator_schema/inputShema.js | 8 -- src/validator_schema/kycOtpSchema.js | 8 -- .../{kycCredentialSchema.js => kycSchema.js} | 14 ++- src/validator_schema/loginSchema.js | 8 -- .../resendVerificationSchema.js | 7 -- src/validator_schema/resetPasswordSchema.js | 9 -- ...eProfileSchema.js => userProfileSchema.js} | 3 +- src/validator_schema/verifyAccountSchema.js | 7 -- src/validator_schema/verifyKycOtpSchema.js | 7 -- 22 files changed, 173 insertions(+), 215 deletions(-) rename src/controller/{newscontroller.js => newsletter.controller.js} (100%) create mode 100644 src/controller/post.controller.js delete mode 100644 src/controller/postcontroller.js create mode 100644 src/controller/support.controller.js delete mode 100644 src/controller/supportcontroller.js rename src/routes/{newsletterroute.js => newsletter.route.js} (93%) rename src/routes/{postroute.js => post.route.js} (95%) rename src/routes/{supportroute.js => support.route.js} (72%) create mode 100644 src/validator_schema/authSchema.js delete mode 100644 src/validator_schema/createAccountShema.js delete mode 100644 src/validator_schema/kycOtpSchema.js rename src/validator_schema/{kycCredentialSchema.js => kycSchema.js} (53%) delete mode 100644 src/validator_schema/loginSchema.js delete mode 100644 src/validator_schema/resendVerificationSchema.js delete mode 100644 src/validator_schema/resetPasswordSchema.js rename src/validator_schema/{updateProfileSchema.js => userProfileSchema.js} (59%) delete mode 100644 src/validator_schema/verifyAccountSchema.js delete mode 100644 src/validator_schema/verifyKycOtpSchema.js diff --git a/src/app.js b/src/app.js index 3cc4c20..3e0cc4f 100644 --- a/src/app.js +++ b/src/app.js @@ -5,9 +5,9 @@ import errorMiddleware from "./middleware/error.middleware" import { connectDB, PORT } from "./config" import passport from "passport"; import adminRouter from "./routes/admin.route" -import postRouter from "./routes/postroute"; -import newsRouter from "./routes/newsletterroute.js"; -import supportRouter from "./routes/supportroute.js"; +import postRouter from "./routes/post.route"; +// import newsRouter from "./routes/newsletter.route.js"; +import supportRouter from "./routes/support.route.js"; const app = express() @@ -24,7 +24,7 @@ app.use(passport.initialize()); app.use(`${path}/user`, userRouter) app.use(`${path}/admin`, adminRouter) app.use (`${path}/post`, postRouter); -app.use (`${path}`, newsRouter) +// app.use (`${path}/newsletter`, newsRouter) app.use (`${path}/support`, supportRouter) app.use(`${path}/faq`, faqRouter) diff --git a/src/controller/newscontroller.js b/src/controller/newsletter.controller.js similarity index 100% rename from src/controller/newscontroller.js rename to src/controller/newsletter.controller.js diff --git a/src/controller/post.controller.js b/src/controller/post.controller.js new file mode 100644 index 0000000..ee07a85 --- /dev/null +++ b/src/controller/post.controller.js @@ -0,0 +1,77 @@ +import Post from "../models/blogmodel.js"; +import userModel from "../models/user.model.js"; +import HttpResponse from "../response/HttpResponse.js"; +import HttpException from "../exceptions/HttpException.js"; + +export const writeBlog = async (req, res) => { + const { displayname, title, content } = req.body; + try { + const blogPoster = await userModel.findById(req.userAuth); + + await Post.create({ + displayname, + title, + content, + user: blogPoster._id + }); + + return res.status(201).send(new HttpResponse("success", "your blog has been posted successfully")) + + } catch (error) { + next(error) + } +} + +export const deleteBlog = async (req, res) => { + const blogOwner = await userModel.findById(req.userAuth); + const blog = await Post.findById(req.params.id); + + try { + if (blogOwner && blog) { + await Post.findOneAndDelete({ _id: req.params.id }); + return res.status(200).send(new HttpResponse("success", "the blog post has been deleted successfully")) + + } else { + throw new HttpException(404, "the blog post is not available"); + } + } catch (err) { + next(err) + } + + + +} + +export const updateBlog = async (req, res) => { + const blogOwner = await userModel.findById(req.userAuth); + const blog = await Post.findById(req.params.id); + const { displayname, title, content } = req.body; + + try { + if (blogOwner && blog) { + await Post.findOneAndUpdate({ _id: req.params.id }, { displayname, title, content }, { useFindAndModify: false }); + return res.status(200).send(new HttpResponse("success", "you have made changes to the blog post")) + } else { + throw new HttpException(404, "the blog post is not available"); + } + } catch (err) { + next(err) + } + +} + +export const getAllBlogs = async (req, res) => { + const blogOwner = await userModel.findById(req.userAuth); + + try { + + if (blogOwner) { + const allPosts = await Post.find({}); + return res.status(200).send(new HttpResponse("success", "all post", allPosts)) + } + else throw new HttpException(404, "you are not authorised"); + + } catch (err) { + next(err) + } +} \ No newline at end of file diff --git a/src/controller/postcontroller.js b/src/controller/postcontroller.js deleted file mode 100644 index dafff08..0000000 --- a/src/controller/postcontroller.js +++ /dev/null @@ -1,85 +0,0 @@ -import Post from "../models/blogmodel.js"; -import userModel from "../models/user.model.js"; - -export const writeBlog = async (req,res) => { - const {displayname, title, content} = req.body; - try{ - const blogPoster = await userModel.findById(req.userAuth); - - await Post.create({ - displayname, - title, - content, - user:blogPoster._id - }); - res.status(201).json({ - message: "your blog has been posted successfully" - }); - - - - - - }catch(error) { - res.json(error.message); - } -} - -export const deleteBlog = async (req,res) => { - const blogOwner = await userModel.findById(req.userAuth); - const blog = await Post.findById(req.params.id); - - try{ - if(blogOwner && blog) { - await Post.findOneAndDelete({_id:req.params.id}); - res.json({message:"the blog has been deleted successfully"}); - }else{ - res.status(404).json({message: "the blog is not available"}); - } - }catch(err) { - res.json(err.message); - } - - - -} - -export const updateBlog =async (req,res) => { - const blogOwner = await userModel.findById(req.userAuth); - const blog = await Post.findById(req.params.id); - const {displayname, title, content} = req.body; - - try{ - if(blogOwner && blog) { - await Post.findOneAndUpdate({_id:req.params.id},{displayname, title, content}, {useFindAndModify:false}); - res.json({message:"you have made changes to the blog"}); - }else{ - res.status(404).json({message:"the blog is not available"}); - } - }catch(err) { - res.json(err.message); - } - -} - -export const getAllBlogs = async (req,res) => { - const blogOwner = await userModel.findById(req.userAuth); - - try{ - if(blogOwner) { - const allPosts = await Post.find({}); - - res.status(200).json({ - status:"success", - data: allPosts - }); - } - else { - return res.json({ - message: "you are not authorised" - }); - } - }catch(err) { - res.json(err.message); - } -} \ No newline at end of file diff --git a/src/controller/support.controller.js b/src/controller/support.controller.js new file mode 100644 index 0000000..b67eda2 --- /dev/null +++ b/src/controller/support.controller.js @@ -0,0 +1,40 @@ +import Support from "../models/supportmodel.js"; +import HttpResponse from "../response/HttpResponse.js"; +import HttpException from "../exceptions/HttpException.js"; + + +export const userQuery = async (req, res, next) => { + try { + const { firstname, lastname, email, subject, message } = req.body; + await Support.create({ + firstname, + lastname, + email, + subject, + message + }); + return res.status(201).send(new HttpResponse("success", "your request have been submitted, we will get back to you in some time")) + } catch (err) { + next(err) + } +} + +export const clearTicket = async (req, res, next) => { + const { id } = req.params; + + try { + const support = await Support.findByIdAndUpdate( + id, + { ticketCleared: true }, + { new: true } + ); + + if (!support) throw new HttpException(404, "Support request not found"); + + return res.status(200).send(new HttpResponse("success", "ticket cleared", { support })) + + } catch (err) { + next(err) + } +} + diff --git a/src/controller/supportcontroller.js b/src/controller/supportcontroller.js deleted file mode 100644 index 0cf9dd4..0000000 --- a/src/controller/supportcontroller.js +++ /dev/null @@ -1,40 +0,0 @@ -import Support from "../models/supportmodel.js"; - - -export const userQuery = async (req,res) => { - try{ - const {firstname, lastname, email, subject, message} = req.body; - await Support.create({ - firstname, - lastname, - email, - subject, - message - }); - res.status(201).json({message:"your request have been submitted, we will get back to you in some time"}) - }catch (err) { - res.status(500).json(err.message); - } -} - -export const clearTicket = async (req, res) => { - const { id } = req.params; - - try { - const support = await Support.findByIdAndUpdate( - id, - { ticketCleared: true }, - { new: true } - ); - - if (!support) { - return res.status(404).json({ message: 'Support request not found' }); - } - - res.json(support); - - } catch (err) { - res.status(500).json(err.message); - } -} - diff --git a/src/controller/user.controller.js b/src/controller/user.controller.js index 1c6d77e..a1ba023 100644 --- a/src/controller/user.controller.js +++ b/src/controller/user.controller.js @@ -314,7 +314,7 @@ export async function kycCredentials(req, res, next) { const updatedUser = await userModel.findOneAndUpdate( { email }, { ...data, isKycVerified: true }, - { new: true } + { new: true } ); if (updatedUser) return res.status(200).send(new HttpResponse("success", "done with kyc successfully")); } catch (err) { diff --git a/src/routes/newsletterroute.js b/src/routes/newsletter.route.js similarity index 93% rename from src/routes/newsletterroute.js rename to src/routes/newsletter.route.js index c1e0862..4e49481 100644 --- a/src/routes/newsletterroute.js +++ b/src/routes/newsletter.route.js @@ -1,7 +1,7 @@ import {Router} from "express"; import validatorMiddleware from "../middleware/validator.middleware.js"; import { newsEmailSchema } from "../validator_schema/inputShema.js"; -import { subscribeNewsletter, unsubscribeNewsletter } from "../controller/newscontroller.js"; +import { subscribeNewsletter, unsubscribeNewsletter } from "../controller/newsletter.controller.js"; const newsRouter = Router(); //subscribe newsletter diff --git a/src/routes/postroute.js b/src/routes/post.route.js similarity index 95% rename from src/routes/postroute.js rename to src/routes/post.route.js index 441d2e1..a70d9cb 100644 --- a/src/routes/postroute.js +++ b/src/routes/post.route.js @@ -1,5 +1,5 @@ import express from "express"; -import { writeBlog, deleteBlog, updateBlog, getAllBlogs } from "../controller/postcontroller.js"; +import { writeBlog, deleteBlog, updateBlog, getAllBlogs } from "../controller/post.controller.js"; import validatorMiddleware from "../middleware/validator.middleware.js"; import {writeBlogSchema, updateBlogSchema} from "../validator_schema/inputShema.js"; import checkToken from "../auth/authentication.js"; diff --git a/src/routes/supportroute.js b/src/routes/support.route.js similarity index 72% rename from src/routes/supportroute.js rename to src/routes/support.route.js index e60d5a4..f5a8ee5 100644 --- a/src/routes/supportroute.js +++ b/src/routes/support.route.js @@ -1,11 +1,11 @@ -import {Router} from "express"; +import { Router } from "express"; import validatorMiddleware from "../middleware/validator.middleware.js"; import { supportSchema } from "../validator_schema/inputShema.js"; -import { userQuery, clearTicket } from "../controller/supportcontroller.js"; +import { userQuery, clearTicket } from "../controller/support.controller.js"; const supportRouter = Router(); //subscribe newsletter -supportRouter.route("/ticket").post(validatorMiddleware(supportSchema, "body"), userQuery ); +supportRouter.route("/ticket").post(validatorMiddleware(supportSchema, "body"), userQuery); //unsubcribe newsletter supportRouter.route("/:id").patch(clearTicket); diff --git a/src/routes/user.route.js b/src/routes/user.route.js index 1014028..53e364f 100644 --- a/src/routes/user.route.js +++ b/src/routes/user.route.js @@ -14,20 +14,15 @@ import { } from "../controller/user.controller"; import { Router } from "express"; import validatorMiddleware from "../middleware/validator.middleware"; -import createAccountSchema from "../validator_schema/createAccountShema"; -import verifyAccountSchema from "../validator_schema/verifyAccountSchema"; -import verifyKycOtpSchema from "../validator_schema/verifyKycOtpSchema"; -import loginSchema from "../validator_schema/loginSchema"; -import resetPasswordSchema from "../validator_schema/resetPasswordSchema"; -import resendVerificationSchema from "../validator_schema/resendVerificationSchema"; -import kycCredentialSchema from "../validator_schema/kycCredentialSchema"; +import { createAccountSchema, loginSchema, resendVerificationSchema, resetPasswordSchema, verifyAccountSchema } from "../validator_schema/authSchema" +import { kycCredentialSchema, kycOtpSchema, verifyKycOtpSchema } from "../validator_schema/kycSchema" + import passport from "passport" import jwt from "jsonwebtoken"; import { ACCESS_TOKEN, fileParser } from "../config"; -import updateProfileSchema from "../validator_schema/updateProfileSchema" +import { updateProfileSchema } from "../validator_schema/userProfileSchema" import { userAuth } from "../auth/user.auth"; import { googleStrategy } from "../strategies/google.strategy" -import kycOtpSchema from "../validator_schema/kycOtpSchema"; googleStrategy(); diff --git a/src/validator_schema/authSchema.js b/src/validator_schema/authSchema.js new file mode 100644 index 0000000..109c0ff --- /dev/null +++ b/src/validator_schema/authSchema.js @@ -0,0 +1,30 @@ +import Joi from "joi"; + + +export const createAccountSchema = Joi.object({ + username: Joi.string().required().min(3), + email: Joi.string().email().required(), + password: Joi.string().required(), + // phoneNumber: Joi.string().pattern(/^\+?[1-9]\d{1,14}$/) // pattern for phone number validation + // .required() +}) + +export const verifyAccountSchema = Joi.object({ + otp: Joi.number().min(1000).max(9999).required(), +}) + +export const loginSchema = Joi.object({ + email: Joi.string().email().required(), + password: Joi.string().required(), +}) + +export const resetPasswordSchema = Joi.object({ + token: Joi.string().required(), + confirmPassword: Joi.string().required(), + password: Joi.string().required(), +}) + +export const resendVerificationSchema = Joi.object({ + email: Joi.string().email().required(), +}) + diff --git a/src/validator_schema/createAccountShema.js b/src/validator_schema/createAccountShema.js deleted file mode 100644 index 9a16927..0000000 --- a/src/validator_schema/createAccountShema.js +++ /dev/null @@ -1,12 +0,0 @@ -import Joi from "joi"; - - -const createAccountSchema = Joi.object({ - username: Joi.string().required().min(3), - email: Joi.string().email().required(), - password: Joi.string().required(), - // phoneNumber: Joi.string().pattern(/^\+?[1-9]\d{1,14}$/) // pattern for phone number validation - // .required() -}) - -export default createAccountSchema \ No newline at end of file diff --git a/src/validator_schema/inputShema.js b/src/validator_schema/inputShema.js index 373d58b..1dd48b9 100644 --- a/src/validator_schema/inputShema.js +++ b/src/validator_schema/inputShema.js @@ -1,13 +1,5 @@ import Joi from "joi"; -export const createAccountSchema = Joi.object({ - firstname: Joi.string().required(), - lastname: Joi.string().required(), - age:Joi.number().required(), - email: Joi.string().email().required(), - password: Joi.string().required() -}); - export const writeBlogSchema = Joi.object({ displayname: Joi.string().required(), title: Joi.string().required(), diff --git a/src/validator_schema/kycOtpSchema.js b/src/validator_schema/kycOtpSchema.js deleted file mode 100644 index 9d66b96..0000000 --- a/src/validator_schema/kycOtpSchema.js +++ /dev/null @@ -1,8 +0,0 @@ -import Joi from "joi"; - - -const kycOtpSchema = Joi.object({ - mobile: Joi.string().pattern(/^\+?[1-9]\d{1,14}$/).required() -}) - -export default kycOtpSchema \ No newline at end of file diff --git a/src/validator_schema/kycCredentialSchema.js b/src/validator_schema/kycSchema.js similarity index 53% rename from src/validator_schema/kycCredentialSchema.js rename to src/validator_schema/kycSchema.js index db8b609..ff2b1e8 100644 --- a/src/validator_schema/kycCredentialSchema.js +++ b/src/validator_schema/kycSchema.js @@ -1,8 +1,14 @@ -import Joi from 'joi'; +import Joi from "joi"; + + +export const kycOtpSchema = Joi.object({ + mobile: Joi.string().pattern(/^\+?[1-9]\d{1,14}$/).required() +}) const IMAGE_EXTENSION_REGEX = /\.(gif|jpe?g|tiff?|png|webp|bmp)$/i; -const kycCredentialSchema = Joi.object({ + +export const kycCredentialSchema = Joi.object({ dob: Joi.date(), address: Joi.object({ postalCode: Joi.string().required(), @@ -12,4 +18,6 @@ const kycCredentialSchema = Joi.object({ verifiedSelfie: Joi.string().regex(IMAGE_EXTENSION_REGEX).required() }); -export default kycCredentialSchema; +export const verifyKycOtpSchema = Joi.object({ + otp: Joi.number().min(1000).max(9999).required(), +}) \ No newline at end of file diff --git a/src/validator_schema/loginSchema.js b/src/validator_schema/loginSchema.js deleted file mode 100644 index 57d67b2..0000000 --- a/src/validator_schema/loginSchema.js +++ /dev/null @@ -1,8 +0,0 @@ -import Joi from "joi"; - -const loginSchema = Joi.object({ - email: Joi.string().email().required(), - password: Joi.string().required(), -}) - -export default loginSchema \ No newline at end of file diff --git a/src/validator_schema/resendVerificationSchema.js b/src/validator_schema/resendVerificationSchema.js deleted file mode 100644 index 8e798c0..0000000 --- a/src/validator_schema/resendVerificationSchema.js +++ /dev/null @@ -1,7 +0,0 @@ -import Joi from "joi"; - -const resendVerificationSchema = Joi.object({ - email: Joi.string().email().required(), -}) - -export default resendVerificationSchema \ No newline at end of file diff --git a/src/validator_schema/resetPasswordSchema.js b/src/validator_schema/resetPasswordSchema.js deleted file mode 100644 index 4b7af73..0000000 --- a/src/validator_schema/resetPasswordSchema.js +++ /dev/null @@ -1,9 +0,0 @@ -import Joi from "joi"; - -const resetPasswordSchema = Joi.object({ - token: Joi.string().required(), - confirmPassword: Joi.string().required(), - password: Joi.string().required(), -}) - -export default resetPasswordSchema \ No newline at end of file diff --git a/src/validator_schema/updateProfileSchema.js b/src/validator_schema/userProfileSchema.js similarity index 59% rename from src/validator_schema/updateProfileSchema.js rename to src/validator_schema/userProfileSchema.js index 2ce5ac9..d46ee92 100644 --- a/src/validator_schema/updateProfileSchema.js +++ b/src/validator_schema/userProfileSchema.js @@ -1,8 +1,7 @@ import Joi from "joi"; -const updateProfileSchema = Joi.object({ +export const updateProfileSchema = Joi.object({ first_name: Joi.string().required(), last_name: Joi.string().required(), }) -export default updateProfileSchema \ No newline at end of file diff --git a/src/validator_schema/verifyAccountSchema.js b/src/validator_schema/verifyAccountSchema.js deleted file mode 100644 index 2b3a0f7..0000000 --- a/src/validator_schema/verifyAccountSchema.js +++ /dev/null @@ -1,7 +0,0 @@ -import Joi from "joi"; - -const verifyAccountSchema = Joi.object({ - otp: Joi.number().min(1000).max(9999).required(), -}) - -export default verifyAccountSchema \ No newline at end of file diff --git a/src/validator_schema/verifyKycOtpSchema.js b/src/validator_schema/verifyKycOtpSchema.js deleted file mode 100644 index f0d2571..0000000 --- a/src/validator_schema/verifyKycOtpSchema.js +++ /dev/null @@ -1,7 +0,0 @@ -import Joi from "joi"; - -const verifyKycOtpSchema = Joi.object({ - otp: Joi.number().min(1000).max(9999).required(), -}) - -export default verifyKycOtpSchema \ No newline at end of file From e8a3e8e4b3b8ea6d158042dd307bca7dec03575c Mon Sep 17 00:00:00 2001 From: JEDIBASIL Date: Wed, 15 Mar 2023 20:33:56 +0100 Subject: [PATCH 50/57] refactored admin code --- package.json | 2 +- src/app.js | 2 + src/controller/admin.controller.js | 195 +++++++----------- src/controller/user.controller.js | 2 +- src/routes/admin.route.js | 40 +++- src/routes/user.route.js | 2 +- src/validator_schema/adminAuthSchema.js | 27 +++ .../{authSchema.js => userAuthSchema.js} | 0 8 files changed, 145 insertions(+), 125 deletions(-) create mode 100644 src/validator_schema/adminAuthSchema.js rename src/validator_schema/{authSchema.js => userAuthSchema.js} (100%) diff --git a/package.json b/package.json index ca7f41c..cd2c593 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "dependencies": { "bcrypt": "^5.1.0", "cloudinary": "^1.35.0", - "cors": "^2.8.5", + "cors": "^2.8.5", "dotenv": "^16.0.3", "express": "^4.18.2", "geoip-lite": "^1.4.7", diff --git a/src/app.js b/src/app.js index 3e0cc4f..98e1b7f 100644 --- a/src/app.js +++ b/src/app.js @@ -8,6 +8,7 @@ import adminRouter from "./routes/admin.route" import postRouter from "./routes/post.route"; // import newsRouter from "./routes/newsletter.route.js"; import supportRouter from "./routes/support.route.js"; +import cors from "cors"; const app = express() @@ -16,6 +17,7 @@ const port = PORT || 8080 const path = "/api/v1" connectDB() +app.use(cors({origin: '*', credentials: true})); app.use(express.json()) app.use(passport.initialize()); diff --git a/src/controller/admin.controller.js b/src/controller/admin.controller.js index 84a091e..df74ba7 100644 --- a/src/controller/admin.controller.js +++ b/src/controller/admin.controller.js @@ -1,44 +1,45 @@ import HttpException from "../exceptions/HttpException" +import HttpResponse from "../response/HttpResponse"; import Admin from "../models/admin.model"; import { validateEmail } from "../utils/email-validator" import { validateField } from "../utils/input-validator"; import generateToken from "../utils/jwt/generate-token"; import { extractEmailFromToken } from "../utils/jwt/verify-token"; import { passwordValidator } from "../utils/password-validator"; +import { ACCESS_TOKEN } from "../config"; import sendMail from "../utils/sendMail"; +import jwt from "jsonwebtoken"; + export const registerEmail = async (req, res, next) => { - const {email} = req.body + const { email } = req.body try { - if (validateEmail(email)) { - const existingAdmin = await Admin.findOne({email}) - if (existingAdmin) { - throw new HttpException(400,"Email already exists, kindly login") - } - const token = generateToken(email) - const mailOption = { - from: process.env.MAIL_USER, - to: email, - subject: 'Admin account registration', - text: `Dear ${email}, kindly follow this link in order to continue with your registration process as an admin!\n + const existingAdmin = await Admin.findOne({ email }) + + if (existingAdmin) throw new HttpException(400, "Email already exists, kindly login") + + const token = jwt.sign({ email }, ACCESS_TOKEN, { + expiresIn: "1d", + }); + + const mailOption = { + to: email, + subject: 'Admin account registration', + text: `Dear ${email}, kindly follow this link in order to continue with your registration process as an admin!\n "http://localhost:8080/api/v1/admin/admin-registration-continuation?email=${email}&token=${token}"` - } - await sendMail(mailOption) - const admin = await Admin.create({ - email - }); - - return res.json({ - status: "success", - message: "A registration link has been successfully sent to your email, kindly continue your registration from there.", - token: token - }); } - else { - throw new HttpException(400,"Invalid Email!") - } - + await sendMail(mailOption) + const admin = await Admin.create({ + email + }); + + if (!admin) throw new HttpException(500, "an error occurred") + + return res + .status(200) + .send(new HttpResponse("success", "A registration link has been successfully sent to your email, kindly continue your registration from there.", { token })); + } catch (err) { next(err) } @@ -46,48 +47,33 @@ export const registerEmail = async (req, res, next) => { export const updateAdminRecord = async (req, res, next) => { try { - const {email, token} = req.query - - if (email && token) { - if (!extractEmailFromToken(token, email)) { - throw new HttpException(400,"Invalid or expired token") - } - const admin = await Admin.findOne({email}) - if (!admin) { - throw new HttpException(400,"Invalid email") - } - const {username, password} = req.body - - if (!validateField(username)) { - throw new HttpException(400,"Username must not be less than 6 characters long") - } - - const existingAdmin = await Admin.findOne({username}) - if (existingAdmin) { - throw new HttpException(400,"Username has already been taken") - } - - if (!passwordValidator(password)) { - throw new HttpException( - 400, - "Password must contain a number, a special character, an uppercase letter, and not less than 8 characters long" - ) - } - - admin.username = username - admin.password = password - admin.isAccepted = true - await admin.save(); - - return res.json({ - status: "success", - message: "Your account has been successfully created, you would be notified once your account is being activated.", - }); - } + const { email, token } = req.query + const { username, password } = req.body - else { - throw new HttpException(400,"Invalid request") - } + const decoded = jwt.decode(token, { complete: true }); + const payload = decoded.payload; + + const currentTime = Math.floor(Date.now() / 1000); + if (currentTime >= payload.exp) throw new HttpException(401, "duration expired"); + if (email !== payload.email) throw new HttpException(401, "invalid email"); + + const existingUsername = await Admin.findOne({ username }) + const existingEmail = await Admin.findOne({ email }) + + if (existingEmail.isAccepted) throw new HttpException(409, "admitted already") + if (existingUsername) throw new HttpException(409, "Username has already been taken") + + const admin = await Admin.findOne({ email }) + if (!admin) throw new HttpException(404, "email not registered") + + admin.username = username + admin.password = password + admin.isAccepted = true + await admin.save(); + + return res + .status(200) + .send(new HttpResponse("success", "Your account has been successfully created, you would be notified once your account is being activated.")); } catch (err) { @@ -98,25 +84,18 @@ export const updateAdminRecord = async (req, res, next) => { export const activateAdmin = async (req, res, next) => { try { const id = req.params.id - if (id) { - const admin = await Admin.findOne({_id:id}) - if (!admin) { - throw new HttpException(400,"No admin details found") - } - if (admin.isVerified) { - throw new HttpException(400,"Admin has already been verified") - } - admin.isVerified = true - await admin.save() - return res.json({ - status: "success", - message: `${admin.username} account has now been activated.`, - }); + const admin = await Admin.findOne({ _id: id }) + if (!admin) throw new HttpException(400, "No admin details found") + + if (admin.isVerified) throw new HttpException(400, "Admin has already been verified") + admin.isVerified = true + + await admin.save() + + return res + .status(200) + .send(new HttpResponse("success", `${admin.username} account has now been activated.`)); - } - else { - throw new HttpException(400,"Invalid request") - } } catch (err) { next(err) @@ -125,37 +104,23 @@ export const activateAdmin = async (req, res, next) => { export const adminLogin = async (req, res, next) => { try { - const {email, password} = req.body - - if (email && password && validateEmail(email)) { - const admin = await Admin.findOne({email}) - - if (!admin) { - throw new HttpException(400,"Incorrect Email or Password") - } - - const passwordResult = await admin.isPasswordMatch(password) - console.log("Password Result: ", passwordResult); - if (!passwordResult) { - console.log("Inside here"); - throw new HttpException(400,"Incorrect Email or Password") - } - if(!admin.isVerified) { - throw new HttpException(400,"Unverified account") - } - return res.json({ - status: "success", - message: `Dear ${admin.username}, welcome to the admin dashboard`, - }); - - } - else { - throw new HttpException(400,"Invalid Email!") - } - + const { email, password } = req.body + + const admin = await Admin.findOne({ email }) + + if (!admin) throw new HttpException(404, "Incorrect Email or Password") + + if (!(await admin.isPasswordMatch(password))) throw new HttpException(404, "Incorrect Email or Password") + + if (!admin.isVerified) throw new HttpException(403, "Unverified account") + + return res.json({ + status: "success", + message: `Dear ${admin.username}, welcome to the admin dashboard`, + }); + } catch (err) { next(err) } } - diff --git a/src/controller/user.controller.js b/src/controller/user.controller.js index a1ba023..21ca56e 100644 --- a/src/controller/user.controller.js +++ b/src/controller/user.controller.js @@ -1,4 +1,4 @@ -import HttpException from "../exceptions/HttpException"; +import HttpException from "../exceptions/HttpException"; import userModel from "../models/user.model"; import jwt from "jsonwebtoken"; import emailTemplateReader from "../utils/emailTemplateReader"; diff --git a/src/routes/admin.route.js b/src/routes/admin.route.js index 91ed25c..7e074e7 100644 --- a/src/routes/admin.route.js +++ b/src/routes/admin.route.js @@ -1,12 +1,38 @@ import express from "express"; -import { activateAdmin, adminLogin, registerEmail, updateAdminRecord } from "../controller/admin.controller"; - +import { + activateAdmin, + adminLogin, + registerEmail, + updateAdminRecord, +} from "../controller/admin.controller"; +import validatorMiddleware from "../middleware/validator.middleware"; +import { + registerEmailSchema, + adminCredentialsSchema, + verifyAdminSchema, + loginSchema, + activateAdminSchema +} from "../validator_schema/adminAuthSchema"; const adminRouter = express.Router(); -adminRouter.post("/admin-register-email", registerEmail) -adminRouter.put("/admin-registration-continuation", updateAdminRecord) -adminRouter.patch("/admin-activation/:id", activateAdmin) -adminRouter.post("/login", adminLogin) +adminRouter + .route("/admin-register-email") + .post(validatorMiddleware(registerEmailSchema, "body"), registerEmail); + +adminRouter + .route("/admin-registration-continuation") + .put( + validatorMiddleware(verifyAdminSchema, "query"), + validatorMiddleware(adminCredentialsSchema, "body"), + updateAdminRecord + ); + +adminRouter + .route("/admin-activation/:id") + .patch(validatorMiddleware(activateAdminSchema, "params"), activateAdmin); +adminRouter + .route("/login") + .post(validatorMiddleware(loginSchema, "body"), adminLogin); -export default adminRouter \ No newline at end of file +export default adminRouter; diff --git a/src/routes/user.route.js b/src/routes/user.route.js index 53e364f..fd35876 100644 --- a/src/routes/user.route.js +++ b/src/routes/user.route.js @@ -14,7 +14,7 @@ import { } from "../controller/user.controller"; import { Router } from "express"; import validatorMiddleware from "../middleware/validator.middleware"; -import { createAccountSchema, loginSchema, resendVerificationSchema, resetPasswordSchema, verifyAccountSchema } from "../validator_schema/authSchema" +import { createAccountSchema, loginSchema, resendVerificationSchema, resetPasswordSchema, verifyAccountSchema } from "../validator_schema/userAuthSchema" import { kycCredentialSchema, kycOtpSchema, verifyKycOtpSchema } from "../validator_schema/kycSchema" import passport from "passport" diff --git a/src/validator_schema/adminAuthSchema.js b/src/validator_schema/adminAuthSchema.js new file mode 100644 index 0000000..d9f3e11 --- /dev/null +++ b/src/validator_schema/adminAuthSchema.js @@ -0,0 +1,27 @@ +import Joi from "joi"; + +export const registerEmailSchema = Joi.object({ + email: Joi.string().email().required(), +}) + +const PASSWORD_REGEX = /^(?=.*\d)(?=.*[!@#$%^&*])(?=.*[a-z])(?=.*[A-Z]).{8,}$/ + + +export const verifyAdminSchema = Joi.object({ + email: Joi.string().email().required(), + token: Joi.string().required(), +}) + +export const adminCredentialsSchema = Joi.object({ + password: Joi.string().regex(PASSWORD_REGEX).required(), + username: Joi.string().min(6).required(), +}) + +export const loginSchema = Joi.object({ + email: Joi.string().email().required(), + password: Joi.string().required(), +}) + +export const activateAdminSchema = Joi.object({ + id: Joi.string().required(), +}) \ No newline at end of file diff --git a/src/validator_schema/authSchema.js b/src/validator_schema/userAuthSchema.js similarity index 100% rename from src/validator_schema/authSchema.js rename to src/validator_schema/userAuthSchema.js From 57366d2fb52bd4b35a6d649a92c16b2371471279 Mon Sep 17 00:00:00 2001 From: JEDIBASIL Date: Wed, 15 Mar 2023 20:54:16 +0100 Subject: [PATCH 51/57] still refactoring --- src/controller/admin.controller.js | 95 ++++++++++++++++++++++++++---- 1 file changed, 85 insertions(+), 10 deletions(-) diff --git a/src/controller/admin.controller.js b/src/controller/admin.controller.js index df74ba7..1d62f06 100644 --- a/src/controller/admin.controller.js +++ b/src/controller/admin.controller.js @@ -1,11 +1,6 @@ import HttpException from "../exceptions/HttpException" import HttpResponse from "../response/HttpResponse"; import Admin from "../models/admin.model"; -import { validateEmail } from "../utils/email-validator" -import { validateField } from "../utils/input-validator"; -import generateToken from "../utils/jwt/generate-token"; -import { extractEmailFromToken } from "../utils/jwt/verify-token"; -import { passwordValidator } from "../utils/password-validator"; import { ACCESS_TOKEN } from "../config"; import sendMail from "../utils/sendMail"; import jwt from "jsonwebtoken"; @@ -67,7 +62,7 @@ export const updateAdminRecord = async (req, res, next) => { if (!admin) throw new HttpException(404, "email not registered") admin.username = username - admin.password = password + admin.password = password admin.isAccepted = true await admin.save(); @@ -114,13 +109,93 @@ export const adminLogin = async (req, res, next) => { if (!admin.isVerified) throw new HttpException(403, "Unverified account") - return res.json({ - status: "success", - message: `Dear ${admin.username}, welcome to the admin dashboard`, - }); + return res + .status(200) + .send(new HttpResponse("success", `Dear ${admin.username}, welcome to the admin dashboard`)); } catch (err) { next(err) } } +export const adminProfileUpdate = async (req, res, next) => { + try { + const { id } = req.params + if (!id) { + throw new HttpException(400, "Invalid request") + } + else { + const { firstname, lastname } = req.body + const admin = await Admin.findOneAndUpdate( + { _id: id }, + { firstname: firstname, lastname: lastname }, + { new: true, runValidators: true } + ) + + if (!admin) { + throw new HttpException(400, "Admin not found") + } + + return res + .status(200) + .send(new HttpResponse("success", `Dear ${admin.firstname}, your account has now been updated.`)); + + } + } + catch (err) { + next(err) + } +} + +export const adminsController = async (req, res, next) => { + try { + const admins = await Admin.find({}) + + return res + .status(200) + .send(new HttpResponse("success", `fetched all admin successfully`, admins)); + + } + catch (error) { + next(error) + } +}; + +export const deleteAdminController = async (req, res, next) => { + const id = req.params.id; + try { + if (id) { + const admin = await Admin.findOneAndDelete({ _id: id }) + if (!admin) { + throw new HttpException(400, "Admin not found") + } + res.json({ + status: "success", + message: "Account has been deleted successfully", + }); + } + else { + throw new HttpException(400, "Invalid request") + } + } + catch (error) { + next(error) + } +}; + +export const emailUsersController = async (req, res, next) => { + const { subject, message, isVerified } = req.body + try { + let users + if (isVerified === true) { + users = await userModel.find({ isVerified: true }) + } + else { + users = await userModel.find({}) + } + await autoEmail(users, subject, message, res) + } + catch (error) { + next(error) + } +}; \ No newline at end of file From 7b1f5108d90ab600dcf434e8b6c292a48bbbc5c2 Mon Sep 17 00:00:00 2001 From: JEDIBASIL Date: Wed, 22 Mar 2023 20:34:59 +0100 Subject: [PATCH 52/57] refactored the code --- src/controller/admin.controller.js | 2 +- src/controller/user.controller.js | 1 - src/routes/faq.route.js | 19 +++++++++++++++++-- src/validator_schema/faqSchema.js | 11 +++++++++++ src/validator_schema/inputShema.js | 1 - 5 files changed, 29 insertions(+), 5 deletions(-) create mode 100644 src/validator_schema/faqSchema.js diff --git a/src/controller/admin.controller.js b/src/controller/admin.controller.js index 595f189..a363395 100644 --- a/src/controller/admin.controller.js +++ b/src/controller/admin.controller.js @@ -20,7 +20,7 @@ export const registerEmail = async (req, res, next) => { }); const mailOption = { - to: email, + to: email, subject: 'Admin account registration', text: `Dear ${email}, kindly follow this link in order to continue with your registration process as an admin!\n "http://localhost:8080/api/v1/admin/admin-registration-continuation?email=${email}&token=${token}"` diff --git a/src/controller/user.controller.js b/src/controller/user.controller.js index 21ca56e..eebc01f 100644 --- a/src/controller/user.controller.js +++ b/src/controller/user.controller.js @@ -7,7 +7,6 @@ import { ACCESS_TOKEN, WEB_URL, IPINFO_TOKEN, nexmo } from "../config"; import sendMail from "../utils/sendMail"; import randomstring from "randomstring"; import otpModel from "../models/otp.model"; -import geoip from "node-geoip" import logger from "../utils/logger"; import ipinfo from "ipinfo" import requestIp from "request-ip" diff --git a/src/routes/faq.route.js b/src/routes/faq.route.js index cd8c9ee..3b72b58 100644 --- a/src/routes/faq.route.js +++ b/src/routes/faq.route.js @@ -1,9 +1,24 @@ import { Router } from "express"; import { createFaqs, deleteFaq, getAllFaqs, updateFaq } from "../controller/faq.controller" +import { faqIdSchema, createSchema } from "../validator_schema/faqSchema"; +import validatorMiddleware from "../middleware/validator.middleware"; const faqRouter = Router({ mergeParams: true }) +createSchema +faqRouter.route("/") + .get(getAllFaqs) + .post( + validatorMiddleware(createSchema, "body"), + createFaqs + ) -faqRouter.route("/").get(getAllFaqs).post(createFaqs) -faqRouter.route("/:id").put(updateFaq).delete(deleteFaq) +faqRouter.route("/:id") + .put( + validatorMiddleware(faqIdSchema, "params"), + updateFaq + ).delete( + validatorMiddleware(faqIdSchema, "params"), + deleteFaq + ) export default faqRouter \ No newline at end of file diff --git a/src/validator_schema/faqSchema.js b/src/validator_schema/faqSchema.js new file mode 100644 index 0000000..43ae8d2 --- /dev/null +++ b/src/validator_schema/faqSchema.js @@ -0,0 +1,11 @@ +import Joi from "joi"; + +export const faqIdSchema = Joi.object({ + id: Joi.string().required(), +}) + +export const createSchema = Joi.object({ + question: Joi.string().required(), + answer: Joi.string().required(), + addedBy: Joi.string(), +}) \ No newline at end of file diff --git a/src/validator_schema/inputShema.js b/src/validator_schema/inputShema.js index 1dd48b9..2bd840f 100644 --- a/src/validator_schema/inputShema.js +++ b/src/validator_schema/inputShema.js @@ -23,5 +23,4 @@ export const supportSchema = Joi.object({ email: Joi.string().email().required(), subject: Joi.string().required(), message:Joi.string().required() - }); From ceae656f18faf6cb0f9d4e29cfdfe66592bbbec1 Mon Sep 17 00:00:00 2001 From: JEDIBASIL Date: Tue, 28 Mar 2023 20:44:04 +0100 Subject: [PATCH 53/57] refactoring code --- src/config/index.js | 10 ++- src/controller/post.controller.js | 4 +- src/controller/user.controller.js | 19 ++-- src/models/blogmodel.js | 17 ++-- src/models/faq.model.js | 2 +- src/routes/user.route.js | 115 +++++++++++++------------ src/strategies/google.strategy.js | 6 +- src/validator_schema/kycSchema.js | 2 +- src/validator_schema/userAuthSchema.js | 2 +- 9 files changed, 94 insertions(+), 83 deletions(-) diff --git a/src/config/index.js b/src/config/index.js index 7437e40..2c3a4e4 100644 --- a/src/config/index.js +++ b/src/config/index.js @@ -46,9 +46,9 @@ const parser = multer({ storage: storage }); const nexmo = new Nexmo({ apiKey: process.env.NEXMO_KEY, apiSecret: process.env.NEXMO_SECRET, - }); - -export {cloudinary, parser as fileParser, nexmo } +}); + +export { cloudinary, parser as fileParser, nexmo } export const { MONGODB_URI, @@ -60,5 +60,7 @@ export const { GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, IPINFO_TOKEN, - PORT + PORT, + CALLBACK_URL, + FRONTEND_URL } = process.env; \ No newline at end of file diff --git a/src/controller/post.controller.js b/src/controller/post.controller.js index ee07a85..78d48d5 100644 --- a/src/controller/post.controller.js +++ b/src/controller/post.controller.js @@ -9,10 +9,10 @@ export const writeBlog = async (req, res) => { const blogPoster = await userModel.findById(req.userAuth); await Post.create({ - displayname, + displayname, title, content, - user: blogPoster._id + admin: blogPoster._id }); return res.status(201).send(new HttpResponse("success", "your blog has been posted successfully")) diff --git a/src/controller/user.controller.js b/src/controller/user.controller.js index eebc01f..2857da8 100644 --- a/src/controller/user.controller.js +++ b/src/controller/user.controller.js @@ -1,4 +1,4 @@ -import HttpException from "../exceptions/HttpException"; +import HttpException from "../exceptions/HttpException"; import userModel from "../models/user.model"; import jwt from "jsonwebtoken"; import emailTemplateReader from "../utils/emailTemplateReader"; @@ -24,9 +24,13 @@ export async function createAccount(req, res, next) { await sendVerificationMail(data.email); + const accessToken = jwt.sign({ value: newAccount._id }, ACCESS_TOKEN, { + expiresIn: "30d", + }); + return res .status(200) - .send(new HttpResponse("success", "account created successfully")); + .send(new HttpResponse("success", "account created successfully", { accessToken, kycVerified: false })); } catch (err) { next(err); } @@ -46,7 +50,7 @@ export async function loginAccount(req, res, next) { throw new HttpException(404, "incorrect username or email, and password"); if (!findByEmail.isVerified) - throw new HttpException(403, "account is not verified"); + throw new HttpException(403, "email is not verified"); if (findByEmail.isBlocked) throw new HttpException(403, "account is has been blocked"); @@ -65,7 +69,7 @@ export async function loginAccount(req, res, next) { return res .status(200) .send( - new HttpResponse("success", "account authenticated", { accessToken }) + new HttpResponse("success", "account authenticated", { accessToken, kycVerified: findByEmail.isKycVerified }) ); } catch (err) { next(err); @@ -86,6 +90,7 @@ export async function verify(req, res, next) { if (currentTime >= exp) throw new HttpException(401, "otp has expired"); const user = await userModel.findOne({ email: value }); + if (user.isVerified) throw new HttpException(400, "account already verified"); user.isVerified = true; await user.save(); @@ -238,6 +243,9 @@ export async function uploadImg(req, res, next) { if (!req.file) throw new HttpException(400, "field is required") return res.status(200).send(new HttpResponse("success", "image uploaded", { url: req.file.path })); } catch (err) { + console.log(err) + console.log(req.file) + next(err) } } @@ -297,7 +305,6 @@ export async function verifyKycOtp(req, res, next) { user.phoneNumber = value; await user.save(); - return res.status(200).send(new HttpResponse("success", "otp verified successfully")); } catch (err) { next(err) @@ -313,7 +320,7 @@ export async function kycCredentials(req, res, next) { const updatedUser = await userModel.findOneAndUpdate( { email }, { ...data, isKycVerified: true }, - { new: true } + { new: true } ); if (updatedUser) return res.status(200).send(new HttpResponse("success", "done with kyc successfully")); } catch (err) { diff --git a/src/models/blogmodel.js b/src/models/blogmodel.js index 06e0929..4acb0cb 100644 --- a/src/models/blogmodel.js +++ b/src/models/blogmodel.js @@ -14,19 +14,14 @@ const blogSchema = new mongoose.Schema({ type:String, required:[true,"write content"] }, - postviews: [{ + admin: { type:mongoose.Schema.Types.ObjectId, - ref: "User" - }], - - user: [{ - type:mongoose.Schema.Types.ObjectId, - ref: "User" - }], - photo: [{ + ref: "Admin" + }, + photo: { type:String, - - }] + required:true, + } }, { timestamps:true, toJSON:{virtuals:true} diff --git a/src/models/faq.model.js b/src/models/faq.model.js index 34affe7..af0d2ae 100644 --- a/src/models/faq.model.js +++ b/src/models/faq.model.js @@ -1,4 +1,5 @@ import { Schema, model } from "mongoose" +import moment from "moment"; const faqSchema = new Schema({ question: { @@ -12,7 +13,6 @@ const faqSchema = new Schema({ addedBy: { type: String, - required: true }, createdAt: { diff --git a/src/routes/user.route.js b/src/routes/user.route.js index fd35876..2cbeecd 100644 --- a/src/routes/user.route.js +++ b/src/routes/user.route.js @@ -10,51 +10,71 @@ import { kycOtp, verifyKycOtp, getProfile, - kycCredentials + kycCredentials, } from "../controller/user.controller"; import { Router } from "express"; import validatorMiddleware from "../middleware/validator.middleware"; -import { createAccountSchema, loginSchema, resendVerificationSchema, resetPasswordSchema, verifyAccountSchema } from "../validator_schema/userAuthSchema" -import { kycCredentialSchema, kycOtpSchema, verifyKycOtpSchema } from "../validator_schema/kycSchema" +import { + createAccountSchema, + loginSchema, + resendVerificationSchema, + resetPasswordSchema, + verifyAccountSchema, +} from "../validator_schema/userAuthSchema"; +import { + kycCredentialSchema, + kycOtpSchema, + verifyKycOtpSchema, +} from "../validator_schema/kycSchema"; -import passport from "passport" +import passport from "passport"; import jwt from "jsonwebtoken"; -import { ACCESS_TOKEN, fileParser } from "../config"; -import { updateProfileSchema } from "../validator_schema/userProfileSchema" +import { ACCESS_TOKEN, fileParser, FRONTEND_URL } from "../config"; +import { updateProfileSchema } from "../validator_schema/userProfileSchema"; import { userAuth } from "../auth/user.auth"; -import { googleStrategy } from "../strategies/google.strategy" - +import { googleStrategy } from "../strategies/google.strategy"; googleStrategy(); const userRouter = Router({ mergeParams: true }); - userRouter .route("/create") - .post( - validatorMiddleware(createAccountSchema, "body"), - createAccount - ); + .post(validatorMiddleware(createAccountSchema, "body"), createAccount); userRouter .route("/auth/google") - .get(passport.authenticate('google', { scope: ['profile', 'email', 'https://www.googleapis.com/auth/user.phonenumbers.read'], session: false })); + .get( + passport.authenticate("google", { + scope: [ + "profile", + "email", + "https://www.googleapis.com/auth/user.phonenumbers.read", + ], + session: false, + }) + ); -userRouter.route('/auth/google/callback') - .get(passport.authenticate('google', { failureRedirect: '/login', session: false }), (req, res) => { - const accessToken = jwt.sign({ value: req.user._id }, ACCESS_TOKEN, { - expiresIn: "30d", - }); - res.redirect(`http://127.0.0.1:1420/?token=${accessToken}`); - }); +userRouter + .route("/auth/google/callback") + .get( + passport.authenticate("google", { + failureRedirect: "/login", + session: false, + }), + (req, res) => { + const accessToken = jwt.sign({ value: req.user._id }, ACCESS_TOKEN, { + expiresIn: "30d", + }); + res.redirect( + `http://localhost:5173/app/?token=${accessToken}?kycVerified=${req.user.isKycVerified}` + ); + } + ); userRouter .route("/verify") - .post( - validatorMiddleware(verifyAccountSchema, "body"), - verify - ); + .post(validatorMiddleware(verifyAccountSchema, "body"), verify); userRouter .route("/verify/resend") @@ -65,9 +85,7 @@ userRouter userRouter .route("/login") - .post(validatorMiddleware(loginSchema, "body"), - loginAccount - ); + .post(validatorMiddleware(loginSchema, "body"), loginAccount); userRouter .route("/verify/reset-password") @@ -78,49 +96,38 @@ userRouter userRouter .route("/reset-password") - .post( - validatorMiddleware(resetPasswordSchema, "body"), - resetPassword - ); + .post(validatorMiddleware(resetPasswordSchema, "body"), resetPassword); -userRouter.route("/profile") - .get( - userAuth, - getProfile - ) +userRouter.route("/profile").get(userAuth, getProfile); -userRouter.route("/profile") +userRouter + .route("/profile") .put( userAuth, validatorMiddleware(updateProfileSchema, "body"), updateProfile - ) + ); -userRouter.route("/kyc") +userRouter + .route("/kyc") .post( userAuth, validatorMiddleware(kycCredentialSchema, "body"), kycCredentials - ) + ); -userRouter.route("/kyc/photo") - .post( - fileParser.single("photo"), - uploadImg - ) +userRouter.route("/kyc/photo").post(fileParser.single("photo"), uploadImg); -userRouter.route("/kyc/otp") - .post( - userAuth, - validatorMiddleware(kycOtpSchema, "body"), - kycOtp - ) +userRouter + .route("/kyc/otp") + .post(userAuth, validatorMiddleware(kycOtpSchema, "body"), kycOtp); -userRouter.route("/kyc/otp/verify") +userRouter + .route("/kyc/otp/verify") .post( userAuth, validatorMiddleware(verifyKycOtpSchema, "body"), verifyKycOtp - ) + ); export default userRouter; diff --git a/src/strategies/google.strategy.js b/src/strategies/google.strategy.js index ea3870f..fd57202 100644 --- a/src/strategies/google.strategy.js +++ b/src/strategies/google.strategy.js @@ -1,6 +1,6 @@ import passport from "passport"; import { Strategy as GoogleStrategy } from "passport-google-oauth20"; -import { GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET } from "../config"; +import { GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, CALLBACK_URL } from "../config"; import userModel from "../models/user.model"; import ipinfo from "ipinfo" import { IPINFO_TOKEN } from "../config"; @@ -13,7 +13,7 @@ export function googleStrategy() { { clientID: GOOGLE_CLIENT_ID, clientSecret: GOOGLE_CLIENT_SECRET, - callbackURL: "https://stakepro.onrender.com/api/v1/user/auth/google/callback", + callbackURL: `${CALLBACK_URL}/api/v1/user/auth/google/callback`, passReqToCallback: true, }, async (req, _accessToken, _refreshToken, profile, done) => { @@ -25,7 +25,7 @@ export function googleStrategy() { last_name: profile._json.given_name, first_name: profile._json.family_name, picture: profile._json.picture, - isVerified:true + isVerified: true }; let user = await userModel.findOne({ email: googleuser.email }); diff --git a/src/validator_schema/kycSchema.js b/src/validator_schema/kycSchema.js index ff2b1e8..0e59916 100644 --- a/src/validator_schema/kycSchema.js +++ b/src/validator_schema/kycSchema.js @@ -19,5 +19,5 @@ export const kycCredentialSchema = Joi.object({ }); export const verifyKycOtpSchema = Joi.object({ - otp: Joi.number().min(1000).max(9999).required(), + otp: Joi.string().min(4).max(4).required(), }) \ No newline at end of file diff --git a/src/validator_schema/userAuthSchema.js b/src/validator_schema/userAuthSchema.js index 109c0ff..170696a 100644 --- a/src/validator_schema/userAuthSchema.js +++ b/src/validator_schema/userAuthSchema.js @@ -10,7 +10,7 @@ export const createAccountSchema = Joi.object({ }) export const verifyAccountSchema = Joi.object({ - otp: Joi.number().min(1000).max(9999).required(), + otp: Joi.string().min(4).max(4) }) export const loginSchema = Joi.object({ From 158a71b21bc285b6b2dd615cc7de69e1b4c00b32 Mon Sep 17 00:00:00 2001 From: hardeylarning Date: Wed, 29 Mar 2023 11:43:39 +0100 Subject: [PATCH 54/57] added jwt directory --- src/controller/admin.controller.js | 2 +- src/utils/email-template.js | 46 ++++++++++++++++++++++++++++++ src/utils/jwt/generate-token.js | 4 +-- src/utils/jwt/get-token.js | 6 ++++ src/utils/jwt/verify-token.js | 15 ++++++---- 5 files changed, 64 insertions(+), 9 deletions(-) create mode 100644 src/utils/email-template.js create mode 100644 src/utils/jwt/get-token.js diff --git a/src/controller/admin.controller.js b/src/controller/admin.controller.js index a363395..8f3e6ce 100644 --- a/src/controller/admin.controller.js +++ b/src/controller/admin.controller.js @@ -45,7 +45,7 @@ export const registerEmail = async (req, res, next) => { export const updateAdminRecord = async (req, res, next) => { try { const { email, token } = req.query - const { username, password } = req.body + const { username, password } = req.body const decoded = jwt.decode(token, { complete: true }); const payload = decoded.payload; diff --git a/src/utils/email-template.js b/src/utils/email-template.js new file mode 100644 index 0000000..f002a09 --- /dev/null +++ b/src/utils/email-template.js @@ -0,0 +1,46 @@ + +export function htmlTemplate(baseUrl, name, email, token) { + return ` + + + + + + Email template + + + +
+ Dear ${name}, +
+

+ kindly follow this link in order to continue with your registration process as an admin! +

+
+ Activate your account +
+ + + ` +} \ No newline at end of file diff --git a/src/utils/jwt/generate-token.js b/src/utils/jwt/generate-token.js index 96c3fe7..8c4db67 100644 --- a/src/utils/jwt/generate-token.js +++ b/src/utils/jwt/generate-token.js @@ -1,8 +1,8 @@ import jwt from "jsonwebtoken" -const generateToken = (email) => { +const generateToken = (email, role) => { return jwt.sign( - {email}, process.env.JWT_KEY, + {email, role}, process.env.ACCESS_TOKEN, {expiresIn: process.env.JWT_EXPIRATION_TIME} ) } diff --git a/src/utils/jwt/get-token.js b/src/utils/jwt/get-token.js new file mode 100644 index 0000000..4ac5e8e --- /dev/null +++ b/src/utils/jwt/get-token.js @@ -0,0 +1,6 @@ +export const getTokenFromHeader = req => { + const headers = req.headers; + const token = headers['authorization'] + if(token) return token.split(" ")[1] + return false +} \ No newline at end of file diff --git a/src/utils/jwt/verify-token.js b/src/utils/jwt/verify-token.js index 685ea7c..96a71c4 100644 --- a/src/utils/jwt/verify-token.js +++ b/src/utils/jwt/verify-token.js @@ -1,15 +1,18 @@ import jwt from "jsonwebtoken" export const verifyToken = token => { - return jwt.verify(token, process.env.JWT_KEY, (error, decoded) => { + return jwt.verify(token, process.env.ACCESS_TOKEN, (error, decoded) => { if (error) return false return decoded }) } -export const extractEmailFromToken = (token, email) => { - const extractedEmail = verifyToken(token); - console.log("Extracted: ", extractedEmail); - console.log("Extracted Email: ", extractedEmail.email); - return extractedEmail.email == email +export const extractEmailAndRoleFromToken = (token, email, role) => { + const extractedSubject = verifyToken(token); + return extractedSubject.email == email && extractedSubject.role == role +} + +export const extractRoleFromToken = (token, role) => { + const extractedSubject = verifyToken(token); + return extractedSubject.role == role } \ No newline at end of file From 59a83255d923010fbb672c132802f821ca00d3f9 Mon Sep 17 00:00:00 2001 From: hardeylarning Date: Wed, 29 Mar 2023 11:48:37 +0100 Subject: [PATCH 55/57] updated controller --- src/controller/admin.controller.js | 270 +++++++++++-------- src/middleware/auth/admin-has-access.js | 17 ++ src/middleware/auth/is-admin-logged-in.js | 17 ++ src/middleware/auth/superadmin-has-access.js | 17 ++ src/models/admin.model.js | 9 +- 5 files changed, 218 insertions(+), 112 deletions(-) create mode 100644 src/middleware/auth/admin-has-access.js create mode 100644 src/middleware/auth/is-admin-logged-in.js create mode 100644 src/middleware/auth/superadmin-has-access.js diff --git a/src/controller/admin.controller.js b/src/controller/admin.controller.js index 8f3e6ce..f00393a 100644 --- a/src/controller/admin.controller.js +++ b/src/controller/admin.controller.js @@ -1,42 +1,47 @@ import HttpException from "../exceptions/HttpException" -import HttpResponse from "../response/HttpResponse"; import Admin from "../models/admin.model"; -import { ACCESS_TOKEN } from "../config"; -import userModel from "../models/user.model"; +// import userModel from "../models/user.model"; import { autoEmail } from "../service/user-email-service"; +import { htmlTemplate } from "../utils/email-template"; +import { validateEmail } from "../utils/email-validator" +import { validateField } from "../utils/input-validator"; +import generateToken from "../utils/jwt/generate-token"; +import { extractEmailAndRoleFromToken} from "../utils/jwt/verify-token"; +import { passwordValidator } from "../utils/password-validator"; import sendMail from "../utils/sendMail"; -import jwt from "jsonwebtoken"; export const registerEmail = async (req, res, next) => { - const { email } = req.body + const {email} = req.body try { - const existingAdmin = await Admin.findOne({ email }) - - if (existingAdmin) throw new HttpException(400, "Email already exists, kindly login") - - const token = jwt.sign({ email }, ACCESS_TOKEN, { - expiresIn: "1d", - }); - - const mailOption = { - to: email, - subject: 'Admin account registration', - text: `Dear ${email}, kindly follow this link in order to continue with your registration process as an admin!\n - "http://localhost:8080/api/v1/admin/admin-registration-continuation?email=${email}&token=${token}"` + if (validateEmail(email)) { + const existingAdmin = await Admin.findOne({email}) + if (existingAdmin) { + throw new HttpException(400,"Email already exists, kindly login") + } + const token = generateToken(email, 'ADMIN') + const mailOption = { + from: process.env.MAIL_USER, + to: email, + subject: 'Admin account registration', + html: htmlTemplate(process.env.BASE_URL, email.substring(0, email.indexOf('@')), email, token) + + } + await sendMail(mailOption) + // const admin = await Admin.create({ + // email + // }); + + return res.json({ + status: "success", + message: "A registration link has been successfully sent to your email, kindly continue your registration from there.", + }); } + else { - await sendMail(mailOption) - const admin = await Admin.create({ - email - }); - - if (!admin) throw new HttpException(500, "an error occurred") - - return res - .status(200) - .send(new HttpResponse("success", "A registration link has been successfully sent to your email, kindly continue your registration from there.", { token })); - + throw new HttpException(400,"Invalid Email!") + } + } catch (err) { next(err) } @@ -44,33 +49,56 @@ export const registerEmail = async (req, res, next) => { export const updateAdminRecord = async (req, res, next) => { try { - const { email, token } = req.query - const { username, password } = req.body + const {email, token} = req.query - const decoded = jwt.decode(token, { complete: true }); - const payload = decoded.payload; + if (email && token) { + if (!extractEmailAndRoleFromToken(token, email, 'ADMIN')) { + throw new HttpException(400,"Invalid or expired token") + } + // const decoded = jwt.decode(token, {complete: true}) + // const payload = decoded.payload - const currentTime = Math.floor(Date.now() / 1000); - if (currentTime >= payload.exp) throw new HttpException(401, "duration expired"); - if (email !== payload.email) throw new HttpException(401, "invalid email"); + // const currentTime = Math.floor(Date.now() / 1000) - const existingUsername = await Admin.findOne({ username }) - const existingEmail = await Admin.findOne({ email }) + // if (currentTime >= payload.exp) { + // throw new HttpException(400,"duration expired") + // } - if (existingEmail.isAccepted) throw new HttpException(409, "admitted already") - if (existingUsername) throw new HttpException(409, "Username has already been taken") + const {username, password} = req.body + + if (!validateField(username)) { + throw new HttpException(400,"Username must not be less than 6 characters long") + } - const admin = await Admin.findOne({ email }) - if (!admin) throw new HttpException(404, "email not registered") + const existingAdmin = await Admin.findOne({username}) + if (existingAdmin) { + throw new HttpException(400,"Username has already been taken") + } + + if (!passwordValidator(password)) { + throw new HttpException( + 400, + "Password must contain a number, a special character, an uppercase letter, and not less than 8 characters long" + ) + } - admin.username = username - admin.password = password - admin.isAccepted = true - await admin.save(); + const admin = await Admin.create({ + email, + username, + password, + isAccepted: true + }); - return res - .status(200) - .send(new HttpResponse("success", "Your account has been successfully created, you would be notified once your account is being activated.")); + return res.json({ + status: "success", + message: "Your account has been successfully created, you would be notified once your account is being activated.", + data: admin + }); + } + + else { + throw new HttpException(400,"Invalid request") + } } catch (err) { @@ -81,18 +109,25 @@ export const updateAdminRecord = async (req, res, next) => { export const activateAdmin = async (req, res, next) => { try { const id = req.params.id - const admin = await Admin.findOne({ _id: id }) - if (!admin) throw new HttpException(400, "No admin details found") - - if (admin.isVerified) throw new HttpException(400, "Admin has already been verified") - admin.isVerified = true - - await admin.save() - - return res - .status(200) - .send(new HttpResponse("success", `${admin.username} account has now been activated.`)); + if (id) { + const admin = await Admin.findOne({_id:id}) + if (!admin) { + throw new HttpException(400,"No admin details found") + } + if (admin.isVerified) { + throw new HttpException(400,"Admin has already been verified") + } + admin.isVerified = true + await admin.save() + return res.json({ + status: "success", + message: `${admin.username} account has now been activated.`, + }); + } + else { + throw new HttpException(400,"Invalid request") + } } catch (err) { next(err) @@ -101,20 +136,34 @@ export const activateAdmin = async (req, res, next) => { export const adminLogin = async (req, res, next) => { try { - const { email, password } = req.body - - const admin = await Admin.findOne({ email }) - - if (!admin) throw new HttpException(404, "Incorrect Email or Password") - - if (!(await admin.isPasswordMatch(password))) throw new HttpException(404, "Incorrect Email or Password") - - if (!admin.isVerified) throw new HttpException(403, "Unverified account") - - return res - .status(200) - .send(new HttpResponse("success", `Dear ${admin.username}, welcome to the admin dashboard`)); + const {email, password} = req.body + + if (email && password && validateEmail(email)) { + const admin = await Admin.findOne({email}).select("+password") + if (!admin) { + throw new HttpException(400,"Incorrect Email or Password") + } + + const passwordResult = await admin.isPasswordMatch(password) + console.log('Password Result: ', passwordResult); + if (!passwordResult) { + throw new HttpException(400,"Incorrect Email or Password") + } + if(!admin.isVerified) { + throw new HttpException(400,"Unverified account") + } + return res.json({ + status: "success", + message: `Dear ${admin.username}, welcome to the admin dashboard`, + token: generateToken(admin.email, admin.roles) + }); + + } + else { + throw new HttpException(400,"Invalid Email!") + } + } catch (err) { next(err) } @@ -122,26 +171,27 @@ export const adminLogin = async (req, res, next) => { export const adminProfileUpdate = async (req, res, next) => { try { - const { id } = req.params + const {id} = req.params if (!id) { - throw new HttpException(400, "Invalid request") + throw new HttpException(400,"Invalid request") } else { - const { firstname, lastname } = req.body + const {firstname, lastname} = req.body const admin = await Admin.findOneAndUpdate( - { _id: id }, - { firstname: firstname, lastname: lastname }, - { new: true, runValidators: true } - ) + {_id: id}, + {firstname: firstname, lastname: lastname}, + {new: true, runValidators: true} + ) - if (!admin) { - throw new HttpException(400, "Admin not found") - } - - return res - .status(200) - .send(new HttpResponse("success", `Dear ${admin.firstname}, your account has now been updated.`)); + if (!admin) { + throw new HttpException(400,"Admin not found") + } + return res.json({ + status: "success", + message: `Dear ${admin.firstname}, your account has now been updated.`, + }); + } } catch (err) { @@ -151,25 +201,24 @@ export const adminProfileUpdate = async (req, res, next) => { export const adminsController = async (req, res, next) => { try { - const admins = await Admin.find({}) - - return res - .status(200) - .send(new HttpResponse("success", `fetched all admin successfully`, admins)); - - } + const admins = await Admin.find({}) + res.json({ + status: "success", + data: admins, + }); + } catch (error) { next(error) } -}; + }; export const deleteAdminController = async (req, res, next) => { const id = req.params.id; try { if (id) { - const admin = await Admin.findOneAndDelete({ _id: id }) + const admin = await Admin.findOneAndDelete({_id: id}) if (!admin) { - throw new HttpException(400, "Admin not found") + throw new HttpException(400,"Admin not found") } res.json({ status: "success", @@ -177,27 +226,28 @@ export const deleteAdminController = async (req, res, next) => { }); } else { - throw new HttpException(400, "Invalid request") + throw new HttpException(400,"Invalid request") } - } + } catch (error) { next(error) } -}; + }; -export const emailUsersController = async (req, res, next) => { - const { subject, message, isVerified } = req.body + export const emailUsersController = async (req, res, next) => { + const {subject, message, isVerified} = req.body try { - let users - if (isVerified === true) { - users = await userModel.find({ isVerified: true }) + let users + if (isVerified === true) { + users = await Admin.find({isVerified: true}) } else { - users = await userModel.find({}) - } - await autoEmail(users, subject, message, res) - } + users = await Admin.find({}) + } + await autoEmail(users, subject, message, res) + } catch (error) { next(error) } -}; \ No newline at end of file + }; + diff --git a/src/middleware/auth/admin-has-access.js b/src/middleware/auth/admin-has-access.js new file mode 100644 index 0000000..24b0749 --- /dev/null +++ b/src/middleware/auth/admin-has-access.js @@ -0,0 +1,17 @@ +import HttpException from "../../exceptions/HttpException"; +import { getTokenFromHeader } from "../../utils/jwt/get-token"; +import { verifyToken } from "../../utils/jwt/verify-token"; + +export const hasAccess = (req, res, next) => { + // get token from header + const token = getTokenFromHeader(req) + if(!token) throw new HttpException(400,"It seems there was no token attached to the header!") + + const decodedUser = verifyToken(token); + req.role = decodedUser.role + + if (!decodedUser || decodedUser.role !== 'ADMIN') { + throw new HttpException(401,"Unauthorized") + } + next(); +} \ No newline at end of file diff --git a/src/middleware/auth/is-admin-logged-in.js b/src/middleware/auth/is-admin-logged-in.js new file mode 100644 index 0000000..d3f383c --- /dev/null +++ b/src/middleware/auth/is-admin-logged-in.js @@ -0,0 +1,17 @@ +import HttpException from "../../exceptions/HttpException"; +import { getTokenFromHeader } from "../../utils/jwt/get-token"; +import { verifyToken } from "../../utils/jwt/verify-token"; + + +export const isAdminLoggedIn = (req, res, next) => { + // get token from header + const token = getTokenFromHeader(req) + if(!token) throw new HttpException(400,"It seems there was no token attached to the header!") + + const decodedUser = verifyToken(token); + if (!decodedUser) { + throw new HttpException(400,"Invalid or expired token passed! Kindly login to generate new token thank you.") + } + req.adminEmail = decodedUser.email + next(); +} \ No newline at end of file diff --git a/src/middleware/auth/superadmin-has-access.js b/src/middleware/auth/superadmin-has-access.js new file mode 100644 index 0000000..b2c3c27 --- /dev/null +++ b/src/middleware/auth/superadmin-has-access.js @@ -0,0 +1,17 @@ +import HttpException from "../../exceptions/HttpException"; +import { getTokenFromHeader } from "../../utils/jwt/get-token"; +import { verifyToken } from "../../utils/jwt/verify-token"; + +export const hasSuperadminAccess = (req, res, next) => { + // get token from header + const token = getTokenFromHeader(req) + if(!token) throw new HttpException(400,"It seems there was no token attached to the header!") + + const decodedUser = verifyToken(token); + req.role = decodedUser.role + + if (!decodedUser || decodedUser.role !== 'SUPERADMIN') { + throw new HttpException(401,"Unauthorized") + } + next(); +} \ No newline at end of file diff --git a/src/models/admin.model.js b/src/models/admin.model.js index d892625..7dc4b69 100644 --- a/src/models/admin.model.js +++ b/src/models/admin.model.js @@ -12,7 +12,7 @@ const adminSchema = new mongoose.Schema({ }, username: { type: String, - // unique: true, + unique: true, lowercase: true, trim: true, }, @@ -24,7 +24,12 @@ const adminSchema = new mongoose.Schema({ }, password: { type: String, - // select: false, + select: false, + }, + roles: { + type: String, + enum: ["ADMIN", "SUPERADMIN"], + default: "ADMIN" }, isVerified: { type: Boolean, From a48420461e20f2f4f090f42fa1d055a1bf952caa Mon Sep 17 00:00:00 2001 From: hardeylarning Date: Wed, 29 Mar 2023 11:59:40 +0100 Subject: [PATCH 56/57] fixed auth middleware --- src/routes/admin.route.js | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/routes/admin.route.js b/src/routes/admin.route.js index 78fd89d..c1a077c 100644 --- a/src/routes/admin.route.js +++ b/src/routes/admin.route.js @@ -19,12 +19,15 @@ import { activateAdminSchema } from "../validator_schema/adminAuthSchema"; +import { isAdminLoggedIn } from "../middleware/auth/is-admin-logged-in"; +import { hasSuperadminAccess } from "../middleware/auth/superadmin-has-access"; + const adminRouter = express.Router(); adminRouter .route("/admin-register-email") - .post(validatorMiddleware(registerEmailSchema, "body"), registerEmail); + .post(validatorMiddleware(registerEmailSchema, "body"), isAdminLoggedIn, registerEmail); adminRouter .route("/admin-registration-continuation") @@ -36,7 +39,7 @@ adminRouter adminRouter .route("/admin-activation/:id") - .patch(validatorMiddleware(activateAdminSchema, "params"), activateAdmin); + .patch(validatorMiddleware(activateAdminSchema, "params"), isAdminLoggedIn, hasSuperadminAccess, activateAdmin); adminRouter .route("/login") @@ -44,16 +47,16 @@ adminRouter adminRouter .route("/admin-profile-update/:id") - .put(adminProfileUpdate); + .put(isAdminLoggedIn, adminProfileUpdate); adminRouter .route("/") - .get(adminsController); + .get(isAdminLoggedIn, hasSuperadminAccess, adminsController); adminRouter .route("/:id") - .delete(deleteAdminController); + .delete(isAdminLoggedIn, hasSuperadminAccess, deleteAdminController); adminRouter .route("/email-users") - .post(emailUsersController); + .post(isAdminLoggedIn, emailUsersController); export default adminRouter; From 1ddec945fcc015b42d5a11d44f08dc1a26a3e802 Mon Sep 17 00:00:00 2001 From: hardeylarning Date: Wed, 29 Mar 2023 12:04:46 +0100 Subject: [PATCH 57/57] done with controller --- src/controller/admin.controller.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/controller/admin.controller.js b/src/controller/admin.controller.js index f00393a..675261c 100644 --- a/src/controller/admin.controller.js +++ b/src/controller/admin.controller.js @@ -153,6 +153,9 @@ export const adminLogin = async (req, res, next) => { if(!admin.isVerified) { throw new HttpException(400,"Unverified account") } + if(!admin.isBlocked) { + throw new HttpException(400,"your account has been blocked, kindly reach out to Superadmin") + } return res.json({ status: "success", message: `Dear ${admin.username}, welcome to the admin dashboard`,