mirror of
https://github.com/documenso/documenso.git
synced 2026-07-21 23:43:43 +10:00
ba0dead96f
Currently direct templates can be created without the required signatures fields for signers. This means that the document can be fully signed by everyone but will ultimately fail the sealing step which leaves the document in an unrecoverable state.
568 lines
17 KiB
TypeScript
568 lines
17 KiB
TypeScript
import fs from 'node:fs';
|
|
import path from 'node:path';
|
|
|
|
import { ALIGNMENT_TEST_FIELDS } from '@documenso/app-tests/constants/field-alignment-pdf';
|
|
import { FIELD_META_TEST_FIELDS } from '@documenso/app-tests/constants/field-meta-pdf';
|
|
import { OVERFLOW_TEST_FIELDS } from '@documenso/app-tests/constants/field-overflow-pdf';
|
|
import { isBase64Image } from '@documenso/lib/constants/signatures';
|
|
import { incrementDocumentId, incrementTemplateId } from '@documenso/lib/server-only/envelope/increment-id';
|
|
import { SignatureLevel } from '@documenso/lib/types/signature-level';
|
|
import { nanoid, prefixedId } from '@documenso/lib/universal/id';
|
|
import { DIRECT_TEMPLATE_RECIPIENT_EMAIL, DIRECT_TEMPLATE_RECIPIENT_NAME } from '../../lib/constants/direct-templates';
|
|
import { prisma } from '..';
|
|
import {
|
|
DocumentDataType,
|
|
DocumentSource,
|
|
DocumentStatus,
|
|
EnvelopeType,
|
|
ReadStatus,
|
|
SendStatus,
|
|
SigningStatus,
|
|
} from '../client';
|
|
import { seedPendingDocument } from './documents';
|
|
import { seedDirectTemplate, seedTemplate } from './templates';
|
|
import { seedUser } from './users';
|
|
|
|
const createDocumentData = async ({ documentData }: { documentData: string }) => {
|
|
return prisma.documentData.create({
|
|
data: {
|
|
type: DocumentDataType.BYTES_64,
|
|
data: documentData,
|
|
initialData: documentData,
|
|
},
|
|
});
|
|
};
|
|
|
|
export const seedDatabase = async () => {
|
|
const examplePdf = fs.readFileSync(path.join(__dirname, '../../../assets/example.pdf')).toString('base64');
|
|
|
|
const exampleUserExists = await prisma.user.findFirst({
|
|
where: {
|
|
email: 'example@documenso.com',
|
|
},
|
|
});
|
|
|
|
const adminUserExists = await prisma.user.findFirst({
|
|
where: {
|
|
email: 'admin@documenso.com',
|
|
},
|
|
});
|
|
|
|
if (exampleUserExists || adminUserExists) {
|
|
return;
|
|
}
|
|
|
|
const exampleUser = await seedUser({
|
|
name: 'Example User',
|
|
email: 'example@documenso.com',
|
|
});
|
|
|
|
const adminUser = await seedUser({
|
|
name: 'Admin User',
|
|
email: 'admin@documenso.com',
|
|
isAdmin: true,
|
|
});
|
|
|
|
for (let i = 1; i <= 4; i++) {
|
|
const documentData = await createDocumentData({ documentData: examplePdf });
|
|
|
|
const documentId = await incrementDocumentId();
|
|
|
|
const documentMeta = await prisma.documentMeta.create({
|
|
data: {},
|
|
});
|
|
|
|
await prisma.envelope.create({
|
|
data: {
|
|
id: prefixedId('envelope'),
|
|
secondaryId: documentId.formattedDocumentId,
|
|
internalVersion: 1,
|
|
signatureLevel: SignatureLevel.SES,
|
|
type: EnvelopeType.DOCUMENT,
|
|
documentMetaId: documentMeta.id,
|
|
source: DocumentSource.DOCUMENT,
|
|
title: `Example Document ${i}`,
|
|
envelopeItems: {
|
|
create: {
|
|
id: prefixedId('envelope_item'),
|
|
title: `Example Document ${i}`,
|
|
documentDataId: documentData.id,
|
|
order: 1,
|
|
},
|
|
},
|
|
userId: exampleUser.user.id,
|
|
teamId: exampleUser.team.id,
|
|
recipients: {
|
|
create: {
|
|
name: String(adminUser.user.name),
|
|
email: adminUser.user.email,
|
|
token: Math.random().toString(36).slice(2, 9),
|
|
},
|
|
},
|
|
},
|
|
});
|
|
}
|
|
|
|
for (let i = 1; i <= 4; i++) {
|
|
const documentData = await createDocumentData({ documentData: examplePdf });
|
|
|
|
const documentId = await incrementDocumentId();
|
|
|
|
const documentMeta = await prisma.documentMeta.create({
|
|
data: {},
|
|
});
|
|
|
|
await prisma.envelope.create({
|
|
data: {
|
|
id: prefixedId('envelope'),
|
|
secondaryId: documentId.formattedDocumentId,
|
|
internalVersion: 1,
|
|
signatureLevel: SignatureLevel.SES,
|
|
type: EnvelopeType.DOCUMENT,
|
|
source: DocumentSource.DOCUMENT,
|
|
title: `Document ${i}`,
|
|
documentMetaId: documentMeta.id,
|
|
envelopeItems: {
|
|
create: {
|
|
id: prefixedId('envelope_item'),
|
|
title: `Document ${i}`,
|
|
documentDataId: documentData.id,
|
|
order: 1,
|
|
},
|
|
},
|
|
userId: adminUser.user.id,
|
|
teamId: adminUser.team.id,
|
|
recipients: {
|
|
create: {
|
|
name: String(exampleUser.user.name),
|
|
email: exampleUser.user.email,
|
|
token: Math.random().toString(36).slice(2, 9),
|
|
},
|
|
},
|
|
},
|
|
});
|
|
}
|
|
|
|
await seedPendingDocument(exampleUser.user, exampleUser.team.id, [adminUser.user], {
|
|
key: 'example-pending',
|
|
createDocumentOptions: {
|
|
title: 'Pending Document',
|
|
},
|
|
});
|
|
|
|
await seedPendingDocument(adminUser.user, adminUser.team.id, [exampleUser.user], {
|
|
key: 'admin-pending',
|
|
createDocumentOptions: {
|
|
title: 'Pending Document',
|
|
},
|
|
});
|
|
|
|
await Promise.all([
|
|
seedTemplate({
|
|
title: 'Template 1',
|
|
userId: exampleUser.user.id,
|
|
teamId: exampleUser.team.id,
|
|
}),
|
|
seedDirectTemplate({
|
|
title: 'Direct Template 1',
|
|
userId: exampleUser.user.id,
|
|
teamId: exampleUser.team.id,
|
|
}),
|
|
seedTemplate({
|
|
title: 'Template 1',
|
|
userId: adminUser.user.id,
|
|
teamId: adminUser.team.id,
|
|
}),
|
|
seedDirectTemplate({
|
|
title: 'Direct Template 1',
|
|
userId: adminUser.user.id,
|
|
teamId: adminUser.team.id,
|
|
}),
|
|
seedOverflowTestDocument({
|
|
userId: exampleUser.user.id,
|
|
teamId: exampleUser.team.id,
|
|
recipientName: exampleUser.user.name || '',
|
|
recipientEmail: exampleUser.user.email,
|
|
insertFields: false,
|
|
status: DocumentStatus.DRAFT,
|
|
}),
|
|
seedOverflowTestDocument({
|
|
userId: adminUser.user.id,
|
|
teamId: adminUser.team.id,
|
|
recipientName: adminUser.user.name || '',
|
|
recipientEmail: adminUser.user.email,
|
|
insertFields: false,
|
|
status: DocumentStatus.DRAFT,
|
|
}),
|
|
seedAlignmentTestDocument({
|
|
userId: exampleUser.user.id,
|
|
teamId: exampleUser.team.id,
|
|
recipientName: exampleUser.user.name || '',
|
|
recipientEmail: exampleUser.user.email,
|
|
insertFields: false,
|
|
status: DocumentStatus.DRAFT,
|
|
type: EnvelopeType.TEMPLATE,
|
|
}),
|
|
seedAlignmentTestDocument({
|
|
userId: exampleUser.user.id,
|
|
teamId: exampleUser.team.id,
|
|
recipientName: exampleUser.user.name || '',
|
|
recipientEmail: exampleUser.user.email,
|
|
insertFields: false,
|
|
status: DocumentStatus.DRAFT,
|
|
type: EnvelopeType.TEMPLATE,
|
|
isDirectTemplate: true,
|
|
directTemplateToken: 'example',
|
|
}),
|
|
seedAlignmentTestDocument({
|
|
userId: exampleUser.user.id,
|
|
teamId: exampleUser.team.id,
|
|
recipientName: exampleUser.user.name || '',
|
|
recipientEmail: exampleUser.user.email,
|
|
insertFields: false,
|
|
status: DocumentStatus.DRAFT,
|
|
}),
|
|
seedAlignmentTestDocument({
|
|
userId: exampleUser.user.id,
|
|
teamId: exampleUser.team.id,
|
|
recipientName: exampleUser.user.name || '',
|
|
recipientEmail: exampleUser.user.email,
|
|
insertFields: true,
|
|
status: DocumentStatus.PENDING,
|
|
}),
|
|
seedAlignmentTestDocument({
|
|
userId: adminUser.user.id,
|
|
teamId: adminUser.team.id,
|
|
recipientName: adminUser.user.name || '',
|
|
recipientEmail: adminUser.user.email,
|
|
insertFields: false,
|
|
status: DocumentStatus.DRAFT,
|
|
type: EnvelopeType.TEMPLATE,
|
|
}),
|
|
seedAlignmentTestDocument({
|
|
userId: adminUser.user.id,
|
|
teamId: adminUser.team.id,
|
|
recipientName: adminUser.user.name || '',
|
|
recipientEmail: adminUser.user.email,
|
|
insertFields: false,
|
|
status: DocumentStatus.DRAFT,
|
|
type: EnvelopeType.TEMPLATE,
|
|
isDirectTemplate: true,
|
|
directTemplateToken: 'admin',
|
|
}),
|
|
seedAlignmentTestDocument({
|
|
userId: adminUser.user.id,
|
|
teamId: adminUser.team.id,
|
|
recipientName: adminUser.user.name || '',
|
|
recipientEmail: adminUser.user.email,
|
|
insertFields: false,
|
|
status: DocumentStatus.DRAFT,
|
|
}),
|
|
seedAlignmentTestDocument({
|
|
userId: adminUser.user.id,
|
|
teamId: adminUser.team.id,
|
|
recipientName: adminUser.user.name || '',
|
|
recipientEmail: adminUser.user.email,
|
|
insertFields: true,
|
|
status: DocumentStatus.PENDING,
|
|
}),
|
|
]);
|
|
};
|
|
|
|
export const seedAlignmentTestDocument = async ({
|
|
userId,
|
|
teamId,
|
|
title = 'Envelope Full Field Test',
|
|
recipientName,
|
|
recipientEmail,
|
|
insertFields,
|
|
status,
|
|
type = EnvelopeType.DOCUMENT,
|
|
isDirectTemplate = false,
|
|
directTemplateToken,
|
|
}: {
|
|
userId: number;
|
|
teamId: number;
|
|
title?: string;
|
|
recipientName: string;
|
|
recipientEmail: string;
|
|
insertFields: boolean;
|
|
status: DocumentStatus;
|
|
type?: EnvelopeType;
|
|
isDirectTemplate?: boolean;
|
|
directTemplateToken?: string;
|
|
}) => {
|
|
const alignmentPdf = fs
|
|
.readFileSync(path.join(__dirname, '../../../assets/field-font-alignment.pdf'))
|
|
.toString('base64');
|
|
|
|
const fieldMetaPdf = fs.readFileSync(path.join(__dirname, '../../../assets/field-meta.pdf')).toString('base64');
|
|
|
|
const alignmentDocumentData = await createDocumentData({ documentData: alignmentPdf });
|
|
const fieldMetaDocumentData = await createDocumentData({ documentData: fieldMetaPdf });
|
|
|
|
const secondaryId =
|
|
type === EnvelopeType.DOCUMENT
|
|
? await incrementDocumentId().then((v) => v.formattedDocumentId)
|
|
: await incrementTemplateId().then((v) => v.formattedTemplateId);
|
|
|
|
const documentMeta = await prisma.documentMeta.create({
|
|
data: {},
|
|
});
|
|
|
|
const createdEnvelope = await prisma.envelope.create({
|
|
data: {
|
|
id: prefixedId('envelope'),
|
|
secondaryId,
|
|
internalVersion: 2,
|
|
signatureLevel: SignatureLevel.SES,
|
|
type,
|
|
documentMetaId: documentMeta.id,
|
|
source: DocumentSource.DOCUMENT,
|
|
title,
|
|
status,
|
|
envelopeItems: {
|
|
createMany: {
|
|
data: [
|
|
{
|
|
id: prefixedId('envelope_item'),
|
|
title: `alignment-pdf`,
|
|
documentDataId: alignmentDocumentData.id,
|
|
order: 1,
|
|
},
|
|
{
|
|
id: prefixedId('envelope_item'),
|
|
title: `field-meta-pdf`,
|
|
documentDataId: fieldMetaDocumentData.id,
|
|
order: 2,
|
|
},
|
|
],
|
|
},
|
|
},
|
|
userId,
|
|
teamId,
|
|
recipients: {
|
|
create: {
|
|
name: isDirectTemplate ? DIRECT_TEMPLATE_RECIPIENT_NAME : recipientName,
|
|
email: isDirectTemplate ? DIRECT_TEMPLATE_RECIPIENT_EMAIL : recipientEmail,
|
|
token: nanoid(),
|
|
sendStatus: status === 'DRAFT' ? SendStatus.NOT_SENT : SendStatus.SENT,
|
|
signingStatus: status === 'COMPLETED' ? SigningStatus.SIGNED : SigningStatus.NOT_SIGNED,
|
|
readStatus: status !== 'DRAFT' ? ReadStatus.OPENED : ReadStatus.NOT_OPENED,
|
|
},
|
|
},
|
|
},
|
|
include: {
|
|
recipients: true,
|
|
envelopeItems: true,
|
|
},
|
|
});
|
|
|
|
const { id, recipients, envelopeItems } = createdEnvelope;
|
|
|
|
if (isDirectTemplate) {
|
|
const directTemplateRecipient = recipients.find((recipient) => recipient.email === DIRECT_TEMPLATE_RECIPIENT_EMAIL);
|
|
|
|
if (!directTemplateRecipient) {
|
|
throw new Error('Need to create a direct template recipient');
|
|
}
|
|
|
|
await prisma.templateDirectLink.create({
|
|
data: {
|
|
envelopeId: id,
|
|
enabled: true,
|
|
token: directTemplateToken ?? Math.random().toString(),
|
|
directTemplateRecipientId: directTemplateRecipient.id,
|
|
},
|
|
});
|
|
}
|
|
|
|
const recipientId = recipients[0].id;
|
|
const envelopeItemAlignmentItem = envelopeItems.find((item) => item.order === 1)?.id;
|
|
const envelopeItemFieldMetaItem = envelopeItems.find((item) => item.order === 2)?.id;
|
|
|
|
if (!envelopeItemAlignmentItem || !envelopeItemFieldMetaItem) {
|
|
throw new Error('Envelope item not found');
|
|
}
|
|
|
|
await Promise.all(
|
|
ALIGNMENT_TEST_FIELDS.map(async (field) => {
|
|
await prisma.field.create({
|
|
data: {
|
|
...field,
|
|
recipientId,
|
|
envelopeItemId: envelopeItemAlignmentItem,
|
|
envelopeId: id,
|
|
customText: insertFields ? field.customText : '',
|
|
inserted:
|
|
insertFields && ((!field?.fieldMeta?.readOnly && Boolean(field.customText)) || field.type === 'SIGNATURE'),
|
|
signature:
|
|
field.signature && insertFields
|
|
? {
|
|
create: {
|
|
recipientId,
|
|
signatureImageAsBase64: isBase64Image(field.signature) ? field.signature : null,
|
|
typedSignature: isBase64Image(field.signature) ? null : field.signature,
|
|
},
|
|
}
|
|
: undefined,
|
|
},
|
|
});
|
|
}),
|
|
);
|
|
|
|
await Promise.all(
|
|
FIELD_META_TEST_FIELDS.map(async (field) => {
|
|
await prisma.field.create({
|
|
data: {
|
|
...field,
|
|
recipientId,
|
|
envelopeItemId: envelopeItemFieldMetaItem,
|
|
envelopeId: id,
|
|
customText: insertFields ? field.customText : '',
|
|
inserted:
|
|
insertFields && ((!field?.fieldMeta?.readOnly && Boolean(field.customText)) || field.type === 'SIGNATURE'),
|
|
signature:
|
|
field.signature && insertFields
|
|
? {
|
|
create: {
|
|
recipientId,
|
|
signatureImageAsBase64: isBase64Image(field.signature) ? field.signature : null,
|
|
typedSignature: isBase64Image(field.signature) ? null : field.signature,
|
|
},
|
|
}
|
|
: undefined,
|
|
},
|
|
});
|
|
}),
|
|
);
|
|
|
|
return await prisma.envelope.findFirstOrThrow({
|
|
where: {
|
|
id: createdEnvelope.id,
|
|
},
|
|
include: {
|
|
recipients: true,
|
|
envelopeItems: true,
|
|
},
|
|
});
|
|
};
|
|
|
|
export const seedOverflowTestDocument = async ({
|
|
userId,
|
|
teamId,
|
|
recipientName,
|
|
recipientEmail,
|
|
insertFields,
|
|
status,
|
|
}: {
|
|
userId: number;
|
|
teamId: number;
|
|
recipientName: string;
|
|
recipientEmail: string;
|
|
insertFields: boolean;
|
|
status: DocumentStatus;
|
|
}) => {
|
|
const overflowPdf = fs.readFileSync(path.join(__dirname, '../../../assets/field-overflow.pdf')).toString('base64');
|
|
|
|
const overflowDocumentData = await createDocumentData({ documentData: overflowPdf });
|
|
|
|
const secondaryId = await incrementDocumentId().then((v) => v.formattedDocumentId);
|
|
|
|
const documentMeta = await prisma.documentMeta.create({
|
|
data: {},
|
|
});
|
|
|
|
const createdEnvelope = await prisma.envelope.create({
|
|
data: {
|
|
id: prefixedId('envelope'),
|
|
secondaryId,
|
|
internalVersion: 2,
|
|
signatureLevel: SignatureLevel.SES,
|
|
type: EnvelopeType.DOCUMENT,
|
|
documentMetaId: documentMeta.id,
|
|
source: DocumentSource.DOCUMENT,
|
|
title: 'Overflow Test',
|
|
status,
|
|
envelopeItems: {
|
|
createMany: {
|
|
data: [
|
|
{
|
|
id: prefixedId('envelope_item'),
|
|
title: 'field-overflow',
|
|
documentDataId: overflowDocumentData.id,
|
|
order: 1,
|
|
},
|
|
],
|
|
},
|
|
},
|
|
userId,
|
|
teamId,
|
|
recipients: {
|
|
create: {
|
|
name: recipientName,
|
|
email: recipientEmail,
|
|
token: nanoid(),
|
|
sendStatus: status === 'DRAFT' ? SendStatus.NOT_SENT : SendStatus.SENT,
|
|
signingStatus: status === 'COMPLETED' ? SigningStatus.SIGNED : SigningStatus.NOT_SIGNED,
|
|
readStatus: status !== 'DRAFT' ? ReadStatus.OPENED : ReadStatus.NOT_OPENED,
|
|
},
|
|
},
|
|
},
|
|
include: {
|
|
recipients: true,
|
|
envelopeItems: true,
|
|
},
|
|
});
|
|
|
|
const { id, recipients, envelopeItems } = createdEnvelope;
|
|
|
|
const recipientId = recipients[0].id;
|
|
const envelopeItemId = envelopeItems.find((item) => item.order === 1)?.id;
|
|
|
|
if (!envelopeItemId) {
|
|
throw new Error('Envelope item not found');
|
|
}
|
|
|
|
await Promise.all(
|
|
OVERFLOW_TEST_FIELDS.map(async (field) => {
|
|
// Use seedFieldMeta (full meta with all defaults) instead of fieldMeta
|
|
// (minimal meta for API testing) since the seed bypasses API validation.
|
|
const { fieldMeta: _fieldMeta, seedFieldMeta, ...fieldData } = field;
|
|
|
|
await prisma.field.create({
|
|
data: {
|
|
...fieldData,
|
|
fieldMeta: seedFieldMeta,
|
|
recipientId,
|
|
envelopeItemId,
|
|
envelopeId: id,
|
|
customText: insertFields ? field.customText : '',
|
|
inserted:
|
|
insertFields && ((!seedFieldMeta?.readOnly && Boolean(field.customText)) || field.type === 'SIGNATURE'),
|
|
signature:
|
|
field.signature && insertFields
|
|
? {
|
|
create: {
|
|
recipientId,
|
|
signatureImageAsBase64: isBase64Image(field.signature) ? field.signature : null,
|
|
typedSignature: isBase64Image(field.signature) ? null : field.signature,
|
|
},
|
|
}
|
|
: undefined,
|
|
},
|
|
});
|
|
}),
|
|
);
|
|
|
|
return await prisma.envelope.findFirstOrThrow({
|
|
where: {
|
|
id: createdEnvelope.id,
|
|
},
|
|
include: {
|
|
recipients: true,
|
|
envelopeItems: true,
|
|
},
|
|
});
|
|
};
|