From ddb9dd11d7824c50825d772abcb943f0a641dbc1 Mon Sep 17 00:00:00 2001 From: Catalin Pit <25515812+catalinpit@users.noreply.github.com> Date: Fri, 9 Feb 2024 16:07:33 +0200 Subject: [PATCH] feat: added backend stuff --- .../webhooks/create-webhook-dialog.tsx | 69 ++++++++++++++----- .../server-only/webhooks/create-webhook.ts | 28 ++++++++ .../webhooks/get-webhooks-by-user-id.ts | 9 +++ packages/trpc/server/router.ts | 2 + packages/trpc/server/webhook-router/router.ts | 35 ++++++++++ packages/trpc/server/webhook-router/schema.ts | 14 ++++ 6 files changed, 138 insertions(+), 19 deletions(-) create mode 100644 packages/lib/server-only/webhooks/create-webhook.ts create mode 100644 packages/lib/server-only/webhooks/get-webhooks-by-user-id.ts create mode 100644 packages/trpc/server/webhook-router/router.ts create mode 100644 packages/trpc/server/webhook-router/schema.ts diff --git a/apps/web/src/components/(dashboard)/settings/webhooks/create-webhook-dialog.tsx b/apps/web/src/components/(dashboard)/settings/webhooks/create-webhook-dialog.tsx index ec6d0f152..7d4003fb5 100644 --- a/apps/web/src/components/(dashboard)/settings/webhooks/create-webhook-dialog.tsx +++ b/apps/web/src/components/(dashboard)/settings/webhooks/create-webhook-dialog.tsx @@ -1,16 +1,26 @@ 'use client'; import { useState } from 'react'; + import { useRouter } from 'next/navigation'; import { zodResolver } from '@hookform/resolvers/zod'; import type * as DialogPrimitive from '@radix-ui/react-dialog'; import { useForm } from 'react-hook-form'; +import type { z } from 'zod'; +import { trpc } from '@documenso/trpc/react'; +import { ZCreateWebhookFormSchema } from '@documenso/trpc/server/webhook-router/schema'; import { Button } from '@documenso/ui/primitives/button'; -import { Input } from '@documenso/ui/primitives/input'; -import { Switch } from '@documenso/ui/primitives/switch'; -import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger, DialogFooter } from '@documenso/ui/primitives/dialog'; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, +} from '@documenso/ui/primitives/dialog'; import { Form, FormControl, @@ -19,9 +29,13 @@ import { FormLabel, FormMessage, } from '@documenso/ui/primitives/form/form'; +import { Input } from '@documenso/ui/primitives/input'; +import { Switch } from '@documenso/ui/primitives/switch'; +import { useToast } from '@documenso/ui/primitives/use-toast'; import { MultiSelectCombobox } from './multiselect-combobox'; +type TCreateWebhookFormSchema = z.infer; export type CreateWebhookDialogProps = { trigger?: React.ReactNode; @@ -29,10 +43,11 @@ export type CreateWebhookDialogProps = { export const CreateWebhookDialog = ({ trigger, ...props }: CreateWebhookDialogProps) => { const router = useRouter(); + const { toast } = useToast(); const [open, setOpen] = useState(false); - const form = useForm<>({ - resolver: zodResolver(), + const form = useForm({ + resolver: zodResolver(ZCreateWebhookFormSchema), values: { webhookUrl: '', eventTriggers: [], @@ -41,9 +56,30 @@ export const CreateWebhookDialog = ({ trigger, ...props }: CreateWebhookDialogPr }, }); - const onSubmit = async () => { - console.log('submitted'); - } + const { mutateAsync: createWebhook } = trpc.webhook.createWebhook.useMutation(); + + const onSubmit = async (values: TCreateWebhookFormSchema) => { + try { + await createWebhook(values); + + setOpen(false); + + toast({ + title: 'Webhook created', + description: 'The webhook was successfully created.', + }); + + form.reset(); + + router.refresh(); + } catch (err) { + toast({ + title: 'Error', + description: 'An error occurred while creating the webhook. Please try again.', + variant: 'destructive', + }); + } + }; return ( { console.log(values); - onChange(values) + onChange(values); }} /> @@ -101,28 +137,28 @@ export const CreateWebhookDialog = ({ trigger, ...props }: CreateWebhookDialogPr )} /> - ( Secret - + )} /> - ( Active -
-
- diff --git a/packages/lib/server-only/webhooks/create-webhook.ts b/packages/lib/server-only/webhooks/create-webhook.ts new file mode 100644 index 000000000..ee352c49f --- /dev/null +++ b/packages/lib/server-only/webhooks/create-webhook.ts @@ -0,0 +1,28 @@ +import { prisma } from '@documenso/prisma'; +import type { WebhookTriggerEvents } from '@documenso/prisma/client'; + +export interface CreateWebhookOptions { + webhookUrl: string; + eventTriggers: WebhookTriggerEvents[]; + secret: string | null; + enabled: boolean; + userId: number; +} + +export const createWebhook = async ({ + webhookUrl, + eventTriggers, + secret, + enabled, + userId, +}: CreateWebhookOptions) => { + return await prisma.webhook.create({ + data: { + webhookUrl, + eventTriggers, + secret, + enabled, + userId, + }, + }); +}; diff --git a/packages/lib/server-only/webhooks/get-webhooks-by-user-id.ts b/packages/lib/server-only/webhooks/get-webhooks-by-user-id.ts new file mode 100644 index 000000000..a775ac30c --- /dev/null +++ b/packages/lib/server-only/webhooks/get-webhooks-by-user-id.ts @@ -0,0 +1,9 @@ +import { prisma } from '@documenso/prisma'; + +export const getWebhooksByUserId = async (userId: number) => { + return await prisma.webhook.findMany({ + where: { + userId, + }, + }); +}; diff --git a/packages/trpc/server/router.ts b/packages/trpc/server/router.ts index aec70fd63..571d43669 100644 --- a/packages/trpc/server/router.ts +++ b/packages/trpc/server/router.ts @@ -11,6 +11,7 @@ import { teamRouter } from './team-router/router'; import { templateRouter } from './template-router/router'; import { router } from './trpc'; import { twoFactorAuthenticationRouter } from './two-factor-authentication-router/router'; +import { webhookRouter } from './webhook-router/router'; export const appRouter = router({ auth: authRouter, @@ -24,6 +25,7 @@ export const appRouter = router({ singleplayer: singleplayerRouter, team: teamRouter, template: templateRouter, + webhook: webhookRouter, twoFactorAuthentication: twoFactorAuthenticationRouter, }); diff --git a/packages/trpc/server/webhook-router/router.ts b/packages/trpc/server/webhook-router/router.ts new file mode 100644 index 000000000..ffdd7c4bf --- /dev/null +++ b/packages/trpc/server/webhook-router/router.ts @@ -0,0 +1,35 @@ +import { TRPCError } from '@trpc/server'; + +import { createWebhook } from '@documenso/lib/server-only/webhooks/create-webhook'; +import { getWebhooksByUserId } from '@documenso/lib/server-only/webhooks/get-webhooks-by-user-id'; + +import { authenticatedProcedure, router } from '../trpc'; +import { ZCreateWebhookFormSchema } from './schema'; + +export const webhookRouter = router({ + getWebhooks: authenticatedProcedure.query(async ({ ctx }) => { + try { + return await getWebhooksByUserId(ctx.user.id); + } catch (err) { + throw new TRPCError({ + code: 'BAD_REQUEST', + message: 'We were unable to fetch your webhooks. Please try again later.', + }); + } + }), + createWebhook: authenticatedProcedure + .input(ZCreateWebhookFormSchema) + .mutation(async ({ input, ctx }) => { + try { + return await createWebhook({ + ...input, + userId: ctx.user.id, + }); + } catch (err) { + throw new TRPCError({ + code: 'BAD_REQUEST', + message: 'We were unable to create this webhook. Please try again later.', + }); + } + }), +}); diff --git a/packages/trpc/server/webhook-router/schema.ts b/packages/trpc/server/webhook-router/schema.ts new file mode 100644 index 000000000..feceac054 --- /dev/null +++ b/packages/trpc/server/webhook-router/schema.ts @@ -0,0 +1,14 @@ +import { z } from 'zod'; + +import { WebhookTriggerEvents } from '@documenso/prisma/client'; + +export const ZCreateWebhookFormSchema = z.object({ + webhookUrl: z.string().url(), + eventTriggers: z + .array(z.nativeEnum(WebhookTriggerEvents)) + .min(1, { message: 'At least one event trigger is required' }), + secret: z.string().nullable(), + enabled: z.boolean(), +}); + +export type TCreateWebhookFormSchema = z.infer;