From 2deaad5c345265494b0dbfbc2d8c8d6277a6bcc8 Mon Sep 17 00:00:00 2001 From: Catalin Pit Date: Mon, 27 Nov 2023 12:50:21 +0200 Subject: [PATCH] feat: token page --- apps/web/src/components/forms/token.tsx | 158 ++++++++++++++++++ .../public-api/get-all-user-tokens.ts | 13 ++ .../trpc/server/api-token-router/router.ts | 11 ++ 3 files changed, 182 insertions(+) create mode 100644 apps/web/src/components/forms/token.tsx create mode 100644 packages/lib/server-only/public-api/get-all-user-tokens.ts diff --git a/apps/web/src/components/forms/token.tsx b/apps/web/src/components/forms/token.tsx new file mode 100644 index 000000000..8e9d9d2b6 --- /dev/null +++ b/apps/web/src/components/forms/token.tsx @@ -0,0 +1,158 @@ +'use client'; + +import { useRouter } from 'next/navigation'; + +import { zodResolver } from '@hookform/resolvers/zod'; +import { useForm } from 'react-hook-form'; +import type { z } from 'zod'; + +import type { User } from '@documenso/prisma/client'; +import { TRPCClientError } from '@documenso/trpc/client'; +import { trpc } from '@documenso/trpc/react'; +import { ZCreateTokenMutationSchema } from '@documenso/trpc/server/api-token-router/schema'; +import { cn } from '@documenso/ui/lib/utils'; +import { Button } from '@documenso/ui/primitives/button'; +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from '@documenso/ui/primitives/form/form'; +import { Input } from '@documenso/ui/primitives/input'; +import { useToast } from '@documenso/ui/primitives/use-toast'; + +export type ApiTokenFormProps = { + user: User; + className?: string; +}; + +type TCreateTokenMutationSchema = z.infer; + +export const ApiTokenForm = ({ user, className }: ApiTokenFormProps) => { + const router = useRouter(); + + const { toast } = useToast(); + + const { data: tokens } = trpc.apiToken.getTokens.useQuery(); + const { mutateAsync: createTokenMutation } = trpc.apiToken.createToken.useMutation(); + + const form = useForm({ + resolver: zodResolver(ZCreateTokenMutationSchema), + values: { + tokenName: '', + }, + }); + + const deleteToken = () => { + console.log('deleted'); + }; + + const copyToken = () => { + console.log('copied'); + }; + + const onSubmit = async ({ tokenName }: TCreateTokenMutationSchema) => { + try { + await createTokenMutation({ + tokenName, + }); + + toast({ + title: 'Token created', + description: 'A new token was created successfully.', + duration: 5000, + }); + + router.refresh(); + } catch (error) { + if (error instanceof TRPCClientError && error.data?.code === 'BAD_REQUEST') { + toast({ + title: 'An error occurred', + description: error.message, + variant: 'destructive', + }); + } else { + toast({ + title: 'An unknown error occurred', + variant: 'destructive', + description: + 'We encountered an unknown error while attempting create the new token. Please try again later.', + }); + } + } + }; + + return ( +
+

Your existing tokens

+
    + {tokens?.map((token) => ( +
  • +
    +

    + {token.name} ({token.algorithm}) +

    +

    {token.token}

    +

    + Created:{' '} + {token.createdAt + ? new Date(token.createdAt).toLocaleDateString(undefined, { + weekday: 'long', + year: 'numeric', + month: 'long', + day: 'numeric', + }) + : 'N/A'} +

    +

    + Expires:{' '} + {token.expires + ? new Date(token.expires).toLocaleDateString(undefined, { + weekday: 'long', + year: 'numeric', + month: 'long', + day: 'numeric', + }) + : 'N/A'} +

    + + +
    +
  • + ))} +
+

Create a new token

+
+ +
+ ( + + Token Name + + + + + + )} + /> + +
+ +
+
+
+ +
+ ); +}; diff --git a/packages/lib/server-only/public-api/get-all-user-tokens.ts b/packages/lib/server-only/public-api/get-all-user-tokens.ts new file mode 100644 index 000000000..c6c7a7d94 --- /dev/null +++ b/packages/lib/server-only/public-api/get-all-user-tokens.ts @@ -0,0 +1,13 @@ +import { prisma } from '@documenso/prisma'; + +export type GetUserTokensOptions = { + userId: number; +}; + +export const getUserTokens = async ({ userId }: GetUserTokensOptions) => { + return prisma.apiToken.findMany({ + where: { + userId, + }, + }); +}; diff --git a/packages/trpc/server/api-token-router/router.ts b/packages/trpc/server/api-token-router/router.ts index 88f3ad9d0..49dac8809 100644 --- a/packages/trpc/server/api-token-router/router.ts +++ b/packages/trpc/server/api-token-router/router.ts @@ -2,6 +2,7 @@ import { TRPCError } from '@trpc/server'; import { createApiToken } from '@documenso/lib/server-only/public-api/create-api-token'; import { deleteApiTokenById } from '@documenso/lib/server-only/public-api/delete-api-token-by-id'; +import { getUserTokens } from '@documenso/lib/server-only/public-api/get-all-user-tokens'; import { getApiTokenById } from '@documenso/lib/server-only/public-api/get-api-token-by-id'; import { authenticatedProcedure, router } from '../trpc'; @@ -12,6 +13,16 @@ import { } from './schema'; export const apiTokenRouter = router({ + getTokens: authenticatedProcedure.query(async ({ ctx }) => { + try { + return await getUserTokens({ userId: ctx.user.id }); + } catch (e) { + throw new TRPCError({ + code: 'BAD_REQUEST', + message: 'We were unable to find your API tokens. Please try again.', + }); + } + }), getTokenById: authenticatedProcedure .input(ZGetApiTokenByIdQuerySchema) .query(async ({ input, ctx }) => {