From c2ea5e5859427e817835aa13fde1f4d421135a7f Mon Sep 17 00:00:00 2001 From: David Nguyen Date: Tue, 6 Jan 2026 14:26:19 +1100 Subject: [PATCH] fix: migrate certificate generation (#2251) Generate certificates and audit logs using Konva instead of browserless. This should: - Reduce the changes of generations failing - Improve sealing speed --- apps/remix/app/components/forms/token.tsx | 8 +- .../document-signing-mobile-widget.tsx | 18 +- .../document-page-view-recent-activity.tsx | 44 +- .../envelope-editor-settings-dialog.tsx | 16 +- .../components/tables/document-logs-table.tsx | 2 +- .../tables/internal-audit-log-table.tsx | 22 +- .../_authenticated+/o.$orgUrl._index.tsx | 26 +- .../o.$orgUrl.settings.branding.tsx | 2 +- .../_internal+/[__htmltopdf]+/audit-log.tsx | 4 +- .../_recipient+/sign.$token+/_index.tsx | 12 +- .../_recipient+/sign.$token+/rejected.tsx | 8 +- .../_recipient+/sign.$token+/waiting.tsx | 4 +- .../visual-regression/blank-certificate.png | Bin 147496 -> 166551 bytes .../email/templates/confirm-team-email.tsx | 2 +- packages/email/templates/reset-password.tsx | 2 +- packages/lib/constants/app.ts | 9 + packages/lib/constants/pdf.ts | 5 + .../internal/seal-document.handler.ts | 160 ++-- .../htmltopdf/get-audit-logs-pdf.ts | 3 + .../htmltopdf/get-certificate-pdf.ts | 3 + .../server-only/pdf/generate-audit-log-pdf.ts | 60 ++ .../pdf/generate-certificate-pdf.ts | 160 ++++ .../lib/server-only/pdf/render-audit-logs.ts | 721 +++++++++++++++ .../lib/server-only/pdf/render-certificate.ts | 819 ++++++++++++++++++ packages/lib/types/document-audit-logs.ts | 2 + packages/lib/utils/document-audit-logs.ts | 17 +- packages/prisma/seed/documents.ts | 17 +- .../trpc/server/envelope-router/router.ts | 2 +- .../ui/primitives/data-table-pagination.tsx | 2 +- packages/ui/primitives/dialog.tsx | 8 +- .../primitives/document-flow/add-subject.tsx | 12 +- .../document-flow/document-flow-root.tsx | 23 +- .../document-flow/field-content.tsx | 10 +- packages/ui/primitives/sheet.tsx | 8 +- .../signature-pad/signature-pad-draw.tsx | 6 +- .../template-flow/add-template-settings.tsx | 24 +- 36 files changed, 2008 insertions(+), 233 deletions(-) create mode 100644 packages/lib/server-only/pdf/generate-audit-log-pdf.ts create mode 100644 packages/lib/server-only/pdf/generate-certificate-pdf.ts create mode 100644 packages/lib/server-only/pdf/render-audit-logs.ts create mode 100644 packages/lib/server-only/pdf/render-certificate.ts diff --git a/apps/remix/app/components/forms/token.tsx b/apps/remix/app/components/forms/token.tsx index 7311bfce4..95b1a8830 100644 --- a/apps/remix/app/components/forms/token.tsx +++ b/apps/remix/app/components/forms/token.tsx @@ -212,12 +212,12 @@ export const ApiTokenForm = ({ className, tokens }: ApiTokenFormProps) => { />
- + Never expire
@@ -254,14 +254,14 @@ export const ApiTokenForm = ({ className, tokens }: ApiTokenFormProps) => { > -

+

Your token was created successfully! Make sure to copy it because you won't be able to see it again!

-

+

{newlyCreatedToken.token}

diff --git a/apps/remix/app/components/general/document-signing/document-signing-mobile-widget.tsx b/apps/remix/app/components/general/document-signing/document-signing-mobile-widget.tsx index 6c765b2a1..40eb9dc92 100644 --- a/apps/remix/app/components/general/document-signing/document-signing-mobile-widget.tsx +++ b/apps/remix/app/components/general/document-signing/document-signing-mobile-widget.tsx @@ -35,7 +35,7 @@ export const DocumentSigningMobileWidget = () => { return (
-
+
{/* Main Header Bar */}
@@ -48,15 +48,15 @@ export const DocumentSigningMobileWidget = () => { aria-label={isExpanded ? 'Collapse' : 'Expand'} > {isExpanded ? ( - + ) : ( - + )} )}
-

+

{match(recipient.role) .with(RecipientRole.VIEWER, () => View Document) .with(RecipientRole.SIGNER, () => Sign Document) @@ -65,7 +65,7 @@ export const DocumentSigningMobileWidget = () => { .otherwise(() => null)}

-

+

{recipientFieldsRemaining.length === 0 ? ( match(recipient.role) .with(RecipientRole.VIEWER, () => ( @@ -102,11 +102,11 @@ export const DocumentSigningMobileWidget = () => { {recipient.role !== RecipientRole.VIEWER && recipient.role !== RecipientRole.ASSISTANT && (

-
+
{ {/* Expandable Content */} {isExpanded && ( -
+
{!hidePoweredBy && ( -
+
Powered by
diff --git a/apps/remix/app/components/general/document/document-page-view-recent-activity.tsx b/apps/remix/app/components/general/document/document-page-view-recent-activity.tsx index abeeacbc4..12febc4cb 100644 --- a/apps/remix/app/components/general/document/document-page-view-recent-activity.tsx +++ b/apps/remix/app/components/general/document/document-page-view-recent-activity.tsx @@ -22,7 +22,7 @@ export const DocumentPageViewRecentActivity = ({ documentId, userId, }: DocumentPageViewRecentActivityProps) => { - const { _ } = useLingui(); + const { _, i18n } = useLingui(); const { data, @@ -48,9 +48,9 @@ export const DocumentPageViewRecentActivity = ({ const documentAuditLogs = useMemo(() => (data?.pages ?? []).flatMap((page) => page.data), [data]); return ( -
+
-

+

Recent activity

@@ -59,18 +59,18 @@ export const DocumentPageViewRecentActivity = ({ {isLoading && (
- +
)} {isLoadingError && (
-

+

Unable to load document history

@@ -83,16 +83,16 @@ export const DocumentPageViewRecentActivity = ({ {hasNextPage && (
  • -
    +
    -
    -
    +
    +
    @@ -101,7 +101,7 @@ export const DocumentPageViewRecentActivity = ({ {documentAuditLogs.length === 0 && (
    -

    +

    No recent activity

    @@ -115,44 +115,44 @@ export const DocumentPageViewRecentActivity = ({ 'absolute left-0 top-0 flex w-6 justify-center', )} > -
    +
    -
    +
    {match(auditLog.type) .with(DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_COMPLETED, () => ( -
    +
    )) .with(DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_RECIPIENT_COMPLETED, () => ( -
    +
    )) .with(DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_RECIPIENT_REJECTED, () => ( -
    +
    )) .with(DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_OPENED, () => ( -
    +
    )) .otherwise(() => ( -
    +
    ))}

    - {formatDocumentAuditLogAction(_, auditLog, userId).description} + {formatDocumentAuditLogAction(i18n, auditLog, userId).description}

    -
  • 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 5ee92f661..475dc5b62 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 @@ -339,7 +339,7 @@ export const EnvelopeEditorSettingsDialog = ({ {/* Sidebar. */} -
    +
    Document Settings @@ -390,7 +390,7 @@ export const EnvelopeEditorSettingsDialog = ({ - + Controls the language for the document, including the language to be used for email notifications, and the final certificate @@ -441,7 +441,7 @@ export const EnvelopeEditorSettingsDialog = ({ }))} selectedValues={field.value} onChange={field.onChange} - className="bg-background w-full" + className="w-full bg-background" emptySelectionPlaceholder="Select signature types" /> @@ -518,7 +518,7 @@ export const EnvelopeEditorSettingsDialog = ({ - + Add an external ID to the document. This can be used to identify the document in external systems. @@ -548,7 +548,7 @@ export const EnvelopeEditorSettingsDialog = ({ - + Add a URL to redirect the user to once the document is signed @@ -576,7 +576,7 @@ export const EnvelopeEditorSettingsDialog = ({ - +

    Document Distribution Method @@ -735,14 +735,14 @@ export const EnvelopeEditorSettingsDialog = ({ - + -