From 0c08ca33fd95f20fd7f4358b9574972a320d9283 Mon Sep 17 00:00:00 2001 From: aXenDeveloper Date: Fri, 22 Nov 2024 23:40:56 +0100 Subject: [PATCH] fix(frontend): Handle errors from server functions on production --- apps/frontend/DockerFile | 2 +- .../templates/docker/apps/frontend/DockerFile | 2 +- .../templates/docker/deploy.sh | 2 +- .../src/hooks/sign/in/mutation-api.ts | 53 +++++++---- .../hooks/sign/in/use-sign-in-admin-view.ts | 22 ++--- .../src/hooks/sign/in/use-sign-in-view.ts | 31 +++---- .../src/hooks/sign/up/mutation-api.ts | 33 +++++-- .../src/hooks/sign/up/use-sign-up-view.ts | 89 +++++++++---------- .../members/create/hooks/mutation-api.ts | 32 +++++-- .../create/hooks/use-create-user-admin.ts | 74 +++++++-------- .../views/members/user/actions/edit/edit.tsx | 33 +++---- .../members/user/actions/edit/mutation-api.ts | 24 +++-- 12 files changed, 223 insertions(+), 174 deletions(-) diff --git a/apps/frontend/DockerFile b/apps/frontend/DockerFile index abd0a05b9..d6181360e 100644 --- a/apps/frontend/DockerFile +++ b/apps/frontend/DockerFile @@ -12,7 +12,7 @@ RUN turbo prune frontend --docker FROM base AS installer WORKDIR /app COPY --from=builder /app/out/json/ . -RUN pnpm i +RUN pnpm i --ignore-scripts COPY --from=builder /app/out/full/ . COPY --from=builder /app/.prettierrc.mjs ./ diff --git a/packages/create-vitnode-app/templates/docker/apps/frontend/DockerFile b/packages/create-vitnode-app/templates/docker/apps/frontend/DockerFile index abd0a05b9..d6181360e 100644 --- a/packages/create-vitnode-app/templates/docker/apps/frontend/DockerFile +++ b/packages/create-vitnode-app/templates/docker/apps/frontend/DockerFile @@ -12,7 +12,7 @@ RUN turbo prune frontend --docker FROM base AS installer WORKDIR /app COPY --from=builder /app/out/json/ . -RUN pnpm i +RUN pnpm i --ignore-scripts COPY --from=builder /app/out/full/ . COPY --from=builder /app/.prettierrc.mjs ./ diff --git a/packages/create-vitnode-app/templates/docker/deploy.sh b/packages/create-vitnode-app/templates/docker/deploy.sh index 5c60a4e20..ec65f0ad0 100644 --- a/packages/create-vitnode-app/templates/docker/deploy.sh +++ b/packages/create-vitnode-app/templates/docker/deploy.sh @@ -201,7 +201,7 @@ else echo ".env file already exists. Skipping creation." fi -sudo docker-compose -f ./docker-compose.yml -p vitnode up -d +sudo docker-compose -f ./docker-compose.yml -p vitnode up --build -d sudo chown -R 1001:1001 ./apps/backend/src/plugins/core sudo chown -R 1001:1001 ./apps/backend/uploads diff --git a/packages/frontend/src/hooks/sign/in/mutation-api.ts b/packages/frontend/src/hooks/sign/in/mutation-api.ts index c2a4c7d53..60cd21231 100644 --- a/packages/frontend/src/hooks/sign/in/mutation-api.ts +++ b/packages/frontend/src/hooks/sign/in/mutation-api.ts @@ -8,28 +8,43 @@ import { cookies } from 'next/headers'; import { SignInAuthBody } from 'vitnode-shared/auth/auth.dto'; export const mutationApi = async (body: SignInAuthBody) => { - await fetcher({ - method: 'POST', - url: '/core/auth/sign_in', - body, - }); - - const cookie = await cookies(); - if (body.admin) { - const adminIdFromCookie = await getAdminIdCookie(); - if (adminIdFromCookie) { - revalidateTags.sessionAdmin(+adminIdFromCookie); + try { + await fetcher({ + method: 'POST', + url: '/core/auth/sign_in', + body, + }); + + const cookie = await cookies(); + if (body.admin) { + const adminIdFromCookie = await getAdminIdCookie(); + if (adminIdFromCookie) { + revalidateTags.sessionAdmin(+adminIdFromCookie); + } + + await redirect('/admin/core/dashboard'); + + return; } - await redirect('/admin/core/dashboard'); + const userIdFromCookie = cookie.get('vitnode-user-id')?.value; + if (userIdFromCookie) { + revalidateTags.session(+userIdFromCookie); + } + await redirect('/'); + } catch (err) { + const { message } = err as Error; + if (message === 'NEXT_REDIRECT') { + await redirect('/'); + } + if (message.includes('EMAIL_NOT_VERIFIED')) { + return { message: 'EMAIL_NOT_VERIFIED' }; + } - return; - } + if (message.includes('ACCESS_DENIED')) { + return { message: 'ACCESS_DENIED' }; + } - const userIdFromCookie = cookie.get('vitnode-user-id')?.value; - if (userIdFromCookie) { - revalidateTags.session(+userIdFromCookie); + return { message: 'INTERNAL_SERVER_ERROR' }; } - - await redirect('/'); }; diff --git a/packages/frontend/src/hooks/sign/in/use-sign-in-admin-view.ts b/packages/frontend/src/hooks/sign/in/use-sign-in-admin-view.ts index 220204f85..f7a2819a8 100644 --- a/packages/frontend/src/hooks/sign/in/use-sign-in-admin-view.ts +++ b/packages/frontend/src/hooks/sign/in/use-sign-in-admin-view.ts @@ -15,21 +15,17 @@ export const useSignInAdminView = () => { }); const onSubmit = async (values: z.infer) => { - try { - await mutationApi({ ...values, admin: true }); - } catch (e) { - const { message } = e as Error; - if (message === 'NEXT_REDIRECT') return; - if (message.includes('ACCESS_DENIED')) { - setError('ACCESS_DENIED'); + const mutation = await mutationApi({ ...values, admin: true }); + if (!mutation?.message) return; + if (mutation.message === 'ACCESS_DENIED') { + setError('ACCESS_DENIED'); - return; - } - - toast.error(t('title'), { - description: t('internal_server_error'), - }); + return; } + + toast.error(t('title'), { + description: t('internal_server_error'), + }); }; return { diff --git a/packages/frontend/src/hooks/sign/in/use-sign-in-view.ts b/packages/frontend/src/hooks/sign/in/use-sign-in-view.ts index babd86477..5d0e6d0ad 100644 --- a/packages/frontend/src/hooks/sign/in/use-sign-in-view.ts +++ b/packages/frontend/src/hooks/sign/in/use-sign-in-view.ts @@ -17,28 +17,23 @@ export const useSignInView = () => { const onSubmit = async (values: z.infer) => { setError(''); + const mutation = await mutationApi(values); + if (!mutation?.message) return; + if (mutation.message === 'EMAIL_NOT_VERIFIED') { + setError('EMAIL_NOT_VERIFIED'); - try { - await mutationApi(values); - } catch (e) { - const { message } = e as Error; - if (message === 'NEXT_REDIRECT') return; - if (message.includes('EMAIL_NOT_VERIFIED')) { - setError('EMAIL_NOT_VERIFIED'); - - return; - } - - if (message.includes('ACCESS_DENIED')) { - setError('ACCESS_DENIED'); + return; + } - return; - } + if (mutation.message === 'ACCESS_DENIED') { + setError('ACCESS_DENIED'); - toast.error(t('title'), { - description: t('internal_server_error'), - }); + return; } + + toast.error(t('title'), { + description: t('internal_server_error'), + }); }; return { diff --git a/packages/frontend/src/hooks/sign/up/mutation-api.ts b/packages/frontend/src/hooks/sign/up/mutation-api.ts index 21df894f7..d7996576e 100644 --- a/packages/frontend/src/hooks/sign/up/mutation-api.ts +++ b/packages/frontend/src/hooks/sign/up/mutation-api.ts @@ -8,12 +8,29 @@ export const mutationApi = async ( token: string; } & SignUpAuthBody, ) => { - await fetcher<{ email: string }, SignUpAuthBody>({ - url: '/core/auth/sign_up', - method: 'POST', - body, - headers: { - 'x-vitnode-captcha-token': body.token, - }, - }); + try { + await fetcher<{ email: string }, SignUpAuthBody>({ + url: '/core/auth/sign_up', + method: 'POST', + body, + headers: { + 'x-vitnode-captcha-token': body.token, + }, + }); + } catch (err) { + const { message } = err as Error; + if (message.includes('CAPTCHA_FAILED')) { + return { message: 'CAPTCHA_FAILED' }; + } + + if (message.includes('EMAIL_ALREADY_EXISTS')) { + return { message: 'EMAIL_ALREADY_EXISTS' }; + } + + if (message.includes('NAME_ALREADY_EXISTS')) { + return { message: 'NAME_ALREADY_EXISTS' }; + } + + return { message: 'INTERNAL_SERVER_ERROR' }; + } }; diff --git a/packages/frontend/src/hooks/sign/up/use-sign-up-view.ts b/packages/frontend/src/hooks/sign/up/use-sign-up-view.ts index e74160f43..6d09e3ce8 100644 --- a/packages/frontend/src/hooks/sign/up/use-sign-up-view.ts +++ b/packages/frontend/src/hooks/sign/up/use-sign-up-view.ts @@ -62,59 +62,58 @@ export const useSignUpView = () => { return; } - try { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const { terms, ...rest } = values; - await mutationApi({ - ...rest, - token, - }); - + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { terms, ...rest } = values; + const mutation = await mutationApi({ + ...rest, + token, + }); + if (!mutation?.message) { setEmailSuccess(values.email); - } catch (err) { - const { message } = err as Error; - if (message.includes('CAPTCHA_FAILED')) { - toast.error(tCore('errors.title'), { - description: tCore('errors.captcha_failed'), - }); + return; + } - return; - } + if (mutation.message === 'CAPTCHA_FAILED') { + toast.error(tCore('errors.title'), { + description: tCore('errors.captcha_failed'), + }); - if (message.includes('EMAIL_ALREADY_EXISTS')) { - form.setError( - 'email', - { - type: 'manual', - message: t('email.already_exists'), - }, - { - shouldFocus: true, - }, - ); + return; + } - return; - } - if (message.includes('NAME_ALREADY_EXISTS')) { - form.setError( - 'name', - { - type: 'manual', - message: t('name.already_exists'), - }, - { - shouldFocus: true, - }, - ); + if (mutation.message === 'EMAIL_ALREADY_EXISTS') { + form.setError( + 'email', + { + type: 'manual', + message: t('email.already_exists'), + }, + { + shouldFocus: true, + }, + ); - return; - } + return; + } + if (mutation.message === 'NAME_ALREADY_EXISTS') { + form.setError( + 'name', + { + type: 'manual', + message: t('name.already_exists'), + }, + { + shouldFocus: true, + }, + ); - toast.error(tCore('errors.title'), { - description: tCore('errors.internal_server_error'), - }); + return; } + + toast.error(tCore('errors.title'), { + description: tCore('errors.internal_server_error'), + }); }; return { diff --git a/packages/frontend/src/views/admin/views/members/create/hooks/mutation-api.ts b/packages/frontend/src/views/admin/views/members/create/hooks/mutation-api.ts index 91abb3f0f..b9ebeeb00 100644 --- a/packages/frontend/src/views/admin/views/members/create/hooks/mutation-api.ts +++ b/packages/frontend/src/views/admin/views/members/create/hooks/mutation-api.ts @@ -9,14 +9,28 @@ export const mutationApi = async ( token: string; } & SignUpAuthBody, ) => { - await fetcher<{ email: string }, SignUpAuthBody>({ - url: '/core/auth/sign_up', - method: 'POST', - body, - headers: { - 'x-vitnode-captcha-token': body.token, - }, - }); + try { + await fetcher<{ email: string }, SignUpAuthBody>({ + url: '/core/auth/sign_up', + method: 'POST', + body, + headers: { + 'x-vitnode-captcha-token': body.token, + }, + }); - revalidatePath('/[locale]/admin/(auth)/(vitnode)/members/users', 'page'); + revalidatePath('/[locale]/admin/(auth)/(vitnode)/members/users', 'page'); + } catch (err) { + const { message } = err as Error; + + if (message.includes('EMAIL_ALREADY_EXISTS')) { + return { message: 'EMAIL_ALREADY_EXISTS' }; + } + + if (message.includes('NAME_ALREADY_EXISTS')) { + return { message: 'NAME_ALREADY_EXISTS' }; + } + + return { message: 'INTERNAL_SERVER_ERROR' }; + } }; diff --git a/packages/frontend/src/views/admin/views/members/create/hooks/use-create-user-admin.ts b/packages/frontend/src/views/admin/views/members/create/hooks/use-create-user-admin.ts index 779f61b54..016c59e53 100644 --- a/packages/frontend/src/views/admin/views/members/create/hooks/use-create-user-admin.ts +++ b/packages/frontend/src/views/admin/views/members/create/hooks/use-create-user-admin.ts @@ -59,50 +59,52 @@ export const useCreateUserAdmin = () => { return; } - try { - await mutationApi({ - ...values, - token, - }); - + const mutation = await mutationApi({ + ...values, + token, + }); + if (!mutation?.message) { setOpen?.(false); toast.success(t('success'), { description: values.name, }); - } catch (e) { - const error = e as Error; - if (error.message.includes('EMAIL_ALREADY_EXISTS')) { - form.setError( - 'email', - { - type: 'manual', - message: tSignUp('email.already_exists'), - }, - { - shouldFocus: true, - }, - ); - return; - } else if (error.message.includes('NAME_ALREADY_EXISTS')) { - form.setError( - 'name', - { - type: 'manual', - message: tSignUp('name.already_exists'), - }, - { - shouldFocus: true, - }, - ); + return; + } - return; - } + if (mutation.message === 'EMAIL_ALREADY_EXISTS') { + form.setError( + 'email', + { + type: 'manual', + message: tSignUp('email.already_exists'), + }, + { + shouldFocus: true, + }, + ); - toast.error(tCore('title'), { - description: tCore('internal_server_error'), - }); + return; + } + + if (mutation.message === 'NAME_ALREADY_EXISTS') { + form.setError( + 'name', + { + type: 'manual', + message: tSignUp('name.already_exists'), + }, + { + shouldFocus: true, + }, + ); + + return; } + + toast.error(tCore('title'), { + description: tCore('internal_server_error'), + }); }; return { formSchema, onSubmit, values, setValues, isReady }; diff --git a/packages/frontend/src/views/admin/views/members/user/actions/edit/edit.tsx b/packages/frontend/src/views/admin/views/members/user/actions/edit/edit.tsx index 77b5e4a20..83cdc9f6d 100644 --- a/packages/frontend/src/views/admin/views/members/user/actions/edit/edit.tsx +++ b/packages/frontend/src/views/admin/views/members/user/actions/edit/edit.tsx @@ -62,31 +62,32 @@ export const EditActionUserMembersAdmin = ({ values: z.infer, form: UseFormReturn>, ) => { - try { - await mutationApi({ - id, - ...values, - group_id: +values.group[0].key, - }); + const mutation = await mutationApi({ + id, + ...values, + group_id: +values.group[0].key, + }); + if (!mutation?.message) { setOpen?.(false); toast.success(t('success'), { description: values.name, }); - } catch (e) { - const error = e as Error; - if (error.message.includes('EMAIL_ALREADY_EXISTS')) { - form.setError('email', { - message: tSignUp('email.already_exists'), - }); - return; - } + return; + } - toast.error(tCore('title'), { - description: tCore('internal_server_error'), + if (mutation.message === 'EMAIL_ALREADY_EXISTS') { + form.setError('email', { + message: tSignUp('email.already_exists'), }); + + return; } + + toast.error(tCore('title'), { + description: tCore('internal_server_error'), + }); }; return ( diff --git a/packages/frontend/src/views/admin/views/members/user/actions/edit/mutation-api.ts b/packages/frontend/src/views/admin/views/members/user/actions/edit/mutation-api.ts index 2a8b4fce9..97c9c319a 100644 --- a/packages/frontend/src/views/admin/views/members/user/actions/edit/mutation-api.ts +++ b/packages/frontend/src/views/admin/views/members/user/actions/edit/mutation-api.ts @@ -11,12 +11,22 @@ export const mutationApi = async ({ id, ...body }: { id: number } & EditUserMembersAdminBody) => { - await fetcher({ - url: `/admin/members/users/${id}`, - method: 'PUT', - body, - }); + try { + await fetcher({ + url: `/admin/members/users/${id}`, + method: 'PUT', + body, + }); - revalidateTags.session(id); - revalidateTags.sessionAdmin(id); + revalidateTags.session(id); + revalidateTags.sessionAdmin(id); + } catch (err) { + const { message } = err as Error; + + if (message.includes('EMAIL_ALREADY_EXISTS')) { + return { message: 'EMAIL_ALREADY_EXISTS' }; + } + + return { message: 'INTERNAL_SERVER_ERROR' }; + } };