From b36724fe9171678f37c204fbb35d496da9cc9b61 Mon Sep 17 00:00:00 2001 From: Michael Brusegard <56915010+michaelbrusegard@users.noreply.github.com> Date: Sun, 10 Nov 2024 16:29:37 +0100 Subject: [PATCH] feat: add default toast on API error --- bun.lockb | Bin 269284 -> 269724 bytes package.json | 1 + src/app/[locale]/layout.tsx | 2 ++ src/components/ui/Toaster.tsx | 49 ++++++++++++++++++++++++++++++++ src/lib/api/error.ts | 27 ++++++++++++++++++ src/lib/api/queryClient.ts | 10 +++++++ src/lib/api/types.ts | 8 ++++++ src/server/api/index.ts | 3 +- src/server/api/routers/auth.ts | 26 +++++++++++++++++ src/server/api/routers/index.ts | 2 ++ 10 files changed, 127 insertions(+), 1 deletion(-) create mode 100644 src/components/ui/Toaster.tsx create mode 100644 src/lib/api/error.ts create mode 100644 src/lib/api/types.ts create mode 100644 src/server/api/routers/auth.ts create mode 100644 src/server/api/routers/index.ts diff --git a/bun.lockb b/bun.lockb index 3232640ee72dbdecfa40277eebbe2fdf4edcc6d3..0f9617aa8a124b9ed35149814756ceca461ca464 100755 GIT binary patch delta 42055 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 B;u-(| delta 41465 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 diff --git a/package.json b/package.json index 6f14c07..57fa882 100644 --- a/package.json +++ b/package.json @@ -62,6 +62,7 @@ "react-day-picker": "^9.2.1", "react-dom": "^19.0.0-rc-fb9a90fa48-20240614", "reading-time": "^1.5.0", + "sonner": "^1.7.0", "superjson": "^2.2.1", "tailwind-merge": "^2.5.4", "tsparticles": "^3.5.0", diff --git a/src/app/[locale]/layout.tsx b/src/app/[locale]/layout.tsx index ac7f8cd..b3310ba 100644 --- a/src/app/[locale]/layout.tsx +++ b/src/app/[locale]/layout.tsx @@ -1,4 +1,5 @@ import { RootProviders } from '@/components/providers/RootProviders'; +import { Toaster } from '@/components/ui/Toaster'; import { routing } from '@/lib/locale'; import { cx } from '@/lib/utils'; import { getTranslations, setRequestLocale } from 'next-intl/server'; @@ -84,6 +85,7 @@ export default async function LocaleLayout(props: LocaleLayoutProps) {
{children} +
diff --git a/src/components/ui/Toaster.tsx b/src/components/ui/Toaster.tsx new file mode 100644 index 0000000..c40a780 --- /dev/null +++ b/src/components/ui/Toaster.tsx @@ -0,0 +1,49 @@ +'use client'; + +import { + CircleCheckIcon, + CircleXIcon, + InfoIcon, + LoaderIcon, + TriangleAlertIcon, +} from 'lucide-react'; +import { useTheme } from 'next-themes'; +import { Toaster as Sonner, toast } from 'sonner'; + +import { useMediaQuery } from '@/lib/hooks/useMediaQuery'; + +type ToasterProps = React.ComponentProps; + +const Toaster = ({ ...props }: ToasterProps) => { + const { theme = 'system' } = useTheme(); + const isDesktop = useMediaQuery('(min-width: 768px)'); + + return ( + , + info: , + warning: , + error: , + loading: , + }} + {...props} + /> + ); +}; + +export { Toaster, toast }; diff --git a/src/lib/api/error.ts b/src/lib/api/error.ts new file mode 100644 index 0000000..295df0b --- /dev/null +++ b/src/lib/api/error.ts @@ -0,0 +1,27 @@ +import { toast } from '@/components/ui/Toaster'; +import type { TRPCClientError } from '@/lib/api/types'; + +function handleGlobalError(error: Error) { + const TRPCError = error as TRPCClientError; + + if (TRPCError.toast) { + switch (TRPCError.toast) { + case 'success': + toast.success(TRPCError.message, { richColors: true }); + break; + case 'info': + toast.info(TRPCError.message, { richColors: true }); + break; + case 'warning': + toast.warning(TRPCError.message, { richColors: true }); + break; + default: + toast.error(TRPCError.message, { richColors: true }); + break; + } + } else { + throw error; + } +} + +export { handleGlobalError }; diff --git a/src/lib/api/queryClient.ts b/src/lib/api/queryClient.ts index b121db5..7c4b265 100644 --- a/src/lib/api/queryClient.ts +++ b/src/lib/api/queryClient.ts @@ -1,11 +1,21 @@ import { + MutationCache, + QueryCache, QueryClient, defaultShouldDehydrateQuery, } from '@tanstack/react-query'; import SuperJSON from 'superjson'; +import { handleGlobalError } from '@/lib/api/error'; + function createQueryClient() { return new QueryClient({ + queryCache: new QueryCache({ + onError: handleGlobalError, + }), + mutationCache: new MutationCache({ + onError: handleGlobalError, + }), defaultOptions: { queries: { // With SSR, we usually want to set some default staleTime diff --git a/src/lib/api/types.ts b/src/lib/api/types.ts new file mode 100644 index 0000000..887c177 --- /dev/null +++ b/src/lib/api/types.ts @@ -0,0 +1,8 @@ +import type { router } from '@/server/api'; +import type { TRPCClientError as BaseTRPCClientError } from '@trpc/client'; + +type TRPCClientError = BaseTRPCClientError & { + toast?: 'success' | 'info' | 'warning' | 'error'; +}; + +export type { TRPCClientError }; diff --git a/src/server/api/index.ts b/src/server/api/index.ts index 3e770ca..7f50e5e 100644 --- a/src/server/api/index.ts +++ b/src/server/api/index.ts @@ -1,8 +1,9 @@ -import { testRouter } from '@/server/api/routers/test'; +import { authRouter, testRouter } from '@/server/api/routers'; import { createCallerFactory, createRouter } from '@/server/api/trpc'; const router = createRouter({ test: testRouter, + auth: authRouter, }); const createCaller = createCallerFactory(router); diff --git a/src/server/api/routers/auth.ts b/src/server/api/routers/auth.ts new file mode 100644 index 0000000..b9305db --- /dev/null +++ b/src/server/api/routers/auth.ts @@ -0,0 +1,26 @@ +import { publicProcedure } from '@/server/api/procedures'; +import { RefillingTokenBucket } from '@/server/api/rate-limit/refillingTokenBucket'; +import { createRouter } from '@/server/api/trpc'; +import { getFeideAuthorizationURL } from '@/server/auth/feide'; + +import { TRPCError } from '@trpc/server'; +import { headers } from 'next/headers'; + +const ipBucket = new RefillingTokenBucket(5, 60); + +const authRouter = createRouter({ + getFeideAuthorizationURL: publicProcedure.query(async () => { + const headerStore = await headers(); + const clientIP = headerStore.get('X-Forwarded-For'); + + if (clientIP !== null && !ipBucket.check(clientIP, 1)) { + throw new TRPCError({ + code: 'TOO_MANY_REQUESTS', + message: 'Rate limit exceeded. Please try again later.', + }); + } + return getFeideAuthorizationURL(); + }), +}); + +export { authRouter }; diff --git a/src/server/api/routers/index.ts b/src/server/api/routers/index.ts new file mode 100644 index 0000000..10c2590 --- /dev/null +++ b/src/server/api/routers/index.ts @@ -0,0 +1,2 @@ +export * from './test'; +export * from './auth';