feat: validate signers have signature fields before distribution (#2411)

API users were inadvertently sending documents without signature fields,
causing confusion for recipients and breaking their signing flows.

- Add getRecipientsWithMissingFields helper in recipients.ts
- Add server-side validation in sendDocument to block distribution
- Fix v1 API to return 400 instead of 500 for validation errors
- Consolidate UI signature field checks to use isSignatureFieldType
- Add E2E tests for both v1 and v2 APIs
This commit is contained in:
Lucas Smith
2026-01-26 15:22:12 +11:00
committed by GitHub
parent b538580a1e
commit 0a3e0b8727
19 changed files with 1031 additions and 67 deletions
+31
View File
@@ -2,9 +2,40 @@ import type { Envelope } from '@prisma/client';
import { type Field, type Recipient, RecipientRole, SigningStatus } from '@prisma/client';
import { z } from 'zod';
import { isSignatureFieldType } from '@documenso/prisma/guards/is-signature-field';
import { NEXT_PUBLIC_WEBAPP_URL } from '../constants/app';
import { extractLegacyIds } from '../universal/id';
/**
* Roles that require fields to be assigned before a document can be distributed.
*
* Currently only SIGNER requires a signature field.
*/
export const RECIPIENT_ROLES_THAT_REQUIRE_FIELDS = [RecipientRole.SIGNER] as const;
/**
* Returns recipients who are missing required fields for their role.
*
* Currently only SIGNERs are validated - they must have at least one signature field.
*/
export const getRecipientsWithMissingFields = <T extends Pick<Recipient, 'id' | 'role'>>(
recipients: T[],
fields: Pick<Field, 'type' | 'recipientId'>[],
): T[] => {
return recipients.filter((recipient) => {
if (recipient.role === RecipientRole.SIGNER) {
const hasSignatureField = fields.some(
(field) => field.recipientId === recipient.id && isSignatureFieldType(field.type),
);
return !hasSignatureField;
}
return false;
});
};
export const formatSigningLink = (token: string) => `${NEXT_PUBLIC_WEBAPP_URL()}/sign/${token}`;
/**