From 03088080e9626e5c978b5d9e2f62f2512aa793b4 Mon Sep 17 00:00:00 2001 From: Aaron William Po Date: Fri, 16 Feb 2024 02:40:35 -0500 Subject: [PATCH] Update auth requests --- src/components/Account/AccountInfo.tsx | 6 +- src/components/Account/Security.tsx | 3 +- src/components/Login/LoginForm.tsx | 2 +- src/components/RegisterUserForm.tsx | 2 +- src/components/UserPage/UserFollowButton.tsx | 5 +- src/pages/users/forgot-password.tsx | 5 +- src/requests/users/auth/index.ts | 169 ++++++++++++++++++ .../users/auth/sendEditUserRequest.ts | 35 ---- .../users/auth/sendForgotPasswordRequest.ts | 24 --- .../users/auth/sendLoginUserRequest.ts | 31 ---- .../users/auth/sendRegisterUserRequest.ts | 33 ---- .../users/auth/sendUpdatePasswordRequest.ts | 26 --- .../users/auth/sendUserFollowRequest.ts | 16 -- src/requests/users/auth/types/index.ts | 41 +++++ .../users/auth/validateEmailRequest.ts | 25 --- .../schema/CreateUserValidationSchemas.ts | 4 +- 16 files changed, 225 insertions(+), 202 deletions(-) create mode 100644 src/requests/users/auth/index.ts delete mode 100644 src/requests/users/auth/sendEditUserRequest.ts delete mode 100644 src/requests/users/auth/sendForgotPasswordRequest.ts delete mode 100644 src/requests/users/auth/sendLoginUserRequest.ts delete mode 100644 src/requests/users/auth/sendRegisterUserRequest.ts delete mode 100644 src/requests/users/auth/sendUpdatePasswordRequest.ts delete mode 100644 src/requests/users/auth/sendUserFollowRequest.ts create mode 100644 src/requests/users/auth/types/index.ts delete mode 100644 src/requests/users/auth/validateEmailRequest.ts diff --git a/src/components/Account/AccountInfo.tsx b/src/components/Account/AccountInfo.tsx index 9eaf183..35b0a82 100644 --- a/src/components/Account/AccountInfo.tsx +++ b/src/components/Account/AccountInfo.tsx @@ -1,4 +1,3 @@ -import validateEmailRequest from '@/requests/users/auth/validateEmailRequest'; import validateUsernameRequest from '@/requests/users/profile/validateUsernameRequest'; import { BaseCreateUserSchema } from '@/services/users/auth/schema/CreateUserValidationSchemas'; import { Switch } from '@headlessui/react'; @@ -7,7 +6,7 @@ import { Dispatch, FC, useContext } from 'react'; import { useForm } from 'react-hook-form'; import { z } from 'zod'; import UserContext from '@/contexts/UserContext'; -import sendEditUserRequest from '@/requests/users/auth/sendEditUserRequest'; + import createErrorToast from '@/util/createErrorToast'; import { toast } from 'react-hot-toast'; import { AccountPageAction, AccountPageState } from '@/reducers/accountPageReducer'; @@ -15,6 +14,7 @@ import FormError from '../ui/forms/FormError'; import FormInfo from '../ui/forms/FormInfo'; import FormLabel from '../ui/forms/FormLabel'; import FormTextInput from '../ui/forms/FormTextInput'; +import { sendEditUserRequest, validateEmailRequest } from '@/requests/users/auth'; interface AccountInfoProps { pageState: AccountPageState; @@ -36,7 +36,7 @@ const AccountInfo: FC = ({ pageState, dispatch }) => { .refine( async (email) => { if (user!.email === email) return true; - return validateEmailRequest(email); + return validateEmailRequest({ email }); }, { message: 'Email is already taken.' }, ), diff --git a/src/components/Account/Security.tsx b/src/components/Account/Security.tsx index 7ed1bbf..e363595 100644 --- a/src/components/Account/Security.tsx +++ b/src/components/Account/Security.tsx @@ -4,10 +4,11 @@ import { SubmitHandler, useForm } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; import { z } from 'zod'; import { UpdatePasswordSchema } from '@/services/users/auth/schema/CreateUserValidationSchemas'; -import sendUpdatePasswordRequest from '@/requests/users/auth/sendUpdatePasswordRequest'; + import { AccountPageState, AccountPageAction } from '@/reducers/accountPageReducer'; import toast from 'react-hot-toast'; import createErrorToast from '@/util/createErrorToast'; +import { sendUpdatePasswordRequest } from '@/requests/users/auth'; import FormError from '../ui/forms/FormError'; import FormInfo from '../ui/forms/FormInfo'; import FormLabel from '../ui/forms/FormLabel'; diff --git a/src/components/Login/LoginForm.tsx b/src/components/Login/LoginForm.tsx index f79bc0d..df2ecb9 100644 --- a/src/components/Login/LoginForm.tsx +++ b/src/components/Login/LoginForm.tsx @@ -1,4 +1,3 @@ -import sendLoginUserRequest from '@/requests/users/auth/sendLoginUserRequest'; import LoginValidationSchema from '@/services/users/auth/schema/LoginValidationSchema'; import { zodResolver } from '@hookform/resolvers/zod'; import { useRouter } from 'next/router'; @@ -15,6 +14,7 @@ import FormLabel from '../ui/forms/FormLabel'; import FormSegment from '../ui/forms/FormSegment'; import FormTextInput from '../ui/forms/FormTextInput'; import Button from '../ui/forms/Button'; +import { sendLoginUserRequest } from '@/requests/users/auth'; type LoginT = z.infer; const LoginForm = () => { diff --git a/src/components/RegisterUserForm.tsx b/src/components/RegisterUserForm.tsx index 4970bbb..b126d45 100644 --- a/src/components/RegisterUserForm.tsx +++ b/src/components/RegisterUserForm.tsx @@ -1,4 +1,3 @@ -import sendRegisterUserRequest from '@/requests/users/auth/sendRegisterUserRequest'; import { CreateUserValidationSchemaWithUsernameAndEmailCheck } from '@/services/users/auth/schema/CreateUserValidationSchemas'; import { zodResolver } from '@hookform/resolvers/zod'; import { useRouter } from 'next/router'; @@ -9,6 +8,7 @@ import { z } from 'zod'; import createErrorToast from '@/util/createErrorToast'; import toast from 'react-hot-toast'; +import { sendRegisterUserRequest } from '@/requests/users/auth'; import Button from './ui/forms/Button'; import FormError from './ui/forms/FormError'; import FormInfo from './ui/forms/FormInfo'; diff --git a/src/components/UserPage/UserFollowButton.tsx b/src/components/UserPage/UserFollowButton.tsx index 0b0166c..f2eb28e 100644 --- a/src/components/UserPage/UserFollowButton.tsx +++ b/src/components/UserPage/UserFollowButton.tsx @@ -1,7 +1,8 @@ import useFollowStatus from '@/hooks/data-fetching/user-follows/useFollowStatus'; import useGetUsersFollowedByUser from '@/hooks/data-fetching/user-follows/useGetUsersFollowedByUser'; import useGetUsersFollowingUser from '@/hooks/data-fetching/user-follows/useGetUsersFollowingUser'; -import sendUserFollowRequest from '@/requests/users/auth/sendUserFollowRequest'; +import { sendUserFollowRequest } from '@/requests/users/auth'; + import GetUserSchema from '@/services/users/auth/schema/GetUserSchema'; import { FC, useState } from 'react'; import { FaUserCheck, FaUserPlus } from 'react-icons/fa'; @@ -25,7 +26,7 @@ const UserFollowButton: FC = ({ const onClick = async () => { try { setIsLoading(true); - await sendUserFollowRequest(user.id); + await sendUserFollowRequest({ userId: user.id }); await Promise.all([ mutateFollowStatus(), mutateFollowerCount(), diff --git a/src/pages/users/forgot-password.tsx b/src/pages/users/forgot-password.tsx index d27ca0a..98a914c 100644 --- a/src/pages/users/forgot-password.tsx +++ b/src/pages/users/forgot-password.tsx @@ -12,8 +12,9 @@ import { NextPage } from 'next'; import { SubmitHandler, useForm } from 'react-hook-form'; import toast from 'react-hot-toast'; import { useRouter } from 'next/router'; -import sendForgotPasswordRequest from '@/requests/users/auth/sendForgotPasswordRequest'; + import { FaUserCircle } from 'react-icons/fa'; +import { sendForgotPasswordRequest } from '@/requests/users/auth'; interface ForgotPasswordPageProps {} @@ -29,7 +30,7 @@ const ForgotPasswordPage: NextPage = () => { const onSubmit: SubmitHandler<{ email: string }> = async (data) => { try { const loadingToast = toast.loading('Sending reset link...'); - await sendForgotPasswordRequest(data.email); + await sendForgotPasswordRequest({ email: data.email }); reset(); toast.dismiss(loadingToast); toast.success('Password reset link sent!'); diff --git a/src/requests/users/auth/index.ts b/src/requests/users/auth/index.ts new file mode 100644 index 0000000..7b8c76a --- /dev/null +++ b/src/requests/users/auth/index.ts @@ -0,0 +1,169 @@ +import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema'; +import { BasicUserInfoSchema } from '@/config/auth/types'; +import GetUserSchema from '@/services/users/auth/schema/GetUserSchema'; +import type { + SendEditUserRequest, + SendForgotPasswordRequest, + SendLoginUserRequest, + SendRegisterUserRequest, + SendUpdatePasswordRequest, + SendUserFollowRequest, + ValidateEmailRequest, +} from './types'; +import { z } from 'zod'; + +export const sendEditUserRequest: SendEditUserRequest = async ({ user, data }) => { + const response = await fetch(`/api/users/${user!.id}`, { + body: JSON.stringify(data), + method: 'PUT', + headers: { 'Content-Type': 'application/json' }, + }); + if (!response.ok) { + throw new Error(response.statusText); + } + + const json = await response.json(); + + const parsed = APIResponseValidationSchema.safeParse(json); + if (!parsed.success) { + throw new Error('API response validation failed.'); + } + + return parsed.data; +}; + +export const sendForgotPasswordRequest: SendForgotPasswordRequest = async ({ email }) => { + const response = await fetch('/api/users/forgot-password', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ email }), + }); + + if (!response.ok) { + throw new Error("Something went wrong and we couldn't send the reset link."); + } + + const json = await response.json(); + const parsed = APIResponseValidationSchema.safeParse(json); + + if (!parsed.success) { + throw new Error(parsed.error.message); + } + + return parsed.data; +}; + +export const sendLoginUserRequest: SendLoginUserRequest = async ({ + username, + password, +}) => { + const response = await fetch('/api/users/login', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + username, + password, + }), + }); + + const json: unknown = await response.json(); + + const parsed = APIResponseValidationSchema.safeParse(json); + if (!parsed.success) { + throw new Error('API response validation failed'); + } + + if (!parsed.data.success) { + throw new Error(parsed.data.message); + } + const parsedPayload = BasicUserInfoSchema.safeParse(parsed.data.payload); + if (!parsedPayload.success) { + throw new Error('API response payload validation failed'); + } + + return parsedPayload.data; +}; + +export const sendRegisterUserRequest: SendRegisterUserRequest = async (data) => { + const response = await fetch('/api/users/register', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(data), + }); + + const json = await response.json(); + const parsed = APIResponseValidationSchema.safeParse(json); + + if (!parsed.success) { + throw new Error('API response validation failed.'); + } + + if (!parsed.data.success) { + throw new Error(parsed.data.message); + } + + const parsedPayload = GetUserSchema.safeParse(parsed.data.payload); + + if (!parsedPayload.success) { + throw new Error('API response payload validation failed.'); + } + + return parsedPayload.data; +}; + +export const sendUpdatePasswordRequest: SendUpdatePasswordRequest = async (data) => { + const response = await fetch('/api/users/edit-password', { + body: JSON.stringify(data), + method: 'PUT', + headers: { 'Content-Type': 'application/json' }, + }); + + if (!response.ok) { + throw new Error(response.statusText); + } + const json = await response.json(); + + const parsed = APIResponseValidationSchema.safeParse(json); + + if (!parsed.success) { + throw new Error('API response validation failed.'); + } + + return parsed.data; +}; + +export const sendUserFollowRequest: SendUserFollowRequest = async ({ userId }) => { + const response = await fetch(`/api/users/${userId}/follow-user`, { method: 'POST' }); + const json = await response.json(); + + const parsed = APIResponseValidationSchema.safeParse(json); + + if (!parsed.success) { + throw new Error('Invalid API response.'); + } + + return parsed.data; +}; + +export const validateEmailRequest: ValidateEmailRequest = async ({ email }) => { + const response = await fetch(`/api/users/check-email?email=${email}`); + const json = await response.json(); + + const parsed = APIResponseValidationSchema.safeParse(json); + + if (!parsed.success) { + return false; + } + + const parsedPayload = z + .object({ emailIsTaken: z.boolean() }) + .safeParse(parsed.data.payload); + + if (!parsedPayload.success) { + return false; + } + + return !parsedPayload.data.emailIsTaken; +}; diff --git a/src/requests/users/auth/sendEditUserRequest.ts b/src/requests/users/auth/sendEditUserRequest.ts deleted file mode 100644 index 1ff9feb..0000000 --- a/src/requests/users/auth/sendEditUserRequest.ts +++ /dev/null @@ -1,35 +0,0 @@ -import GetUserSchema from '@/services/users/auth/schema/GetUserSchema'; -import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema'; -import { z } from 'zod'; - -interface SendEditUserRequestArgs { - user: z.infer; - data: { - username: string; - email: string; - firstName: string; - lastName: string; - }; -} - -const sendEditUserRequest = async ({ user, data }: SendEditUserRequestArgs) => { - const response = await fetch(`/api/users/${user!.id}`, { - body: JSON.stringify(data), - method: 'PUT', - headers: { 'Content-Type': 'application/json' }, - }); - if (!response.ok) { - throw new Error(response.statusText); - } - - const json = await response.json(); - - const parsed = APIResponseValidationSchema.safeParse(json); - if (!parsed.success) { - throw new Error('API response validation failed.'); - } - - return parsed; -}; - -export default sendEditUserRequest; diff --git a/src/requests/users/auth/sendForgotPasswordRequest.ts b/src/requests/users/auth/sendForgotPasswordRequest.ts deleted file mode 100644 index 8bbf962..0000000 --- a/src/requests/users/auth/sendForgotPasswordRequest.ts +++ /dev/null @@ -1,24 +0,0 @@ -import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema'; - -const sendForgotPasswordRequest = async (email: string) => { - const response = await fetch('/api/users/forgot-password', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ email }), - }); - - if (!response.ok) { - throw new Error("Something went wrong and we couldn't send the reset link."); - } - - const json = await response.json(); - const parsed = APIResponseValidationSchema.safeParse(json); - - if (!parsed.success) { - throw new Error(parsed.error.message); - } - - return parsed.data; -}; - -export default sendForgotPasswordRequest; diff --git a/src/requests/users/auth/sendLoginUserRequest.ts b/src/requests/users/auth/sendLoginUserRequest.ts deleted file mode 100644 index 7be9e64..0000000 --- a/src/requests/users/auth/sendLoginUserRequest.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { BasicUserInfoSchema } from '@/config/auth/types'; -import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema'; - -const sendLoginUserRequest = async (data: { username: string; password: string }) => { - const response = await fetch('/api/users/login', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify(data), - }); - - const json: unknown = await response.json(); - - const parsed = APIResponseValidationSchema.safeParse(json); - if (!parsed.success) { - throw new Error('API response validation failed'); - } - - if (!parsed.data.success) { - throw new Error(parsed.data.message); - } - const parsedPayload = BasicUserInfoSchema.safeParse(parsed.data.payload); - if (!parsedPayload.success) { - throw new Error('API response payload validation failed'); - } - - return parsedPayload.data; -}; - -export default sendLoginUserRequest; diff --git a/src/requests/users/auth/sendRegisterUserRequest.ts b/src/requests/users/auth/sendRegisterUserRequest.ts deleted file mode 100644 index 3cb6c11..0000000 --- a/src/requests/users/auth/sendRegisterUserRequest.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { CreateUserValidationSchema } from '@/services/users/auth/schema/CreateUserValidationSchemas'; -import GetUserSchema from '@/services/users/auth/schema/GetUserSchema'; -import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema'; -import { z } from 'zod'; - -async function sendRegisterUserRequest(data: z.infer) { - const response = await fetch('/api/users/register', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(data), - }); - - const json = await response.json(); - const parsed = APIResponseValidationSchema.safeParse(json); - - if (!parsed.success) { - throw new Error('API response validation failed.'); - } - - if (!parsed.data.success) { - throw new Error(parsed.data.message); - } - - const parsedPayload = GetUserSchema.safeParse(parsed.data.payload); - - if (!parsedPayload.success) { - throw new Error('API response payload validation failed.'); - } - - return parsedPayload.data; -} - -export default sendRegisterUserRequest; diff --git a/src/requests/users/auth/sendUpdatePasswordRequest.ts b/src/requests/users/auth/sendUpdatePasswordRequest.ts deleted file mode 100644 index d565c30..0000000 --- a/src/requests/users/auth/sendUpdatePasswordRequest.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { UpdatePasswordSchema } from '@/services/users/auth/schema/CreateUserValidationSchemas'; -import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema'; -import { z } from 'zod'; - -const sendUpdatePasswordRequest = async (data: z.infer) => { - const response = await fetch('/api/users/edit-password', { - body: JSON.stringify(data), - method: 'PUT', - headers: { 'Content-Type': 'application/json' }, - }); - - if (!response.ok) { - throw new Error(response.statusText); - } - const json = await response.json(); - - const parsed = APIResponseValidationSchema.safeParse(json); - - if (!parsed.success) { - throw new Error('API response validation failed.'); - } - - return parsed.data; -}; - -export default sendUpdatePasswordRequest; diff --git a/src/requests/users/auth/sendUserFollowRequest.ts b/src/requests/users/auth/sendUserFollowRequest.ts deleted file mode 100644 index 30f1347..0000000 --- a/src/requests/users/auth/sendUserFollowRequest.ts +++ /dev/null @@ -1,16 +0,0 @@ -import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema'; - -const sendUserFollowRequest = async (userId: string) => { - const response = await fetch(`/api/users/${userId}/follow-user`, { method: 'POST' }); - const json = await response.json(); - - const parsed = APIResponseValidationSchema.safeParse(json); - - if (!parsed.success) { - throw new Error('Invalid API response.'); - } - - return parsed; -}; - -export default sendUserFollowRequest; diff --git a/src/requests/users/auth/types/index.ts b/src/requests/users/auth/types/index.ts new file mode 100644 index 0000000..c4d1f9c --- /dev/null +++ b/src/requests/users/auth/types/index.ts @@ -0,0 +1,41 @@ +import { BasicUserInfoSchema } from '@/config/auth/types'; +import { + CreateUserValidationSchema, + UpdatePasswordSchema, +} from '@/services/users/auth/schema/CreateUserValidationSchemas'; +import GetUserSchema from '@/services/users/auth/schema/GetUserSchema'; +import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema'; +import { z } from 'zod'; + +export type SendEditUserRequest = (args: { + user: z.infer; + data: { + username: string; + email: string; + firstName: string; + lastName: string; + }; +}) => Promise>; + +export type SendForgotPasswordRequest = (args: { + email: string; +}) => Promise>; + +export type SendLoginUserRequest = (args: { + username: string; + password: string; +}) => Promise>; + +export type SendRegisterUserRequest = ( + args: z.infer, +) => Promise>; + +export type SendUpdatePasswordRequest = ( + args: z.infer, +) => Promise>; + +export type SendUserFollowRequest = (args: { + userId: string; +}) => Promise>; + +export type ValidateEmailRequest = (args: { email: string }) => Promise; diff --git a/src/requests/users/auth/validateEmailRequest.ts b/src/requests/users/auth/validateEmailRequest.ts deleted file mode 100644 index 4e87291..0000000 --- a/src/requests/users/auth/validateEmailRequest.ts +++ /dev/null @@ -1,25 +0,0 @@ -import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema'; -import { z } from 'zod'; - -const validateEmailRequest = async (email: string) => { - const response = await fetch(`/api/users/check-email?email=${email}`); - const json = await response.json(); - - const parsed = APIResponseValidationSchema.safeParse(json); - - if (!parsed.success) { - return false; - } - - const parsedPayload = z - .object({ emailIsTaken: z.boolean() }) - .safeParse(parsed.data.payload); - - if (!parsedPayload.success) { - return false; - } - - return !parsedPayload.data.emailIsTaken; -}; - -export default validateEmailRequest; diff --git a/src/services/users/auth/schema/CreateUserValidationSchemas.ts b/src/services/users/auth/schema/CreateUserValidationSchemas.ts index 38aae9e..ab3c1bc 100644 --- a/src/services/users/auth/schema/CreateUserValidationSchemas.ts +++ b/src/services/users/auth/schema/CreateUserValidationSchemas.ts @@ -1,4 +1,4 @@ -import validateEmailRequest from '@/requests/users/auth/validateEmailRequest'; +import { validateEmailRequest } from '@/requests/users/auth'; import validateUsernameRequest from '@/requests/users/profile/validateUsernameRequest'; import sub from 'date-fns/sub'; import { z } from 'zod'; @@ -60,7 +60,7 @@ export const CreateUserValidationSchemaWithUsernameAndEmailCheck = email: z .string() .email({ message: 'Email must be a valid email address.' }) - .refine(async (email) => validateEmailRequest(email), { + .refine(async (email) => validateEmailRequest({ email }), { message: 'Email is already taken.', }), username: z