mirror of
https://github.com/documenso/documenso.git
synced 2025-11-13 00:03:33 +10:00
feat: add envelopes (#2025)
This PR is handles the changes required to support envelopes. The new envelope editor/signing page will be hidden during release. The core changes here is to migrate the documents and templates model to a centralized envelopes model. Even though Documents and Templates are removed, from the user perspective they will still exist as we remap envelopes to documents and templates.
This commit is contained in:
@ -1,5 +1,5 @@
|
||||
import type { Document } from '@prisma/client';
|
||||
import { DocumentDataType } from '@prisma/client';
|
||||
import type { Envelope } from '@prisma/client';
|
||||
import { DocumentDataType, EnvelopeType } from '@prisma/client';
|
||||
|
||||
import { getServerLimits } from '@documenso/ee/server-only/limits/server';
|
||||
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
|
||||
@ -7,24 +7,25 @@ import { jobs } from '@documenso/lib/jobs/client';
|
||||
import { createDocumentData } from '@documenso/lib/server-only/document-data/create-document-data';
|
||||
import { getDocumentWithDetailsById } from '@documenso/lib/server-only/document/get-document-with-details-by-id';
|
||||
import { sendDocument } from '@documenso/lib/server-only/document/send-document';
|
||||
import { createEnvelope } from '@documenso/lib/server-only/envelope/create-envelope';
|
||||
import { duplicateEnvelope } from '@documenso/lib/server-only/envelope/duplicate-envelope';
|
||||
import { updateEnvelope } from '@documenso/lib/server-only/envelope/update-envelope';
|
||||
import {
|
||||
ZCreateDocumentFromDirectTemplateResponseSchema,
|
||||
createDocumentFromDirectTemplate,
|
||||
} from '@documenso/lib/server-only/template/create-document-from-direct-template';
|
||||
import { createDocumentFromTemplate } from '@documenso/lib/server-only/template/create-document-from-template';
|
||||
import {
|
||||
ZCreateTemplateResponseSchema,
|
||||
createTemplate,
|
||||
} from '@documenso/lib/server-only/template/create-template';
|
||||
import { createTemplateDirectLink } from '@documenso/lib/server-only/template/create-template-direct-link';
|
||||
import { deleteTemplate } from '@documenso/lib/server-only/template/delete-template';
|
||||
import { deleteTemplateDirectLink } from '@documenso/lib/server-only/template/delete-template-direct-link';
|
||||
import { duplicateTemplate } from '@documenso/lib/server-only/template/duplicate-template';
|
||||
import { findTemplates } from '@documenso/lib/server-only/template/find-templates';
|
||||
import { getTemplateById } from '@documenso/lib/server-only/template/get-template-by-id';
|
||||
import { toggleTemplateDirectLink } from '@documenso/lib/server-only/template/toggle-template-direct-link';
|
||||
import { updateTemplate } from '@documenso/lib/server-only/template/update-template';
|
||||
import { getPresignPostUrl } from '@documenso/lib/universal/upload/server-actions';
|
||||
import { mapSecondaryIdToTemplateId } from '@documenso/lib/utils/envelope';
|
||||
import { mapFieldToLegacyField } from '@documenso/lib/utils/fields';
|
||||
import { mapRecipientToLegacyRecipient } from '@documenso/lib/utils/recipients';
|
||||
import { mapEnvelopeToTemplateLite } from '@documenso/lib/utils/templates';
|
||||
|
||||
import { ZGenericSuccessResponse, ZSuccessResponseSchema } from '../document-router/schema';
|
||||
import { authenticatedProcedure, maybeAuthenticatedProcedure, router } from '../trpc';
|
||||
@ -36,6 +37,7 @@ import {
|
||||
ZCreateTemplateDirectLinkRequestSchema,
|
||||
ZCreateTemplateDirectLinkResponseSchema,
|
||||
ZCreateTemplateMutationSchema,
|
||||
ZCreateTemplateResponseSchema,
|
||||
ZCreateTemplateV2RequestSchema,
|
||||
ZCreateTemplateV2ResponseSchema,
|
||||
ZDeleteTemplateDirectLinkRequestSchema,
|
||||
@ -77,11 +79,44 @@ export const templateRouter = router({
|
||||
},
|
||||
});
|
||||
|
||||
return await findTemplates({
|
||||
const result = await findTemplates({
|
||||
userId: ctx.user.id,
|
||||
teamId,
|
||||
...input,
|
||||
});
|
||||
|
||||
// Remapping for backwards compatibility.
|
||||
return {
|
||||
...result,
|
||||
data: result.data.map((envelope) => {
|
||||
const legacyTemplateId = mapSecondaryIdToTemplateId(envelope.secondaryId);
|
||||
|
||||
return {
|
||||
id: legacyTemplateId,
|
||||
envelopeId: envelope.id,
|
||||
type: envelope.templateType,
|
||||
visibility: envelope.visibility,
|
||||
externalId: envelope.externalId,
|
||||
title: envelope.title,
|
||||
userId: envelope.userId,
|
||||
teamId: envelope.teamId,
|
||||
authOptions: envelope.authOptions,
|
||||
createdAt: envelope.createdAt,
|
||||
updatedAt: envelope.updatedAt,
|
||||
publicTitle: envelope.publicTitle,
|
||||
publicDescription: envelope.publicDescription,
|
||||
folderId: envelope.folderId,
|
||||
useLegacyFieldInsertion: envelope.useLegacyFieldInsertion,
|
||||
team: envelope.team,
|
||||
fields: envelope.fields.map((field) => mapFieldToLegacyField(field, envelope)),
|
||||
recipients: envelope.recipients.map((recipient) =>
|
||||
mapRecipientToLegacyRecipient(recipient, envelope),
|
||||
),
|
||||
templateMeta: envelope.documentMeta,
|
||||
directLink: envelope.directLink,
|
||||
};
|
||||
}),
|
||||
};
|
||||
}),
|
||||
|
||||
/**
|
||||
@ -109,7 +144,10 @@ export const templateRouter = router({
|
||||
});
|
||||
|
||||
return await getTemplateById({
|
||||
id: templateId,
|
||||
id: {
|
||||
type: 'templateId',
|
||||
id: templateId,
|
||||
},
|
||||
userId: ctx.user.id,
|
||||
teamId,
|
||||
});
|
||||
@ -121,7 +159,7 @@ export const templateRouter = router({
|
||||
* @private
|
||||
*/
|
||||
createTemplate: authenticatedProcedure
|
||||
// .meta({
|
||||
// .meta({ // Note before releasing this to public, update the response schema to be correct.
|
||||
// openapi: {
|
||||
// method: 'POST',
|
||||
// path: '/template/create',
|
||||
@ -142,15 +180,26 @@ export const templateRouter = router({
|
||||
},
|
||||
});
|
||||
|
||||
return await createTemplate({
|
||||
const envelope = await createEnvelope({
|
||||
userId: ctx.user.id,
|
||||
teamId,
|
||||
templateDocumentDataId,
|
||||
internalVersion: 1,
|
||||
data: {
|
||||
type: EnvelopeType.TEMPLATE,
|
||||
title,
|
||||
folderId,
|
||||
envelopeItems: [
|
||||
{
|
||||
documentDataId: templateDocumentDataId,
|
||||
},
|
||||
],
|
||||
},
|
||||
requestMetadata: ctx.metadata,
|
||||
});
|
||||
|
||||
return {
|
||||
legacyTemplateId: mapSecondaryIdToTemplateId(envelope.secondaryId),
|
||||
};
|
||||
}),
|
||||
|
||||
/**
|
||||
@ -197,26 +246,38 @@ export const templateRouter = router({
|
||||
type: DocumentDataType.S3_PATH,
|
||||
});
|
||||
|
||||
const createdTemplate = await createTemplate({
|
||||
const createdTemplate = await createEnvelope({
|
||||
userId: user.id,
|
||||
teamId,
|
||||
templateDocumentDataId: templateDocumentData.id,
|
||||
internalVersion: 1,
|
||||
data: {
|
||||
type: EnvelopeType.TEMPLATE,
|
||||
title,
|
||||
envelopeItems: [
|
||||
{
|
||||
documentDataId: templateDocumentData.id,
|
||||
},
|
||||
],
|
||||
folderId,
|
||||
externalId,
|
||||
externalId: externalId ?? undefined,
|
||||
visibility,
|
||||
globalAccessAuth,
|
||||
globalActionAuth,
|
||||
templateType: type,
|
||||
publicTitle,
|
||||
publicDescription,
|
||||
type,
|
||||
},
|
||||
meta,
|
||||
requestMetadata: ctx.metadata,
|
||||
});
|
||||
|
||||
const legacyTemplateId = mapSecondaryIdToTemplateId(createdTemplate.secondaryId);
|
||||
|
||||
const fullTemplate = await getTemplateById({
|
||||
id: createdTemplate.id,
|
||||
id: {
|
||||
type: 'templateId',
|
||||
id: legacyTemplateId,
|
||||
},
|
||||
userId: user.id,
|
||||
teamId,
|
||||
});
|
||||
@ -252,13 +313,22 @@ export const templateRouter = router({
|
||||
},
|
||||
});
|
||||
|
||||
return await updateTemplate({
|
||||
const envelope = await updateEnvelope({
|
||||
userId,
|
||||
teamId,
|
||||
templateId,
|
||||
data,
|
||||
id: {
|
||||
type: 'templateId',
|
||||
id: templateId,
|
||||
},
|
||||
data: {
|
||||
...data,
|
||||
templateType: data?.type, // Backwards compatibility.
|
||||
},
|
||||
meta,
|
||||
requestMetadata: ctx.metadata,
|
||||
});
|
||||
|
||||
return mapEnvelopeToTemplateLite(envelope);
|
||||
}),
|
||||
|
||||
/**
|
||||
@ -285,11 +355,16 @@ export const templateRouter = router({
|
||||
},
|
||||
});
|
||||
|
||||
return await duplicateTemplate({
|
||||
const duplicatedEnvelope = await duplicateEnvelope({
|
||||
userId: ctx.user.id,
|
||||
teamId,
|
||||
templateId,
|
||||
id: {
|
||||
type: 'templateId',
|
||||
id: templateId,
|
||||
},
|
||||
});
|
||||
|
||||
return mapEnvelopeToTemplateLite(duplicatedEnvelope.envelope);
|
||||
}),
|
||||
|
||||
/**
|
||||
@ -317,7 +392,14 @@ export const templateRouter = router({
|
||||
},
|
||||
});
|
||||
|
||||
await deleteTemplate({ userId, id: templateId, teamId });
|
||||
await deleteTemplate({
|
||||
userId,
|
||||
id: {
|
||||
type: 'templateId',
|
||||
id: templateId,
|
||||
},
|
||||
teamId,
|
||||
});
|
||||
|
||||
return ZGenericSuccessResponse;
|
||||
}),
|
||||
@ -360,12 +442,25 @@ export const templateRouter = router({
|
||||
throw new Error('You have reached your document limit.');
|
||||
}
|
||||
|
||||
const document: Document = await createDocumentFromTemplate({
|
||||
templateId,
|
||||
// Backwards compatibility mapping since we need the envelopeItemId for the custom document data.
|
||||
const customDocumentData = customDocumentDataId
|
||||
? [
|
||||
{
|
||||
documentDataId: customDocumentDataId,
|
||||
envelopeItemId: undefined,
|
||||
},
|
||||
]
|
||||
: input.customDocumentData || [];
|
||||
|
||||
const envelope: Envelope = await createDocumentFromTemplate({
|
||||
id: {
|
||||
type: 'templateId',
|
||||
id: templateId,
|
||||
},
|
||||
teamId,
|
||||
userId: ctx.user.id,
|
||||
recipients,
|
||||
customDocumentDataId,
|
||||
customDocumentData,
|
||||
requestMetadata: ctx.metadata,
|
||||
folderId,
|
||||
prefillFields,
|
||||
@ -373,7 +468,10 @@ export const templateRouter = router({
|
||||
|
||||
if (distributeDocument) {
|
||||
await sendDocument({
|
||||
documentId: document.id,
|
||||
id: {
|
||||
type: 'envelopeId',
|
||||
id: envelope.id,
|
||||
},
|
||||
userId: ctx.user.id,
|
||||
teamId,
|
||||
requestMetadata: ctx.metadata,
|
||||
@ -385,7 +483,10 @@ export const templateRouter = router({
|
||||
}
|
||||
|
||||
return getDocumentWithDetailsById({
|
||||
documentId: document.id,
|
||||
id: {
|
||||
type: 'envelopeId',
|
||||
id: envelope.id,
|
||||
},
|
||||
userId: ctx.user.id,
|
||||
teamId,
|
||||
});
|
||||
@ -470,7 +571,14 @@ export const templateRouter = router({
|
||||
},
|
||||
});
|
||||
|
||||
const template = await getTemplateById({ id: templateId, teamId, userId: ctx.user.id });
|
||||
const template = await getTemplateById({
|
||||
id: {
|
||||
type: 'templateId',
|
||||
id: templateId,
|
||||
},
|
||||
teamId,
|
||||
userId: ctx.user.id,
|
||||
});
|
||||
|
||||
const limits = await getServerLimits({ userId: ctx.user.id, teamId: template.teamId });
|
||||
|
||||
@ -480,7 +588,15 @@ export const templateRouter = router({
|
||||
});
|
||||
}
|
||||
|
||||
return await createTemplateDirectLink({ userId, teamId, templateId, directRecipientId });
|
||||
return await createTemplateDirectLink({
|
||||
userId,
|
||||
teamId,
|
||||
id: {
|
||||
type: 'templateId',
|
||||
id: templateId,
|
||||
},
|
||||
directRecipientId,
|
||||
});
|
||||
}),
|
||||
|
||||
/**
|
||||
@ -569,7 +685,10 @@ export const templateRouter = router({
|
||||
}
|
||||
|
||||
const template = await getTemplateById({
|
||||
id: templateId,
|
||||
id: {
|
||||
type: 'templateId',
|
||||
id: templateId,
|
||||
},
|
||||
teamId,
|
||||
userId: user.id,
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user