From 2e6b7c05f7fc35b69913767ca2993fa585749596 Mon Sep 17 00:00:00 2001 From: subnub Date: Sun, 22 Dec 2024 18:41:39 -0500 Subject: [PATCH] finished resend email verification, reset password, and forgot password logic --- backend/enviroment/env.ts | 2 - backend/express-routers/user-router.ts | 37 +++--- backend/middleware/auth.ts | 2 - backend/middleware/authFullUser.ts | 2 - .../middleware/authNoEmailVerificication.ts | 62 ---------- backend/models/user-model.ts | 8 +- backend/services/user-service/user-service.ts | 7 +- backend/utils/createEmailTransporter.ts | 33 ++++++ backend/utils/sendPasswordResetEmail.ts | 67 +++++++---- backend/utils/sendVerificationEmail.ts | 36 +----- src/api/user.ts | 30 +++++ src/components/LoginPage/LoginPage.tsx | 49 ++++++-- .../ResetPasswordPage/ResetPasswordPage.jsx | 48 -------- .../ResetPasswordPage/ResetPasswordPage.tsx | 96 ++++++++++++++++ src/components/ResetPasswordPage/index.jsx | 107 ------------------ .../SettingsPage/SettingsAccountSection.tsx | 55 ++++++++- src/components/SettingsPage/SettingsPage.tsx | 16 ++- .../VerifyEmailPage/VerifyEmailPage.jsx | 9 -- .../VerifyEmailPage/VerifyEmailPage.tsx | 37 ++++++ src/components/VerifyEmailPage/index.jsx | 73 ------------ src/routers/AppRouter.jsx | 8 +- 21 files changed, 386 insertions(+), 398 deletions(-) delete mode 100644 backend/middleware/authNoEmailVerificication.ts create mode 100644 backend/utils/createEmailTransporter.ts delete mode 100644 src/components/ResetPasswordPage/ResetPasswordPage.jsx create mode 100644 src/components/ResetPasswordPage/ResetPasswordPage.tsx delete mode 100644 src/components/ResetPasswordPage/index.jsx delete mode 100644 src/components/VerifyEmailPage/VerifyEmailPage.jsx create mode 100644 src/components/VerifyEmailPage/VerifyEmailPage.tsx delete mode 100644 src/components/VerifyEmailPage/index.jsx diff --git a/backend/enviroment/env.ts b/backend/enviroment/env.ts index c644cd4..4a11e08 100644 --- a/backend/enviroment/env.ts +++ b/backend/enviroment/env.ts @@ -18,7 +18,6 @@ export default { sendgridKey: process.env.SENDGRID_KEY, sendgridEmail: process.env.SENDGRID_EMAIL, remoteURL: process.env.REMOTE_URL, - disableEmailVerification: process.env.DISABLE_EMAIL_VERIFICATION, secureCookies: process.env.SECURE_COOKIES, tempDirectory: process.env.TEMP_DIRECTORY, emailVerification: process.env.EMAIL_VERIFICATION, @@ -49,7 +48,6 @@ module.exports = { sendgridKey: process.env.SENDGRID_KEY, sendgridEmail: process.env.SENDGRID_EMAIL, remoteURL: process.env.REMOTE_URL, - disableEmailVerification: process.env.DISABLE_EMAIL_VERIFICATION, secureCookies: process.env.SECURE_COOKIES, tempDirectory: process.env.TEMP_DIRECTORY, emailVerification: process.env.EMAIL_VERIFICATION, diff --git a/backend/express-routers/user-router.ts b/backend/express-routers/user-router.ts index ee81682..a77366d 100644 --- a/backend/express-routers/user-router.ts +++ b/backend/express-routers/user-router.ts @@ -1,6 +1,5 @@ import { Router } from "express"; import auth from "../middleware/auth"; -import authNoEmailVerification from "../middleware/authNoEmailVerificication"; import UserController from "../controllers/user-controller"; import authRefresh from "../middleware/authRefresh"; import authLogout from "../middleware/authLogout"; @@ -15,11 +14,7 @@ const router = Router(); // GET -router.get( - "/user-service/user", - authNoEmailVerification, - userController.getUser -); +router.get("/user-service/user", auth, userController.getUser); router.get("/user-service/user-detailed", auth, userController.getUserDetailed); @@ -37,21 +32,6 @@ router.post( userController.createUser ); -router.post("/user-service/verify-email", userController.verifyEmail); - -router.post( - "/user-service/resend-verify-email", - authNoEmailVerification, - userController.resendVerifyEmail -); - -router.post( - "/user-service/send-password-reset", - userController.sendPasswordReset -); - -router.post("/user-service/reset-password", userController.resetPassword); - router.post("/user-service/get-token", authRefresh, userController.getToken); // PATCH @@ -63,4 +43,19 @@ router.patch( userController.changePassword ); +router.patch( + "/user-service/resend-verify-email", + auth, + userController.resendVerifyEmail +); + +router.patch("/user-service/verify-email", userController.verifyEmail); + +router.patch("/user-service/reset-password", userController.resetPassword); + +router.patch( + "/user-service/send-password-reset", + userController.sendPasswordReset +); + export default router; diff --git a/backend/middleware/auth.ts b/backend/middleware/auth.ts index e4902b6..b7d159c 100644 --- a/backend/middleware/auth.ts +++ b/backend/middleware/auth.ts @@ -33,8 +33,6 @@ const auth = async (req: RequestType, res: Response, next: NextFunction) => { const user = decoded.user; if (!user) throw new Error("No User"); - if (!user.emailVerified && !env.disableEmailVerification) - throw new Error("Email Not Verified"); req.user = user; diff --git a/backend/middleware/authFullUser.ts b/backend/middleware/authFullUser.ts index c13c84e..42824b1 100644 --- a/backend/middleware/authFullUser.ts +++ b/backend/middleware/authFullUser.ts @@ -38,8 +38,6 @@ const authFullUser = async ( const user = decoded.user; if (!user) throw new Error("No User"); - if (!user.emailVerified && !env.disableEmailVerification) - throw new Error("Email Not Verified"); const fullUser = await User.findById(user._id); diff --git a/backend/middleware/authNoEmailVerificication.ts b/backend/middleware/authNoEmailVerificication.ts deleted file mode 100644 index a27f49d..0000000 --- a/backend/middleware/authNoEmailVerificication.ts +++ /dev/null @@ -1,62 +0,0 @@ -import jwt from "jsonwebtoken"; -import User, { UserInterface } from "../models/user-model"; -import env from "../enviroment/env"; -import { Request, Response, NextFunction } from "express"; -import userUpdateCheck from "../utils/userUpdateCheck"; -import mongoose from "mongoose"; - -interface RequestType extends Request { - user?: userAccessType; - token?: string; - encryptedToken?: string; -} - -type jwtType = { - iv: Buffer; - user: userAccessType; -}; - -type userAccessType = { - _id: mongoose.Types.ObjectId; - emailVerified: boolean; - email: string; - botChecked: boolean; -}; - -const auth = async (req: RequestType, res: Response, next: NextFunction) => { - try { - const accessToken = req.cookies["access-token"]; - - if (!accessToken) throw new Error("No Access Token"); - - const decoded = jwt.verify(accessToken, env.passwordAccess!) as jwtType; - - let user = decoded.user; - - if (!user) throw new Error("No User"); - - if (!user.emailVerified && !env.disableEmailVerification) { - const currentUUID = req.headers.uuid as string; - user = await userUpdateCheck(res, user._id, currentUUID); - } - - req.user = user; - - next(); - } catch (e) { - if ( - e instanceof Error && - e.message !== "No Access Token" && - e.message !== "No User" - ) { - console.log( - "\nAuthorization No Email Verification Middleware Error:", - e.message - ); - } - - return res.status(401).send("Error Authenticating"); - } -}; - -export default auth; diff --git a/backend/models/user-model.ts b/backend/models/user-model.ts index db4666e..2e46a61 100644 --- a/backend/models/user-model.ts +++ b/backend/models/user-model.ts @@ -154,6 +154,12 @@ userSchema.methods.toJSON = function () { delete userObject.privateKey; delete userObject.publicKey; + if (env.emailVerification !== "true") { + delete userObject.emailVerified; + } else { + userObject.emailVerified = user.emailVerified || false; + } + return userObject; }; @@ -203,7 +209,7 @@ userSchema.methods.generateAuthToken = async function ( const userObj = { _id: user._id, - emailVerified: user.emailVerified || env.disableEmailVerification, + emailVerified: user.emailVerified, email: user.email, }; diff --git a/backend/services/user-service/user-service.ts b/backend/services/user-service/user-service.ts index dbd80d6..42634d9 100644 --- a/backend/services/user-service/user-service.ts +++ b/backend/services/user-service/user-service.ts @@ -172,9 +172,14 @@ class UserService { const verifiedEmail = user.emailVerified; + if (env.emailVerification !== "true") { + throw new ForbiddenError("Email Verification Disabled"); + } + if (!verifiedEmail) { const emailToken = await user.generateEmailVerifyToken(); - await sendVerificationEmail(user, emailToken); + const result = await sendVerificationEmail(user, emailToken); + if (!result) throw new InternalServerError("Email Verification Error"); } else { throw new ForbiddenError("Email Already Authorized"); } diff --git a/backend/utils/createEmailTransporter.ts b/backend/utils/createEmailTransporter.ts new file mode 100644 index 0000000..fbf99a9 --- /dev/null +++ b/backend/utils/createEmailTransporter.ts @@ -0,0 +1,33 @@ +import env from "../enviroment/env"; +import nodemailer from "nodemailer"; + +const createEmailTransporter = () => { + const emailVerification = env.emailVerification === "true"; + const emailAPIKey = env.emailAPIKey; + const emailDomain = env.emailDomain; + const emailHost = env.emailHost; + const emailPort = env.emailPort; + const emailAddress = env.emailAddress; + + if (!emailVerification) { + throw new Error("Email Verification Not Enabled"); + } + + if (!emailAPIKey || !emailDomain || !emailHost || !emailAddress) { + throw new Error("Email Verification Not Setup Correctly"); + } + + const transporter = nodemailer.createTransport({ + // @ts-ignore + host: emailHost, + port: emailPort || 587, + auth: { + user: emailDomain, + pass: emailAPIKey, + }, + }); + + return transporter; +}; + +export default createEmailTransporter; diff --git a/backend/utils/sendPasswordResetEmail.ts b/backend/utils/sendPasswordResetEmail.ts index 3f1c3eb..a579e79 100644 --- a/backend/utils/sendPasswordResetEmail.ts +++ b/backend/utils/sendPasswordResetEmail.ts @@ -1,29 +1,56 @@ +import SMTPTransport from "nodemailer/lib/smtp-transport"; import env from "../enviroment/env"; import { UserInterface } from "../models/user-model"; -// import sgMail from "@sendgrid/mail"; +import nodemailer from "nodemailer"; +import createEmailTransporter from "./createEmailTransporter"; + +type MailOptionsType = { + from: string; + to: string; + subject: string; + text: string; +}; + +const sendEmail = ( + transporter: nodemailer.Transporter, + mailOptions: MailOptionsType +) => { + return new Promise((resolve, reject) => { + transporter.sendMail(mailOptions, (error, info) => { + if (error) { + reject(error); + } else { + resolve(info); + } + }); + }); +}; const sendPasswordResetEmail = async ( user: UserInterface, - passwordResetToken: string + resetToken: string ) => { - // if (process.env.NODE_ENV === "test") { - // return; - // } - // const apiKey: any = env.sendgridKey; - // const sendgridEmail:any = env.sendgridEmail; - // const url = env.remoteURL + `/reset-password/${passwordResetToken}` - // // console.log("send grid api key", apiKey) - // // console.log("send grid email", sendgridEmail); - // // console.log("send grid reset url", url) - // sgMail.setApiKey(apiKey); - // const msg = { - // to: user.email, - // from: sendgridEmail, - // subject: "myDrive Password Reset", - // text: `Please navigate to the following link to reset your password: ${url}` - // } - // await sgMail.send(msg); - // //console.log("Send grid email sent reset"); + try { + const transporter = createEmailTransporter(); + + const emailAddress = env.emailAddress!; + const url = env.remoteURL + `/reset-password/${resetToken}`; + + const mailOptions = { + from: emailAddress, + to: user.email, + subject: "myDrive Password Reset", + text: + "Please navigate to the following link to reset your password: " + url, + }; + + await sendEmail(transporter, mailOptions); + + return true; + } catch (e) { + console.log("Error sending password reset email", e); + return false; + } }; export default sendPasswordResetEmail; diff --git a/backend/utils/sendVerificationEmail.ts b/backend/utils/sendVerificationEmail.ts index 5d69ed2..8669e4a 100644 --- a/backend/utils/sendVerificationEmail.ts +++ b/backend/utils/sendVerificationEmail.ts @@ -2,6 +2,7 @@ import SMTPTransport from "nodemailer/lib/smtp-transport"; import env from "../enviroment/env"; import { UserInterface } from "../models/user-model"; import nodemailer from "nodemailer"; +import createEmailTransporter from "./createEmailTransporter"; type MailOptionsType = { from: string; @@ -30,33 +31,11 @@ const sendVerificationEmail = async ( emailToken: string ) => { try { - const emailVerification = env.emailVerification === "true"; - const emailAPIKey = env.emailAPIKey; - const emailDomain = env.emailDomain; - const emailHost = env.emailHost; - const emailPort = env.emailPort; - const emailAddress = env.emailAddress; - - if (!emailVerification) { - return false; - } - - if (!emailAPIKey || !emailDomain || !emailHost || !emailAddress) { - console.log("Email Verification Not Setup Correctly"); - return false; - } + const transporter = createEmailTransporter(); + const emailAddress = env.emailAddress!; const url = env.remoteURL + `/verify-email/${emailToken}`; - const transporter = nodemailer.createTransport({ - host: emailHost, - port: 587 || emailPort, - auth: { - user: emailDomain, - pass: emailAPIKey, - }, - }); - const mailOptions = { from: emailAddress, to: user.email, @@ -66,15 +45,6 @@ const sendVerificationEmail = async ( url, }; - console.log("Sending email verification", mailOptions, { - host: emailHost, - port: 587 || emailPort, - auth: { - user: emailDomain, - pass: emailAPIKey, - }, - }); - await sendEmail(transporter, mailOptions); return true; diff --git a/src/api/user.ts b/src/api/user.ts index 6df69ce..0c6e9a8 100644 --- a/src/api/user.ts +++ b/src/api/user.ts @@ -57,3 +57,33 @@ export const changePasswordAPI = async ( }); return response.data; }; + +export const resendVerifyEmailAPI = async () => { + const response = await axios.patch("/user-service/resend-verify-email"); + return response.data; +}; + +export const verifyEmailAPI = async (emailToken: string) => { + const response = await axios.patch("/user-service/verify-email", { + emailToken, + }); + return response.data; +}; + +export const sendPasswordResetAPI = async (email: string) => { + const response = await axios.patch("/user-service/send-password-reset", { + email, + }); + return response.data; +}; + +export const resetPasswordAPI = async ( + password: string, + passwordToken: string +) => { + const response = await axios.patch("/user-service/reset-password", { + passwordToken, + password, + }); + return response.data; +}; diff --git a/src/components/LoginPage/LoginPage.tsx b/src/components/LoginPage/LoginPage.tsx index 070b2e9..07f46c8 100755 --- a/src/components/LoginPage/LoginPage.tsx +++ b/src/components/LoginPage/LoginPage.tsx @@ -1,5 +1,10 @@ import React, { useEffect, useMemo, useRef, useState } from "react"; -import { createAccountAPI, getUserAPI, loginAPI } from "../../api/user"; +import { + createAccountAPI, + getUserAPI, + loginAPI, + sendPasswordResetAPI, +} from "../../api/user"; import { useLocation, useNavigate } from "react-router-dom"; import { setUser } from "../../reducers/user"; import { useAppDispatch } from "../../hooks/store"; @@ -8,6 +13,7 @@ import AlertIcon from "../../icons/AlertIcon"; import Spinner from "../Spinner/Spinner"; import classNames from "classnames"; import { toast, ToastContainer } from "react-toastify"; +import Swal from "sweetalert2"; const LoginPage = () => { const [email, setEmail] = useState(""); @@ -20,6 +26,7 @@ const LoginPage = () => { const dispatch = useAppDispatch(); const location = useLocation(); const navigate = useNavigate(); + const lastSentPassowordReset = useRef(0); const attemptLoginWithToken = async () => { setAttemptingLogin(true); @@ -75,6 +82,37 @@ const LoginPage = () => { } }; + const resetPassword = async () => { + try { + const currentDate = Date.now(); + if (currentDate - lastSentPassowordReset.current < 1000 * 60 * 1) { + await Swal.fire({ + title: "Please wait 1 minute before resending", + icon: "warning", + confirmButtonColor: "#3085d6", + confirmButtonText: "Okay", + }); + return; + } + + lastSentPassowordReset.current = Date.now(); + + setLoadingLogin(true); + + await toast.promise(sendPasswordResetAPI(email), { + pending: "Sending password reset...", + success: "Password Reset Sent", + error: "Error Sending Password Reset", + }); + + setLoadingLogin(false); + } catch (e) { + console.log("Create Account Error", e); + setLoadingLogin(false); + setError("Create Account Failed"); + } + }; + const isSubmitDisabled = useMemo(() => { switch (mode) { case "login": @@ -94,6 +132,8 @@ const LoginPage = () => { login(); } else if (mode === "create") { createAccount(); + } else if (mode === "reset") { + resetPassword(); } }; @@ -144,12 +184,7 @@ const LoginPage = () => { return (
-
+
{!loadingLogin && ( diff --git a/src/components/ResetPasswordPage/ResetPasswordPage.jsx b/src/components/ResetPasswordPage/ResetPasswordPage.jsx deleted file mode 100644 index abdbc23..0000000 --- a/src/components/ResetPasswordPage/ResetPasswordPage.jsx +++ /dev/null @@ -1,48 +0,0 @@ -import React from "react"; - -const ResetPasswordPage = (props) => ( -
-
-
-
-

{"Reset Password"}

-
-
-
-
-
- -
-
- -
-
- -
-
-
-
-
-
-); - -export default ResetPasswordPage; diff --git a/src/components/ResetPasswordPage/ResetPasswordPage.tsx b/src/components/ResetPasswordPage/ResetPasswordPage.tsx new file mode 100644 index 0000000..0144681 --- /dev/null +++ b/src/components/ResetPasswordPage/ResetPasswordPage.tsx @@ -0,0 +1,96 @@ +import { useNavigate, useParams } from "react-router-dom"; +import Spinner from "../Spinner/Spinner"; +import { ToastContainer, toast } from "react-toastify"; +import { useState } from "react"; +import { resetPasswordAPI } from "../../api/user"; +import AlertIcon from "../../icons/AlertIcon"; + +const ResetPasswordPage = () => { + const token = useParams().token!; + const [password, setPassword] = useState(""); + const [verifyPassword, setVerifyPassword] = useState(""); + const [loadingLogin, setLoadingLogin] = useState(false); + const [error, setError] = useState(""); + const navigate = useNavigate(); + + const isSubmitDisabled = password !== verifyPassword; + + const onSubmit = async (e: any) => { + try { + e.preventDefault(); + setLoadingLogin(true); + await toast.promise(resetPasswordAPI(password, token), { + pending: "Resetting password...", + success: "Password Reset", + error: "Error Resetting Password", + }); + setTimeout(() => { + navigate("/"); + }, 1500); + } catch (e) { + console.log("Reset Password Error", e); + setLoadingLogin(false); + setError("Reset Password Failed"); + } + }; + + return ( +
+
+
+
+
+ {!loadingLogin && ( + logo + )} + {loadingLogin && } +
+
+
+

+ Reset password +

+ + setPassword(e.target.value)} + value={password} + /> + setVerifyPassword(e.target.value)} + value={verifyPassword} + /> + +
+ +
+ + {(isSubmitDisabled || error) && ( +
+
+ +

+ {isSubmitDisabled ? "Passwords do not match" : error} +

+
+
+ )} +
+
+
+ +
+ ); +}; + +export default ResetPasswordPage; diff --git a/src/components/ResetPasswordPage/index.jsx b/src/components/ResetPasswordPage/index.jsx deleted file mode 100644 index a207c14..0000000 --- a/src/components/ResetPasswordPage/index.jsx +++ /dev/null @@ -1,107 +0,0 @@ -import React from "react"; -import Swal from "sweetalert2"; -import axios from "../../axiosInterceptor"; -import env from "../../enviroment/envFrontEnd"; -import ResetPasswordPage from "./ResetPasswordPage"; - -class ResetPasswordPageContainer extends React.Component { - - constructor(props) { - super(props); - - this.state = { - password: "", - verifyPassword: "", - passwordChanged: false, - } - } - - passwordOnChange = (e) => { - - const value = e.target.value; - - this.setState(() => { - - return { - ...this.state, - password: value - } - }) - } - - verifyPasswordOnChange = (e) => { - - const value = e.target.value; - - this.setState(() => { - - return { - ...this.state, - verifyPassword : value - } - }) - } - - resetPassword = (e) => { - - e.preventDefault() - - if (this.state.passwordChanged) { - - window.location.assign(env.url); - return; - } - - const id = this.props.match.params.id; - - if (this.state.password === this.state.verifyPassword) { - - const data = { - passwordToken: id, - password: this.state.password - } - - axios.post("/user-service/reset-password", data).then((response) => { - - Swal.fire( - 'Password Successfully Reset', - 'You Have Successfully Resetted Your Password, All Sessions Have Been Logged Out.', - 'success' - ) - - this.setState(() => { - return { - ...this.state, - passwordChanged: true - } - }) - - }).catch((err) => { - console.log("reset password err", err); - Swal.fire({ - icon: 'error', - title: 'Error', - text: 'Passwords Reset Error', - }) - }); - - } else { - Swal.fire({ - icon: 'error', - title: 'Error', - text: 'Passwords do not match', - }) - } - } - - render() { - return - } -} - -export default ResetPasswordPageContainer; \ No newline at end of file diff --git a/src/components/SettingsPage/SettingsAccountSection.tsx b/src/components/SettingsPage/SettingsAccountSection.tsx index 56f8ed2..d76d1f0 100644 --- a/src/components/SettingsPage/SettingsAccountSection.tsx +++ b/src/components/SettingsPage/SettingsAccountSection.tsx @@ -1,7 +1,7 @@ -import React, { useState } from "react"; +import React, { useRef, useState } from "react"; import SettingsChangePasswordPopup from "./SettingsChangePasswordPopup"; import { toast } from "react-toastify"; -import { logoutAPI } from "../../api/user"; +import { logoutAPI, resendVerifyEmailAPI } from "../../api/user"; import Swal from "sweetalert2"; import { useNavigate } from "react-router-dom"; @@ -9,11 +9,17 @@ interface SettingsPageAccountProps { user: { _id: string; email: string; + emailVerified: boolean; }; + getUser: () => void; } -const SettingsPageAccount: React.FC = ({ user }) => { +const SettingsPageAccount: React.FC = ({ + user, + getUser, +}) => { const [showChangePasswordPopup, setShowChangePasswordPopup] = useState(false); + const lastSentEmailVerifiation = useRef(0); const navigate = useNavigate(); const logoutClick = async () => { @@ -59,7 +65,7 @@ const SettingsPageAccount: React.FC = ({ user }) => { await toast.promise(logoutAPI(), { pending: "Logging out all...", success: "Logged out all", - error: "Error Logging Out Al", + error: "Error Logging Out all", }); window.localStorage.removeItem("hasPreviouslyLoggedIn"); @@ -70,6 +76,32 @@ const SettingsPageAccount: React.FC = ({ user }) => { } }; + const resendEmailVerification = async () => { + try { + const currentDate = Date.now(); + if (currentDate - lastSentEmailVerifiation.current < 1000 * 60 * 1) { + await Swal.fire({ + title: "Please wait 1 minute before resending", + icon: "warning", + confirmButtonColor: "#3085d6", + confirmButtonText: "Okay", + }); + return; + } + lastSentEmailVerifiation.current = Date.now(); + + await toast.promise(resendVerifyEmailAPI(), { + pending: "Resending email verification...", + success: "Email Verification Resent", + error: "Error Resending Email Verification", + }); + + getUser(); + } catch (e) { + console.log("Error resending email verification", e); + } + }; + return (
{showChangePasswordPopup && ( @@ -86,6 +118,21 @@ const SettingsPageAccount: React.FC = ({ user }) => {

Email

{user.email}

+ {"emailVerified" in user && ( +
+

+ {user.emailVerified ? "Email verified" : "Email unverified"} +

+ {!user.emailVerified && ( + + )} +
+ )}

Change password

- {tab === "account" && } + {tab === "account" && ( + + )} {tab === "general" && }
)} diff --git a/src/components/VerifyEmailPage/VerifyEmailPage.jsx b/src/components/VerifyEmailPage/VerifyEmailPage.jsx deleted file mode 100644 index d4cc9dc..0000000 --- a/src/components/VerifyEmailPage/VerifyEmailPage.jsx +++ /dev/null @@ -1,9 +0,0 @@ -import React from "react"; - -const VerifyEmailPage = (props) => ( -
-

{props.state.error ? "Verification Error" : props.state.verified ? "Email Verified" : "Verifying Email..."}

-
-) - -export default VerifyEmailPage; \ No newline at end of file diff --git a/src/components/VerifyEmailPage/VerifyEmailPage.tsx b/src/components/VerifyEmailPage/VerifyEmailPage.tsx new file mode 100644 index 0000000..fdf258a --- /dev/null +++ b/src/components/VerifyEmailPage/VerifyEmailPage.tsx @@ -0,0 +1,37 @@ +import { useParams } from "react-router-dom"; +import { verifyEmailAPI } from "../../api/user"; +import { toast, ToastContainer } from "react-toastify"; +import { useEffect } from "react"; + +const VerifyEmailPage = () => { + const token = useParams().token!; + + const verifyEmail = async () => { + try { + await toast.promise(verifyEmailAPI(token), { + pending: "Verifying email...", + success: "Email Verified", + error: "Error verifying email", + }); + + setTimeout(() => { + window.location.assign("/"); + }, 1500); + } catch (e) { + console.log("Error verifying email", e); + } + }; + + useEffect(() => { + verifyEmail(); + }, []); + + return ( +
+

Verifying email...

+ +
+ ); +}; + +export default VerifyEmailPage; diff --git a/src/components/VerifyEmailPage/index.jsx b/src/components/VerifyEmailPage/index.jsx deleted file mode 100644 index 222814e..0000000 --- a/src/components/VerifyEmailPage/index.jsx +++ /dev/null @@ -1,73 +0,0 @@ -import React from "react"; -import axios from "../../axiosInterceptor"; -import Swal from "sweetalert2"; -import VerifyEmailPage from "./VerifyEmailPage"; - -class VerifiyEmailPageContainer extends React.Component { - - constructor(props) { - super(props); - - this.state = { - verified: false, - error: false, - } - } - - - componentDidMount = () => { - this.checkEmailVerification(); - } - - checkEmailVerification = () => { - - const id = this.props.match.params.id - - if (id) { - - const data = { - emailToken: id - } - - axios.post("/user-service/verify-email", data).then((response) => { - - this.setState(() => { - return { - ...this.state, - verified: true, - error: false - } - }) - - Swal.fire( - 'Email Address Verified', - 'You Have Successfully verified your email address, you may now log into myDrive.', - 'success' - ) - - }).catch((err) => { - - this.setState(() => { - return { - ...this.state, - error: true - } - }) - - Swal.fire( - 'Email Address Verifation Error', - 'Could Not Verifiy Email Address', - 'error' - ) - }) - } - } - - render() { - - return - } -} - -export default VerifiyEmailPageContainer; \ No newline at end of file diff --git a/src/routers/AppRouter.jsx b/src/routers/AppRouter.jsx index 5c85bd5..293ace9 100755 --- a/src/routers/AppRouter.jsx +++ b/src/routers/AppRouter.jsx @@ -11,9 +11,9 @@ import HomePage from "../components/Homepage/Homepage"; import PrivateRoute from "./PrivateRoute"; import PublicRoute from "./PublicRoute"; import DownloadPage from "../components/DownloadPage/DownloadPage"; -import VerifyEmailPage from "../components/VerifyEmailPage"; +import VerifyEmailPage from "../components/VerifyEmailPage/VerifyEmailPage"; import uuid from "uuid"; -import ResetPasswordPage from "../components/ResetPasswordPage"; +import ResetPasswordPage from "../components/ResetPasswordPage/ResetPasswordPage"; import SettingsPage from "../components/SettingsPage/SettingsPage"; import Homepage from "../components/Homepage/Homepage"; import { usePreferenceSetter } from "../hooks/preferenceSetter"; @@ -95,8 +95,8 @@ const AppRouter = () => { } /> - } /> - } /> + } /> + } />