diff --git a/packages/lib/server-only/webhooks/trigger-test-webhook.ts b/packages/lib/server-only/webhooks/trigger-test-webhook.ts index a45549bbb..40ad06b2a 100644 --- a/packages/lib/server-only/webhooks/trigger-test-webhook.ts +++ b/packages/lib/server-only/webhooks/trigger-test-webhook.ts @@ -32,7 +32,7 @@ export const triggerTestWebhook = async ({ try { await triggerWebhook({ event, - data: samplePayload, + data: samplePayload.payload, userId, teamId, }); diff --git a/packages/lib/types/webhook-payload.ts b/packages/lib/types/webhook-payload.ts index 398a5e444..a3462da8d 100644 --- a/packages/lib/types/webhook-payload.ts +++ b/packages/lib/types/webhook-payload.ts @@ -1,4 +1,4 @@ -import type { DocumentMeta, Envelope, Recipient, WebhookTriggerEvents } from '@prisma/client'; +import type { DocumentMeta, Envelope, Recipient } from '@prisma/client'; import { DocumentDistributionMethod, DocumentSigningOrder, @@ -10,6 +10,7 @@ import { RecipientRole, SendStatus, SigningStatus, + WebhookTriggerEvents, } from '@prisma/client'; import { z } from 'zod'; @@ -25,10 +26,10 @@ export const ZWebhookRecipientSchema = z.object({ email: z.string(), name: z.string(), token: z.string(), - documentDeletedAt: z.date().nullable(), - expiresAt: z.date().nullable(), - expirationNotifiedAt: z.date().nullable(), - signedAt: z.date().nullable(), + documentDeletedAt: z.coerce.date().nullable(), + expiresAt: z.coerce.date().nullable(), + expirationNotifiedAt: z.coerce.date().nullable(), + signedAt: z.coerce.date().nullable(), authOptions: z.any().nullable(), signingOrder: z.number().nullable(), rejectionReason: z.string().nullable(), @@ -70,10 +71,10 @@ export const ZWebhookDocumentSchema = z.object({ visibility: z.nativeEnum(DocumentVisibility), title: z.string(), status: z.nativeEnum(DocumentStatus), - createdAt: z.date(), - updatedAt: z.date(), - completedAt: z.date().nullable(), - deletedAt: z.date().nullable(), + createdAt: z.coerce.date(), + updatedAt: z.coerce.date(), + completedAt: z.coerce.date().nullable(), + deletedAt: z.coerce.date().nullable(), teamId: z.number().nullable(), templateId: z.number().nullable(), source: z.nativeEnum(DocumentSource), @@ -86,15 +87,20 @@ export const ZWebhookDocumentSchema = z.object({ Recipient: z.array(ZWebhookRecipientSchema), }); +/** + * Schema for the full webhook delivery envelope (what receivers see on the wire + * and what is persisted to `WebhookCall.requestBody`). + */ +export const ZWebhookPayloadSchema = z.object({ + event: z.nativeEnum(WebhookTriggerEvents), + payload: ZWebhookDocumentSchema, + createdAt: z.string(), + webhookEndpoint: z.string(), +}); + export type TWebhookRecipient = z.infer; export type TWebhookDocument = z.infer; - -export type WebhookPayload = { - event: WebhookTriggerEvents; - payload: TWebhookDocument; - createdAt: string; - webhookEndpoint: string; -}; +export type WebhookPayload = z.infer; export const mapEnvelopeToWebhookDocumentPayload = ( envelope: Envelope & { diff --git a/packages/trpc/server/webhook-router/resend-webhook-call.ts b/packages/trpc/server/webhook-router/resend-webhook-call.ts index 298def29c..fa73fbf2d 100644 --- a/packages/trpc/server/webhook-router/resend-webhook-call.ts +++ b/packages/trpc/server/webhook-router/resend-webhook-call.ts @@ -1,6 +1,7 @@ import { TEAM_MEMBER_ROLE_PERMISSIONS_MAP } from '@documenso/lib/constants/teams'; import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error'; import { jobs } from '@documenso/lib/jobs/client'; +import { ZWebhookPayloadSchema } from '@documenso/lib/types/webhook-payload'; import { buildTeamWhereQuery } from '@documenso/lib/utils/teams'; import { prisma } from '@documenso/prisma'; @@ -39,12 +40,16 @@ export const resendWebhookCallRoute = authenticatedProcedure throw new AppError(AppErrorCode.NOT_FOUND); } + // `requestBody` stores the full delivery envelope; unwrap to the inner + // document so the handler doesn't wrap it a second time. + const { payload: data } = ZWebhookPayloadSchema.parse(webhookCall.requestBody); + await jobs.triggerJob({ name: 'internal.execute-webhook', payload: { event: webhookCall.event, webhookId, - data: webhookCall.requestBody, + data, }, }); });