From 77889270b85238779154f5aa79bb2d098e520e65 Mon Sep 17 00:00:00 2001 From: ephraimduncan Date: Tue, 16 Jun 2026 11:40:36 +0000 Subject: [PATCH] fix: address per-document audit log review feedback - backfill DocumentMeta.includeAuditLog from the effective team/org setting so documents created before the column keep embedding audit logs after deploy - default includeAuditLog in the webhook payload schema so resending historical WebhookCall payloads stays backward-compatible - allow audit-log embedding to be changed after a document is sent (drop the server guards and the editor checkbox lock) - restore the commented change-detection block in updateEnvelope --- .../envelope-editor-settings-dialog.tsx | 2 +- .../document-meta/upsert-document-meta.ts | 30 +-------- .../server-only/envelope/update-envelope.ts | 66 ++++++++++--------- packages/lib/types/webhook-payload.ts | 2 +- .../migration.sql | 14 ++++ 5 files changed, 53 insertions(+), 61 deletions(-) diff --git a/apps/remix/app/components/general/envelope-editor/envelope-editor-settings-dialog.tsx b/apps/remix/app/components/general/envelope-editor/envelope-editor-settings-dialog.tsx index 605e630a6..6d021dcbd 100644 --- a/apps/remix/app/components/general/envelope-editor/envelope-editor-settings-dialog.tsx +++ b/apps/remix/app/components/general/envelope-editor/envelope-editor-settings-dialog.tsx @@ -915,7 +915,7 @@ export const EnvelopeEditorSettingsDialog = ({ trigger, ...props }: EnvelopeEdit field.onChange(Boolean(checked))} /> diff --git a/packages/lib/server-only/document-meta/upsert-document-meta.ts b/packages/lib/server-only/document-meta/upsert-document-meta.ts index 81b35b5db..4b690b539 100644 --- a/packages/lib/server-only/document-meta/upsert-document-meta.ts +++ b/packages/lib/server-only/document-meta/upsert-document-meta.ts @@ -2,13 +2,7 @@ import { DOCUMENT_AUDIT_LOG_TYPE } from '@documenso/lib/types/document-audit-log import type { ApiRequestMetadata } from '@documenso/lib/universal/extract-request-metadata'; import { createDocumentAuditLogData, diffDocumentMetaChanges } from '@documenso/lib/utils/document-audit-logs'; import { prisma } from '@documenso/prisma'; -import { - type DocumentDistributionMethod, - type DocumentSigningOrder, - EnvelopeType, - RecipientRole, - SendStatus, -} from '@prisma/client'; +import { type DocumentDistributionMethod, type DocumentSigningOrder, EnvelopeType } from '@prisma/client'; import type { SupportedLanguageCodes } from '../../constants/i18n'; import { AppError, AppErrorCode } from '../../errors/app-error'; @@ -99,28 +93,6 @@ export const updateDocumentMeta = async ({ } } - if ( - envelope.type === EnvelopeType.DOCUMENT && - includeAuditLog !== undefined && - includeAuditLog !== envelope.documentMeta.includeAuditLog - ) { - const sentRecipientCount = await prisma.recipient.count({ - where: { - envelopeId: envelope.id, - role: { - not: RecipientRole.CC, - }, - sendStatus: SendStatus.SENT, - }, - }); - - if (sentRecipientCount > 0) { - throw new AppError(AppErrorCode.INVALID_BODY, { - message: 'Audit log embedding can only be changed before the document is sent', - }); - } - } - return await prisma.$transaction(async (tx) => { const upsertedDocumentMeta = await tx.documentMeta.update({ where: { diff --git a/packages/lib/server-only/envelope/update-envelope.ts b/packages/lib/server-only/envelope/update-envelope.ts index 10b966231..4bb70e516 100644 --- a/packages/lib/server-only/envelope/update-envelope.ts +++ b/packages/lib/server-only/envelope/update-envelope.ts @@ -4,14 +4,7 @@ import type { CreateDocumentAuditLogDataResponse } from '@documenso/lib/utils/do import { createDocumentAuditLogData } from '@documenso/lib/utils/document-audit-logs'; import { prisma } from '@documenso/prisma'; import type { DocumentMeta, DocumentVisibility, Prisma, TemplateType } from '@prisma/client'; -import { - DocumentStatus, - EnvelopeType, - FolderType, - RecipientRole, - SendStatus, - WebhookTriggerEvents, -} from '@prisma/client'; +import { DocumentStatus, EnvelopeType, FolderType, WebhookTriggerEvents } from '@prisma/client'; import { isDeepEqual } from 'remeda'; import { TEAM_DOCUMENT_VISIBILITY_MAP } from '../../constants/teams'; @@ -144,28 +137,6 @@ export const updateEnvelope = async ({ } } - if ( - envelope.type === EnvelopeType.DOCUMENT && - meta.includeAuditLog !== undefined && - meta.includeAuditLog !== envelope.documentMeta.includeAuditLog - ) { - const sentRecipientCount = await prisma.recipient.count({ - where: { - envelopeId: envelope.id, - role: { - not: RecipientRole.CC, - }, - sendStatus: SendStatus.SENT, - }, - }); - - if (sentRecipientCount > 0) { - throw new AppError(AppErrorCode.INVALID_BODY, { - message: 'Audit log embedding can only be changed before the document is sent', - }); - } - } - let folderUpdateQuery: Prisma.FolderUpdateOneWithoutEnvelopesNestedInput | undefined; // Validate folder ID. @@ -211,6 +182,11 @@ export const updateEnvelope = async ({ const isGlobalActionSame = documentGlobalActionAuth === undefined || isDeepEqual(documentGlobalActionAuth, newGlobalActionAuth); const isDocumentVisibilitySame = data.visibility === undefined || data.visibility === envelope.visibility; + const isFolderSame = data.folderId === undefined || data.folderId === envelope.folderId; + const isTemplateTypeSame = data.templateType === undefined || data.templateType === envelope.templateType; + const isPublicDescriptionSame = + data.publicDescription === undefined || data.publicDescription === envelope.publicDescription; + const isPublicTitleSame = data.publicTitle === undefined || data.publicTitle === envelope.publicTitle; const auditLogs: CreateDocumentAuditLogDataResponse[] = []; @@ -290,6 +266,36 @@ export const updateEnvelope = async ({ ); } + // Todo: Decide if we want to log moving the document around. + // if (!isFolderSame) { + // auditLogs.push( + // createDocumentAuditLogData({ + // type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_FOLDER_UPDATED, + // envelopeId: envelope.id, + // metadata: requestMetadata, + // data: { + // from: envelope.folderId, + // to: data.folderId || '', + // }, + // }), + // ); + // } + + // Todo: Determine if changes are made + // Commented out since we didn't detect the changes to sequence. + // const isMetaSame = isDeepEqual(envelope.documentMeta, meta); + // Early return if nothing is required. + // if ( + // auditLogs.length === 0 && + // data.useLegacyFieldInsertion === undefined && + // isFolderSame && + // isTemplateTypeSame && + // isPublicDescriptionSame && + // isPublicTitleSame + // ) { + // return envelope; + // } + const updatedEnvelope = await prisma.$transaction(async (tx) => { const result = await tx.envelope.update({ where: { diff --git a/packages/lib/types/webhook-payload.ts b/packages/lib/types/webhook-payload.ts index 7042c0a7d..3ff9ee592 100644 --- a/packages/lib/types/webhook-payload.ts +++ b/packages/lib/types/webhook-payload.ts @@ -56,7 +56,7 @@ export const ZWebhookDocumentMetaSchema = z.object({ drawSignatureEnabled: z.boolean(), language: z.string(), distributionMethod: z.nativeEnum(DocumentDistributionMethod), - includeAuditLog: z.boolean(), + includeAuditLog: z.boolean().default(false), emailSettings: z.any().nullable(), }); diff --git a/packages/prisma/migrations/20260521000000_add_document_meta_include_audit_log/migration.sql b/packages/prisma/migrations/20260521000000_add_document_meta_include_audit_log/migration.sql index 6df1ec32b..4d9de4056 100644 --- a/packages/prisma/migrations/20260521000000_add_document_meta_include_audit_log/migration.sql +++ b/packages/prisma/migrations/20260521000000_add_document_meta_include_audit_log/migration.sql @@ -1,2 +1,16 @@ -- AlterTable ALTER TABLE "DocumentMeta" ADD COLUMN "includeAuditLog" BOOLEAN NOT NULL DEFAULT false; + +-- Backfill existing rows from the effective team/organisation setting. Before this +-- column existed the sealing flow read `includeAuditLog` from the team settings +-- (falling back to the organisation default), so documents created prior to this +-- migration must keep that resolved value to avoid silently dropping audit-log +-- embedding after deploy. +UPDATE "DocumentMeta" AS dm +SET "includeAuditLog" = COALESCE(tgs."includeAuditLog", ogs."includeAuditLog") +FROM "Envelope" e +JOIN "Team" t ON t."id" = e."teamId" +JOIN "TeamGlobalSettings" tgs ON tgs."id" = t."teamGlobalSettingsId" +JOIN "Organisation" o ON o."id" = t."organisationId" +JOIN "OrganisationGlobalSettings" ogs ON ogs."id" = o."organisationGlobalSettingsId" +WHERE e."documentMetaId" = dm."id";