From 060faae73fe0a78513466bd285370b18fe568f37 Mon Sep 17 00:00:00 2001 From: Myrfion Date: Sun, 9 Apr 2023 21:05:09 -0400 Subject: [PATCH 1/5] Add error and catch boundaries --- app/components/errors/seen-error-layout.tsx | 33 +++++++++++++++++ app/components/errors/unseen-error-layout.tsx | 24 +++++++++++++ app/routes/__index.tsx | 14 +++++++- app/routes/__index/certificate/index.tsx | 29 +++++++++++++-- .../__index/dns-records/$dnsRecordId.tsx | 36 ++++++++++++++++--- app/routes/__index/dns-records/index.tsx | 28 ++++++++++++++- app/utils.ts | 21 +++++++++++ 7 files changed, 177 insertions(+), 8 deletions(-) create mode 100644 app/components/errors/seen-error-layout.tsx create mode 100644 app/components/errors/unseen-error-layout.tsx diff --git a/app/components/errors/seen-error-layout.tsx b/app/components/errors/seen-error-layout.tsx new file mode 100644 index 00000000..68ba0ff8 --- /dev/null +++ b/app/components/errors/seen-error-layout.tsx @@ -0,0 +1,33 @@ +import { Box, Button, Heading, Text } from '@chakra-ui/react'; +import type { ThrownResponse } from '@remix-run/react'; +import { useNavigate } from '@remix-run/react'; +import { getErrorMessageFromStatusCode } from '~/utils'; + +interface SeenErrorLayoutProps { + result: ThrownResponse; + mapStatusToErrorText?: (statusCode: number) => string; +} + +export default function SeenErrorLayout({ + result, + mapStatusToErrorText = getErrorMessageFromStatusCode, +}: SeenErrorLayoutProps) { + const navigate = useNavigate(); + + return ( + + + {result.status} + + + {mapStatusToErrorText(result.status)} + + + {result.data} + + + + ); +} diff --git a/app/components/errors/unseen-error-layout.tsx b/app/components/errors/unseen-error-layout.tsx new file mode 100644 index 00000000..5ca7703b --- /dev/null +++ b/app/components/errors/unseen-error-layout.tsx @@ -0,0 +1,24 @@ +import { Box, Button, Heading, Text } from '@chakra-ui/react'; +import { useNavigate } from '@remix-run/react'; + +interface UnseenErrorLayoutProps { + errorText: string; +} + +export default function UnseenErrorLayout({ errorText }: UnseenErrorLayoutProps) { + const navigate = useNavigate(); + + return ( + + + Ooops, unexpected error + + + {errorText} + + + + ); +} diff --git a/app/routes/__index.tsx b/app/routes/__index.tsx index 47d3e238..bd6fe4b8 100644 --- a/app/routes/__index.tsx +++ b/app/routes/__index.tsx @@ -1,5 +1,7 @@ import { Box, Container } from '@chakra-ui/react'; -import { Outlet } from '@remix-run/react'; +import { Outlet, useCatch } from '@remix-run/react'; +import SeenErrorLayout from '~/components/errors/seen-error-layout'; +import UnseenErrorLayout from '~/components/errors/unseen-error-layout'; import Header from '~/components/header'; export default function Index() { @@ -14,3 +16,13 @@ export default function Index() { ); } + +export function ErrorBoundary({ error }: { error: Error }) { + return ; +} + +export function CatchBoundary() { + const caught = useCatch(); + + return ; +} diff --git a/app/routes/__index/certificate/index.tsx b/app/routes/__index/certificate/index.tsx index d42e6a03..af05262c 100644 --- a/app/routes/__index/certificate/index.tsx +++ b/app/routes/__index/certificate/index.tsx @@ -2,7 +2,7 @@ import { Flex, Heading } from '@chakra-ui/react'; import type { LoaderArgs, ActionArgs } from '@remix-run/node'; import { json } from '@remix-run/node'; import { typedjson, useTypedLoaderData } from 'remix-typedjson'; -import { useRevalidator } from '@remix-run/react'; +import { useCatch, useRevalidator } from '@remix-run/react'; import { useInterval } from 'react-use'; import { useMemo } from 'react'; import dayjs from 'dayjs'; @@ -12,9 +12,11 @@ import pendingSvg from '~/assets/undraw_processing_re_tbdu.svg'; import Loading from '~/components/display-page'; import CertificateAvailable from '~/components/certificate/certificate-available'; import CertificateRequestView from '~/components/certificate/certificate-request'; -import { useEffectiveUser } from '~/utils'; +import { getErrorMessageFromStatusCode, useEffectiveUser } from '~/utils'; import { getCertificateByUsername } from '~/models/certificate.server'; import { addCertRequest } from '~/queues/certificate/certificate-flow.server'; +import UnseenErrorLayout from '~/components/errors/unseen-error-layout'; +import SeenErrorLayout from '~/components/errors/seen-error-layout'; export const loader = async ({ request }: LoaderArgs) => { const username = await requireUsername(request); @@ -75,6 +77,29 @@ function formatDate(val: Date): string { return date; } +function mapStatusToErrorText(statusCode: number): string { + switch (statusCode) { + case 404: + return 'Sorry we could not find you certificate'; + case 409: + return 'Sorry we could not find you certificate key'; + default: + return getErrorMessageFromStatusCode(statusCode); + } +} + +export function CatchBoundary() { + const caught = useCatch(); + + return ; +} + +export function ErrorBoundary() { + return ( + + ); +} + export default function CertificateIndexRoute() { const user = useEffectiveUser(); const revalidator = useRevalidator(); diff --git a/app/routes/__index/dns-records/$dnsRecordId.tsx b/app/routes/__index/dns-records/$dnsRecordId.tsx index 85f6358e..122207eb 100644 --- a/app/routes/__index/dns-records/$dnsRecordId.tsx +++ b/app/routes/__index/dns-records/$dnsRecordId.tsx @@ -6,15 +6,18 @@ import DnsRecordForm from '~/components/dns-record/form'; import { requireUser } from '~/session.server'; import { getDnsRecordById, updateDnsRecordById } from '~/models/dns-record.server'; import { isNameValid, UpdateDnsRecordSchema } from '~/lib/dns.server'; -import { useActionData } from '@remix-run/react'; -import { buildDomain } from '~/utils'; +import { useActionData, useCatch, useParams } from '@remix-run/react'; +import { buildDomain, getErrorMessageFromStatusCode } from '~/utils'; +import SeenErrorLayout from '~/components/errors/seen-error-layout'; +import UnseenErrorLayout from '~/components/errors/unseen-error-layout'; export const loader = async ({ request, params }: LoaderArgs) => { await requireUser(request); const { dnsRecordId } = params; - if (!dnsRecordId) { - throw new Response('dnsRecordId should be string', { + + if (!dnsRecordId || !parseInt(dnsRecordId)) { + throw new Response('Dns Record Id is not valid', { status: 400, }); } @@ -59,6 +62,31 @@ export const action = async ({ request }: ActionArgs) => { return redirect(`/dns-records`); }; +function mapStatusToErrorText(statusCode: number): string { + switch (statusCode) { + case 400: + return 'Provided record id is not valid'; + default: + return getErrorMessageFromStatusCode(statusCode); + } +} + +export function CatchBoundary() { + const caught = useCatch(); + + return ; +} + +export function ErrorBoundary() { + const { dnsRecordId } = useParams(); + + return ( + + ); +} + export default function DnsRecordRoute() { const dnsRecord = useTypedLoaderData(); const actionData = useActionData(); diff --git a/app/routes/__index/dns-records/index.tsx b/app/routes/__index/dns-records/index.tsx index 9c1c25a1..98c5ab3b 100644 --- a/app/routes/__index/dns-records/index.tsx +++ b/app/routes/__index/dns-records/index.tsx @@ -1,6 +1,6 @@ import { AddIcon } from '@chakra-ui/icons'; import { Button, Center, Flex, Heading, Stat, StatLabel, StatNumber, Text } from '@chakra-ui/react'; -import { Link } from '@remix-run/react'; +import { Link, useCatch } from '@remix-run/react'; import { typedjson, useTypedLoaderData } from 'remix-typedjson'; import { json } from '@remix-run/node'; import { z } from 'zod'; @@ -17,6 +17,9 @@ import { requireUsername } from '~/session.server'; import logger from '~/lib/logger.server'; import type { LoaderArgs, ActionArgs } from '@remix-run/node'; +import SeenErrorLayout from '~/components/errors/seen-error-layout'; +import UnseenErrorLayout from '~/components/errors/unseen-error-layout'; +import { getErrorMessageFromStatusCode } from '~/utils'; export type DnsRecordActionIntent = 'renew-dns-record' | 'delete-dns-record'; @@ -77,6 +80,29 @@ export const action = async ({ request }: ActionArgs) => { } }; +function mapStatusToErrorText(statusCode: number): string { + switch (statusCode) { + case 404: + return 'Sorry we could not find your dns record'; + case 400: + return 'We got an error processing requested action on your dns record'; + default: + return getErrorMessageFromStatusCode(statusCode); + } +} + +export function CatchBoundary() { + const caught = useCatch(); + + return ; +} + +export function ErrorBoundary() { + return ( + + ); +} + export default function DnsRecordsIndexRoute() { const data = useTypedLoaderData(); diff --git a/app/utils.ts b/app/utils.ts index 0cdab6da..f36fe403 100644 --- a/app/utils.ts +++ b/app/utils.ts @@ -149,3 +149,24 @@ export async function getChildrenValuesOfQueueName({ return filteredChildrenValues; } + +export function getErrorMessageFromStatusCode(statusCode: number): string { + switch (statusCode) { + case 400: + return 'Bad Request: The server cannot process the request because it is malformed or invalid.'; + case 401: + return 'Unauthorized: The request requires authentication, and the user is not authenticated.'; + case 403: + return 'Forbidden: The server understands the request but refuses to authorize it.'; + case 404: + return 'Not Found: The server cannot find the requested resource.'; + case 500: + return 'Internal Server Error: The server encountered an unexpected condition that prevented it from fulfilling the request.'; + case 502: + return 'Bad Gateway: The server received an invalid response from the upstream server.'; + case 503: + return 'Service Unavailable: The server is currently unable to handle the request due to a temporary overload or maintenance.'; + default: + return 'An error has occurred.'; + } +} From 47c76e643967e3808c7a662441170fc65b7d63ea Mon Sep 17 00:00:00 2001 From: Myrfion Date: Mon, 10 Apr 2023 10:53:46 -0400 Subject: [PATCH 2/5] Fix typos --- app/routes/__index/certificate/index.tsx | 2 +- app/routes/__index/dns-records/$dnsRecordId.tsx | 2 +- app/routes/__index/dns-records/index.tsx | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/routes/__index/certificate/index.tsx b/app/routes/__index/certificate/index.tsx index af05262c..8201caf6 100644 --- a/app/routes/__index/certificate/index.tsx +++ b/app/routes/__index/certificate/index.tsx @@ -82,7 +82,7 @@ function mapStatusToErrorText(statusCode: number): string { case 404: return 'Sorry we could not find you certificate'; case 409: - return 'Sorry we could not find you certificate key'; + return 'Sorry, your certificate is not issued yet. Please try again later.'; default: return getErrorMessageFromStatusCode(statusCode); } diff --git a/app/routes/__index/dns-records/$dnsRecordId.tsx b/app/routes/__index/dns-records/$dnsRecordId.tsx index 122207eb..06772b38 100644 --- a/app/routes/__index/dns-records/$dnsRecordId.tsx +++ b/app/routes/__index/dns-records/$dnsRecordId.tsx @@ -17,7 +17,7 @@ export const loader = async ({ request, params }: LoaderArgs) => { const { dnsRecordId } = params; if (!dnsRecordId || !parseInt(dnsRecordId)) { - throw new Response('Dns Record Id is not valid', { + throw new Response('DNS Record ID is not valid', { status: 400, }); } diff --git a/app/routes/__index/dns-records/index.tsx b/app/routes/__index/dns-records/index.tsx index 98c5ab3b..9defa30e 100644 --- a/app/routes/__index/dns-records/index.tsx +++ b/app/routes/__index/dns-records/index.tsx @@ -83,7 +83,7 @@ export const action = async ({ request }: ActionArgs) => { function mapStatusToErrorText(statusCode: number): string { switch (statusCode) { case 404: - return 'Sorry we could not find your dns record'; + return 'Sorry we could not find your DNS Record'; case 400: return 'We got an error processing requested action on your dns record'; default: @@ -99,7 +99,7 @@ export function CatchBoundary() { export function ErrorBoundary() { return ( - + ); } From 3aa6140bc6366af915248b8873de4fbd6f01f310 Mon Sep 17 00:00:00 2001 From: Tymur Levtsun <32075241+Myrfion@users.noreply.github.com> Date: Mon, 10 Apr 2023 19:54:34 -0400 Subject: [PATCH 3/5] Update app/routes/__index/certificate/index.tsx Co-authored-by: Eakam --- app/routes/__index/certificate/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/routes/__index/certificate/index.tsx b/app/routes/__index/certificate/index.tsx index 8201caf6..ed4da1c3 100644 --- a/app/routes/__index/certificate/index.tsx +++ b/app/routes/__index/certificate/index.tsx @@ -80,7 +80,7 @@ function formatDate(val: Date): string { function mapStatusToErrorText(statusCode: number): string { switch (statusCode) { case 404: - return 'Sorry we could not find you certificate'; + return 'Sorry we could not find your certificate'; case 409: return 'Sorry, your certificate is not issued yet. Please try again later.'; default: From b624849b2236800b32b2bb1c42522916d83800b5 Mon Sep 17 00:00:00 2001 From: Myrfion Date: Mon, 10 Apr 2023 19:55:19 -0400 Subject: [PATCH 4/5] Fix: typo --- app/routes/__index/dns-records/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/routes/__index/dns-records/index.tsx b/app/routes/__index/dns-records/index.tsx index 9defa30e..1ef4e1b5 100644 --- a/app/routes/__index/dns-records/index.tsx +++ b/app/routes/__index/dns-records/index.tsx @@ -85,7 +85,7 @@ function mapStatusToErrorText(statusCode: number): string { case 404: return 'Sorry we could not find your DNS Record'; case 400: - return 'We got an error processing requested action on your dns record'; + return 'We got an error processing requested action on your DNS Record'; default: return getErrorMessageFromStatusCode(statusCode); } From bc24f7d02bea9dd1b62d02be8a7a82b798056031 Mon Sep 17 00:00:00 2001 From: Myrfion Date: Mon, 10 Apr 2023 19:57:47 -0400 Subject: [PATCH 5/5] Capitalize record id --- app/routes/__index/dns-records/$dnsRecordId.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/routes/__index/dns-records/$dnsRecordId.tsx b/app/routes/__index/dns-records/$dnsRecordId.tsx index 06772b38..32729e44 100644 --- a/app/routes/__index/dns-records/$dnsRecordId.tsx +++ b/app/routes/__index/dns-records/$dnsRecordId.tsx @@ -65,7 +65,7 @@ export const action = async ({ request }: ActionArgs) => { function mapStatusToErrorText(statusCode: number): string { switch (statusCode) { case 400: - return 'Provided record id is not valid'; + return 'Provided Record ID is not valid'; default: return getErrorMessageFromStatusCode(statusCode); }