From 2cdc8f1a88186005301452f9c455e2e56f00c50c Mon Sep 17 00:00:00 2001 From: Woo Jia Hao Date: Fri, 15 Mar 2024 15:11:26 +0800 Subject: [PATCH] [web] support email verification --- web/src/api/auth/auth.api.ts | 19 +++++++++++++++++++ web/src/api/users/user.ts | 1 + web/src/components/Layout.tsx | 2 ++ web/src/main.tsx | 5 +++++ web/src/pages/Register.tsx | 9 ++------- web/src/pages/VerifyAuth.tsx | 31 +++++++++++++++++++++++++++++++ web/src/utilities/navigate.ts | 16 ++++++++++++++++ 7 files changed, 76 insertions(+), 7 deletions(-) create mode 100644 web/src/pages/VerifyAuth.tsx create mode 100644 web/src/utilities/navigate.ts diff --git a/web/src/api/auth/auth.api.ts b/web/src/api/auth/auth.api.ts index a6c83bd..9e2e658 100644 --- a/web/src/api/auth/auth.api.ts +++ b/web/src/api/auth/auth.api.ts @@ -49,4 +49,23 @@ export async function logoutUser() { export async function refreshToken() { const tempApi = generateApi(); await tempApi.post('/auth/refresh'); +} + +export async function verify(token: string) { + try { + await api.post('/auth/verify', { + token + }) + + return null; + } catch (e) { + const resp = (e as AxiosError).response; + if (!resp) { + return 'Something went wrong'; + } + + const data = resp.data as { message: string }; + + return data.message + } } \ No newline at end of file diff --git a/web/src/api/users/user.ts b/web/src/api/users/user.ts index 844abdb..5417b5f 100644 --- a/web/src/api/users/user.ts +++ b/web/src/api/users/user.ts @@ -2,4 +2,5 @@ export interface User { id: string; email: string; username: string; + is_verified: boolean; } \ No newline at end of file diff --git a/web/src/components/Layout.tsx b/web/src/components/Layout.tsx index 51551d8..6d8df2e 100644 --- a/web/src/components/Layout.tsx +++ b/web/src/components/Layout.tsx @@ -6,6 +6,8 @@ interface LayoutProps extends React.PropsWithChildren { hasToast?: boolean; } +export type ToastMessageType = 'error' | 'success' | 'notification'; + export default function Layout({ hasToast = false, children }: LayoutProps) { const [searchParams] = useSearchParams(); diff --git a/web/src/main.tsx b/web/src/main.tsx index c8e2e11..c9ee145 100644 --- a/web/src/main.tsx +++ b/web/src/main.tsx @@ -9,6 +9,7 @@ import { import Login from './pages/Login.tsx' import { UserProvider } from './contexts/userContext.tsx' import Register from './pages/Register.tsx' +import VerifyAuth from './pages/VerifyAuth.tsx' const router = createBrowserRouter([ { @@ -22,6 +23,10 @@ const router = createBrowserRouter([ { path: '/register', element: + }, + { + path: '/auth/verify', + element: } ]) diff --git a/web/src/pages/Register.tsx b/web/src/pages/Register.tsx index d92a2d6..ce2d042 100644 --- a/web/src/pages/Register.tsx +++ b/web/src/pages/Register.tsx @@ -4,6 +4,7 @@ import { RegisterError } from "../api/auth/auth.api"; import Layout from "../components/Layout"; import UnprotectedRoute from "../components/UnprotectedRoute"; import { UserContext } from "../contexts/userContext"; +import { navigateWithToast } from '../utilities/navigate'; export default function Register() { const [errorCode, setErrorCode] = useState(null); @@ -31,13 +32,7 @@ export default function Register() { const status = await register(email, username, password); if (!status) { - navigate({ - pathname: '/login', - search: createSearchParams({ - 'message': 'Account created successfully! Login to your new account', - 'message-type': 'notification' - }).toString() - }) + navigateWithToast(navigate, '/login', 'Account created successfully! Verify your account before logging in. Check your email for further instructions.', 'notification'); } else { const [errorCode, errorMessage] = status; setErrorCode(errorCode); diff --git a/web/src/pages/VerifyAuth.tsx b/web/src/pages/VerifyAuth.tsx new file mode 100644 index 0000000..ace98eb --- /dev/null +++ b/web/src/pages/VerifyAuth.tsx @@ -0,0 +1,31 @@ +import { useEffect } from "react"; +import { useNavigate, useSearchParams } from "react-router-dom"; +import { verify } from "../api/auth/auth.api"; +import { navigateWithToast } from "../utilities/navigate"; + +export default function VerifyAuth() { + const [searchParams] = useSearchParams(); + const navigate = useNavigate(); + + useEffect(() => { + (async () => { + const token = searchParams.get('token'); + + if (token) { + // Perform verification + const result = await verify(token); + if (!result) { + navigateWithToast(navigate, '/login', 'Verified your account. You can login now!', 'success'); + } else { + navigateWithToast(navigate, '/login', `Failed to verify your account because: ${result}`, 'error'); + } + } else { + navigateWithToast(navigate, '/login', 'Invalid auth', 'error'); + } + })(); + }, []) + + return ( +
Verifying your token...
+ ) +} \ No newline at end of file diff --git a/web/src/utilities/navigate.ts b/web/src/utilities/navigate.ts new file mode 100644 index 0000000..f411c9e --- /dev/null +++ b/web/src/utilities/navigate.ts @@ -0,0 +1,16 @@ +import { ToastMessageType } from "../components/Layout"; +import { NavigateFunction, createSearchParams } from "react-router-dom"; + +export function navigateWithToast( + navigate: NavigateFunction, + to: string, + message: string, + messageType: ToastMessageType) { + navigate({ + pathname: to, + search: createSearchParams({ + 'message': message, + 'message-type': messageType, + }).toString() + }); +} \ No newline at end of file