Commit cf02f518 by Harish Patel

added email functionality and also made provision to login with otp

1 parent f99beaf9
module.exports = ({ env }) => ({
// ...
// Reference: https://github.com/strapi/strapi/tree/main/packages/providers/email-nodemailer
email: {
config: {
provider: "nodemailer",
providerOptions: {
host: env("SMTP_HOST", "smtp.example.com"),
port: env("SMTP_PORT", 587),
auth: {
user: env("SMTP_USERNAME"),
pass: env("SMTP_PASSWORD"),
},
// ... any custom nodemailer options
},
settings: {
defaultFrom: "from@example.com",
// defaultReplyTo: "hello@example.com"
},
},
},
// ...
// ...
// Reference: https://www.npmjs.com/package/@strapi/provider-upload-aws-s3
// upload: {
// config: {
// provider: "aws-s3",
// providerOptions: {
// accessKeyId: env("AWS_ACCESS_KEY_ID"),
// secretAccessKey: env("AWS_ACCESS_SECRET"),
// region: env("AWS_REGION"),
// params: {
// Bucket: env("AWS_BUCKET")
// }
// },
// actionOptions: {
// upload: {},
// uploadStream: {},
// delete: {}
// }
// }
// }
// ...
});
......@@ -11,6 +11,7 @@
"dependencies": {
"@strapi/plugin-i18n": "4.3.6",
"@strapi/plugin-users-permissions": "4.3.6",
"@strapi/provider-email-nodemailer": "^4.3.8",
"@strapi/strapi": "4.3.6",
"better-sqlite3": "7.4.6",
"pg": "^8.8.0",
......@@ -3204,6 +3205,19 @@
"npm": ">=6.0.0"
}
},
"node_modules/@strapi/provider-email-nodemailer": {
"version": "4.3.8",
"resolved": "https://registry.npmjs.org/@strapi/provider-email-nodemailer/-/provider-email-nodemailer-4.3.8.tgz",
"integrity": "sha512-KCZxlZlrQcKo9G5RY17KN73x2hcW3rEm/DXo/Is4pqQiehC35HZEMbxzAMc3SG1NArkqtnoI8JtI1BXJd8jomg==",
"dependencies": {
"lodash": "4.17.21",
"nodemailer": "6.7.7"
},
"engines": {
"node": ">=14.19.1 <=16.x.x",
"npm": ">=6.0.0"
}
},
"node_modules/@strapi/provider-email-sendmail": {
"version": "4.3.6",
"resolved": "https://registry.npmjs.org/@strapi/provider-email-sendmail/-/provider-email-sendmail-4.3.6.tgz",
......@@ -11804,6 +11818,14 @@
"node": ">=6"
}
},
"node_modules/nodemailer": {
"version": "6.7.7",
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.7.7.tgz",
"integrity": "sha512-pOLC/s+2I1EXuSqO5Wa34i3kXZG3gugDssH+ZNCevHad65tc8vQlCQpOLaUjopvkRQKm2Cki2aME7fEOPRy3bA==",
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/nodemailer-fetch": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/nodemailer-fetch/-/nodemailer-fetch-1.6.0.tgz",
......@@ -19255,6 +19277,15 @@
"url-join": "4.0.1"
}
},
"@strapi/provider-email-nodemailer": {
"version": "4.3.8",
"resolved": "https://registry.npmjs.org/@strapi/provider-email-nodemailer/-/provider-email-nodemailer-4.3.8.tgz",
"integrity": "sha512-KCZxlZlrQcKo9G5RY17KN73x2hcW3rEm/DXo/Is4pqQiehC35HZEMbxzAMc3SG1NArkqtnoI8JtI1BXJd8jomg==",
"requires": {
"lodash": "4.17.21",
"nodemailer": "6.7.7"
}
},
"@strapi/provider-email-sendmail": {
"version": "4.3.6",
"resolved": "https://registry.npmjs.org/@strapi/provider-email-sendmail/-/provider-email-sendmail-4.3.6.tgz",
......@@ -25349,6 +25380,11 @@
"sorted-array-functions": "^1.3.0"
}
},
"nodemailer": {
"version": "6.7.7",
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.7.7.tgz",
"integrity": "sha512-pOLC/s+2I1EXuSqO5Wa34i3kXZG3gugDssH+ZNCevHad65tc8vQlCQpOLaUjopvkRQKm2Cki2aME7fEOPRy3bA=="
},
"nodemailer-fetch": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/nodemailer-fetch/-/nodemailer-fetch-1.6.0.tgz",
......@@ -12,6 +12,7 @@
"dependencies": {
"@strapi/plugin-i18n": "4.3.6",
"@strapi/plugin-users-permissions": "4.3.6",
"@strapi/provider-email-nodemailer": "^4.3.8",
"@strapi/strapi": "4.3.6",
"better-sqlite3": "7.4.6",
"pg": "^8.8.0",
......
{
"kind": "collectionType",
"collectionName": "up_users",
"info": {
"name": "user",
"description": "",
"singularName": "user",
"pluralName": "users",
"displayName": "User"
},
"options": {
"draftAndPublish": false,
"timestamps": true
},
"attributes": {
"username": {
"type": "string",
"minLength": 3,
"unique": true,
"configurable": false,
"required": true
},
"email": {
"type": "email",
"minLength": 6,
"configurable": false,
"required": true
},
"provider": {
"type": "string",
"configurable": false
},
"password": {
"type": "password",
"minLength": 6,
"configurable": false,
"private": true
},
"resetPasswordToken": {
"type": "string",
"configurable": false,
"private": true
},
"confirmationToken": {
"type": "string",
"configurable": false,
"private": true
},
"confirmed": {
"type": "boolean",
"default": false,
"configurable": false
},
"blocked": {
"type": "boolean",
"default": false,
"configurable": false
},
"role": {
"type": "relation",
"relation": "manyToOne",
"target": "plugin::users-permissions.role",
"inversedBy": "users",
"configurable": false
},
"profileImage": {
"type": "media",
"multiple": false,
"required": false,
"allowedTypes": [
"images"
]
},
"aboutMe": {
"type": "text"
},
"fullName": {
"type": "string",
"required": true
},
"oneTimePassword": {
"type": "string",
"configurable": false,
"private": true
}
}
}
\ No newline at end of file
......@@ -2,10 +2,11 @@ const utils = require("@strapi/utils");
const _ = require("lodash");
const { sanitize } = utils;
const { ApplicationError } = utils.errors;
const { ApplicationError, ValidationError } = utils.errors;
const {
validateRegisterBody,
} = require("@strapi/plugin-users-permissions/server/controllers/validation/auth");
const { getService } = require("@strapi/plugin-users-permissions/server/utils");
const sanitizeUser = (user, ctx) => {
const { auth } = ctx.state;
......@@ -21,9 +22,123 @@ const userPermissionExtension = (plugin) => {
return plugin.controllers.user.update(ctx);
};
plugin.controllers.user.startOtpLogin = async (ctx) => {
const { mahareraNumber, mobileNumber } = ctx.request.body;
if (!mahareraNumber || !mobileNumber) {
throw new ValidationError(
"Please specify both the maharera & mobile numbers."
);
}
const pluginStore = await strapi.store({
type: "plugin",
name: "users-permissions",
});
const emailSettings = await pluginStore.get({ key: "email" });
// Find the channel partner first.
const channelPartner = await strapi
.query("api::channel-partner.channel-partner")
.findOne({
populate: ["user"],
where: {
$and: [{ reraNumber: mahareraNumber }, { mobileNo: mobileNumber }],
},
});
if (!channelPartner) {
throw new ValidationError(
"No channel partner registered with specified maharera number, mobile number combination."
);
}
// Find the linked user next.
const user = await strapi
.query("plugin::users-permissions.user")
.findOne({ id: channelPartner.user.id });
if (!user || user.blocked) {
throw new ValidationError(
"Unable to resolve user linked to channel partner."
);
}
const resetPasswordSettings = _.get(
emailSettings,
"reset_password.options",
{}
);
const oneTimePassword = Math.floor(100000 + Math.random() * 900000);
const emailToSend = {
to: user.email,
from:
resetPasswordSettings.from.email || resetPasswordSettings.from.name
? `${resetPasswordSettings.from.name} <${resetPasswordSettings.from.email}>`
: undefined,
replyTo: resetPasswordSettings.response_email,
subject: `Your one time password is: ${oneTimePassword}`,
text: `Hello ${channelPartner.contactPersonName}, Your one time password to login to your partner portal is ${oneTimePassword}`,
html: `<p>Hello ${channelPartner.contactPersonName}, <br></br>Your one time password to login to your partner portal is ${oneTimePassword}</p><br /> Best Regards, <br /> Team Hiranandani.`,
};
// NOTE: Update the user before sending the email so an Admin can generate the link if the email fails
await getService("user").edit(user.id, {
oneTimePassword: `${oneTimePassword}`,
});
// Send an email to the user.
await strapi.plugin("email").service("email").send(emailToSend);
ctx.send({ ok: true, message: "otp sent" });
};
plugin.controllers.user.finishOtpLogin = async (ctx) => {
const { oneTimePassword, mahareraNumber, mobileNumber } = ctx.request.body;
if (!oneTimePassword || !mobileNumber || !mahareraNumber) {
throw new ValidationError(
"Please specify the oneTimePassword, maharera number and mobile numbers."
);
}
// Find the channel partner first.
const channelPartner = await strapi
.query("api::channel-partner.channel-partner")
.findOne({
populate: ["user"],
where: {
$and: [{ reraNumber: mahareraNumber }, { mobileNo: mobileNumber }],
},
});
if (!channelPartner) {
throw new ValidationError(
"No channel partner registered with specified maharera number, mobile number combination."
);
}
// Find the linked user next.
const user = await strapi.query("plugin::users-permissions.user").findOne({
where: {
$and: [
{ id: channelPartner.user.id },
{ oneTimePassword: oneTimePassword },
],
},
});
if (!user || user.blocked) {
throw new ValidationError("Code provided is not valid.");
}
await getService("user").edit(user.id, {
oneTimePassword: null,
password: oneTimePassword,
});
ctx.send({ ok: true, message: "otp updated" });
};
/** Example of overriding an existing route. */
plugin.controllers.auth.register = async (ctx) => {
const pluginStore = await strapi.store({
type: "plugin",
name: "users-permissions",
......@@ -134,11 +249,22 @@ const userPermissionExtension = (plugin) => {
});
};
/** Endpoint used to allow edits on a user done by currently logged in user only their own record. */
plugin.routes["content-api"].routes.push({
method: "PUT",
path: "/users/me",
handler: "user.updateMe",
});
plugin.routes["content-api"].routes.push({
method: "POST",
path: "/users/start-otp-login",
handler: "user.startOtpLogin",
});
plugin.routes["content-api"].routes.push({
method: "POST",
path: "/users/finish-otp-login",
handler: "user.finishOtpLogin",
});
return plugin;
};
......
......@@ -1791,6 +1791,14 @@
"request" "^2.83.0"
"url-join" "4.0.1"
"@strapi/provider-email-nodemailer@^4.3.8":
"integrity" "sha512-KCZxlZlrQcKo9G5RY17KN73x2hcW3rEm/DXo/Is4pqQiehC35HZEMbxzAMc3SG1NArkqtnoI8JtI1BXJd8jomg=="
"resolved" "https://registry.npmjs.org/@strapi/provider-email-nodemailer/-/provider-email-nodemailer-4.3.8.tgz"
"version" "4.3.8"
dependencies:
"lodash" "4.17.21"
"nodemailer" "6.7.7"
"@strapi/provider-email-sendmail@4.3.6":
"integrity" "sha512-pTz08brITDu1tjGBMZoKfoE+7zEhMoxdFvEMx16h32PLa0TJSjne3vv1ETzmNGWX/JKD6GFH4ESzSnLT2ES6gQ=="
"resolved" "https://registry.npmjs.org/@strapi/provider-email-sendmail/-/provider-email-sendmail-4.3.6.tgz"
......@@ -4312,6 +4320,11 @@
"resolved" "https://registry.npmjs.org/es6-object-assign/-/es6-object-assign-1.1.0.tgz"
"version" "1.1.0"
"esbuild-darwin-64@0.15.7":
"integrity" "sha512-KGPt3r1c9ww009t2xLB6Vk0YyNOXh7hbjZ3EecHoVDxgtbUlYstMPDaReimKe6eOEfyY4hBEEeTvKwPsiH5WZg=="
"resolved" "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.15.7.tgz"
"version" "0.15.7"
"esbuild-loader@^2.19.0":
"integrity" "sha512-dr+j8O4w5RvqZ7I4PPB4EIyVTd679EBQnMm+JBB7av+vu05Zpje2IpK5N3ld1VWa+WxrInIbNFAg093+E1aRsA=="
"resolved" "https://registry.npmjs.org/esbuild-loader/-/esbuild-loader-2.20.0.tgz"
......@@ -4919,6 +4932,11 @@
"resolved" "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz"
"version" "1.0.0"
"fsevents@~2.3.2":
"integrity" "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA=="
"resolved" "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz"
"version" "2.3.2"
"function-bind@^1.1.1":
"integrity" "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
"resolved" "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz"
......@@ -7180,6 +7198,11 @@
dependencies:
"nodemailer-fetch" "1.6.0"
"nodemailer@6.7.7":
"integrity" "sha512-pOLC/s+2I1EXuSqO5Wa34i3kXZG3gugDssH+ZNCevHad65tc8vQlCQpOLaUjopvkRQKm2Cki2aME7fEOPRy3bA=="
"resolved" "https://registry.npmjs.org/nodemailer/-/nodemailer-6.7.7.tgz"
"version" "6.7.7"
"normalize-path@^3.0.0", "normalize-path@~3.0.0":
"integrity" "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="
"resolved" "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz"
......
Styling with Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!