Files
sign/packages/lib/server-only/document/send-document.tsx

276 lines
7.7 KiB
TypeScript
Raw Normal View History

import { createElement } from 'react';
import { mailer } from '@documenso/email/mailer';
import { render } from '@documenso/email/render';
import { DocumentInviteEmailTemplate } from '@documenso/email/templates/document-invite';
2023-09-20 13:48:30 +10:00
import { FROM_ADDRESS, FROM_NAME } from '@documenso/lib/constants/email';
import { sealDocument } from '@documenso/lib/server-only/document/seal-document';
import { updateDocument } from '@documenso/lib/server-only/document/update-document';
import { DOCUMENT_AUDIT_LOG_TYPE } from '@documenso/lib/types/document-audit-logs';
import type { RequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
import { putPdfFile } from '@documenso/lib/universal/upload/put-file';
import { createDocumentAuditLogData } from '@documenso/lib/utils/document-audit-logs';
import { renderCustomEmailTemplate } from '@documenso/lib/utils/render-custom-email-template';
import { prisma } from '@documenso/prisma';
import { DocumentStatus, RecipientRole, SendStatus } from '@documenso/prisma/client';
2024-02-24 11:18:58 +02:00
import { WebhookTriggerEvents } from '@documenso/prisma/client';
import { NEXT_PUBLIC_WEBAPP_URL } from '../../constants/app';
2024-02-12 02:02:43 +00:00
import {
RECIPIENT_ROLES_DESCRIPTION,
RECIPIENT_ROLE_TO_EMAIL_TYPE,
} from '../../constants/recipient-roles';
import { getFile } from '../../universal/upload/get-file';
import { insertFormValuesInPdf } from '../pdf/insert-form-values-in-pdf';
import { triggerWebhook } from '../webhooks/trigger/trigger-webhook';
export type SendDocumentOptions = {
documentId: number;
userId: number;
2024-02-22 13:39:34 +11:00
teamId?: number;
requestMetadata?: RequestMetadata;
2023-09-20 09:06:28 +00:00
};
export const sendDocument = async ({
documentId,
userId,
2024-02-22 13:39:34 +11:00
teamId,
requestMetadata,
}: SendDocumentOptions) => {
const user = await prisma.user.findFirstOrThrow({
where: {
id: userId,
},
select: {
id: true,
name: true,
email: true,
},
});
const document = await prisma.document.findUnique({
where: {
id: documentId,
2024-02-22 13:39:34 +11:00
...(teamId
? {
team: {
id: teamId,
members: {
some: {
userId,
},
},
},
2024-02-22 13:39:34 +11:00
}
: {
userId,
teamId: null,
}),
},
include: {
Recipient: true,
documentMeta: true,
documentData: true,
},
});
const customEmail = document?.documentMeta;
if (!document) {
throw new Error('Document not found');
}
if (document.Recipient.length === 0) {
throw new Error('Document has no recipients');
}
if (document.status === DocumentStatus.COMPLETED) {
throw new Error('Can not send completed document');
}
const { documentData } = document;
if (!documentData.data) {
throw new Error('Document data not found');
}
if (document.formValues) {
const file = await getFile(documentData);
const prefilled = await insertFormValuesInPdf({
pdf: Buffer.from(file),
formValues: document.formValues as Record<string, string | number | boolean>,
});
const newDocumentData = await putPdfFile({
name: document.title,
type: 'application/pdf',
arrayBuffer: async () => Promise.resolve(prefilled),
});
const result = await prisma.document.update({
where: {
id: document.id,
},
data: {
documentDataId: newDocumentData.id,
},
});
Object.assign(document, result);
}
2023-12-02 12:43:43 +11:00
await Promise.all(
document.Recipient.map(async (recipient) => {
if (recipient.sendStatus === SendStatus.SENT || recipient.role === RecipientRole.CC) {
return;
}
const recipientEmailType = RECIPIENT_ROLE_TO_EMAIL_TYPE[recipient.role];
const { email, name } = recipient;
feat: update emails for self-signer (#1108) ## Description Updated the email content based on whether the document owner is a recipient or not. If the document owner is a recipient (self-signer): * the email subject will be `Please view/sign/approve your document` * the email header will be `Please view/sign/approve your document "<your-doc-title>"` * the email content will be `You have initiated the document "<your-doc-title>" that requires you to view/sign/approve it.` Otherwise: * the email subject will be `Please view/sign/approve this document` * the email header will be `<doc-owner> has invited you to view/sign/approve "<doc-title>"` * the email content will be `<doc-owner> has invited you to view/sign/approve the document "<doc-title>".` ## Related Issue Related to #1091 ## Testing Performed Tested the feature with a different number of recipients (including and excluding the document owner - self-signer). Tested both the sending and resending functionality. ## Checklist - [x] I have tested these changes locally and they work as expected. - [ ] I have added/updated tests that prove the effectiveness of these changes. - [ ] I have updated the documentation to reflect these changes, if applicable. - [x] I have followed the project's coding style guidelines. - [ ] I have addressed the code review feedback from the previous submission, if applicable. ## UI Screenshots ![CleanShot 2024-04-18 at 12 26 11@2x](https://github.com/documenso/documenso/assets/25515812/ca80f625-befb-4cbc-a541-f2186379d2e8) ![CleanShot 2024-04-18 at 12 27 40@2x](https://github.com/documenso/documenso/assets/25515812/8bcbb6fc-ba98-4fa1-8538-2d062febd27b) ![CleanShot 2024-04-18 at 12 27 53@2x](https://github.com/documenso/documenso/assets/25515812/25d77d98-b5ec-4270-8ffa-43774fe70526) ![CleanShot 2024-04-18 at 12 30 00@2x](https://github.com/documenso/documenso/assets/25515812/a90bb8e3-3ea8-42ff-9971-559b3e81ae6f) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Summary by CodeRabbit - **New Features** - Enhanced the document invitation components to support scenarios where the recipient is also the sender, providing customized email content and subject lines. - Introduced new properties in email templates to improve clarity and relevance based on the user's role in the document signing process. - **Refactor** - Updated components to use a more flexible `headerContent` property for displaying invitation headers, replacing previous individual inviter details. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
2024-04-19 13:45:33 +03:00
const selfSigner = email === user.email;
const selfSignerCustomEmail = `You have initiated the document ${`"${document.title}"`} that requires you to ${RECIPIENT_ROLES_DESCRIPTION[
recipient.role
].actionVerb.toLowerCase()} it.`;
const customEmailTemplate = {
'signer.name': name,
'signer.email': email,
'document.name': document.title,
};
const assetBaseUrl = NEXT_PUBLIC_WEBAPP_URL() || 'http://localhost:3000';
const signDocumentLink = `${NEXT_PUBLIC_WEBAPP_URL()}/sign/${recipient.token}`;
2023-08-17 19:56:18 +10:00
const template = createElement(DocumentInviteEmailTemplate, {
documentName: document.title,
inviterName: user.name || undefined,
inviterEmail: user.email,
2023-08-17 19:56:18 +10:00
assetBaseUrl,
signDocumentLink,
feat: update emails for self-signer (#1108) ## Description Updated the email content based on whether the document owner is a recipient or not. If the document owner is a recipient (self-signer): * the email subject will be `Please view/sign/approve your document` * the email header will be `Please view/sign/approve your document "<your-doc-title>"` * the email content will be `You have initiated the document "<your-doc-title>" that requires you to view/sign/approve it.` Otherwise: * the email subject will be `Please view/sign/approve this document` * the email header will be `<doc-owner> has invited you to view/sign/approve "<doc-title>"` * the email content will be `<doc-owner> has invited you to view/sign/approve the document "<doc-title>".` ## Related Issue Related to #1091 ## Testing Performed Tested the feature with a different number of recipients (including and excluding the document owner - self-signer). Tested both the sending and resending functionality. ## Checklist - [x] I have tested these changes locally and they work as expected. - [ ] I have added/updated tests that prove the effectiveness of these changes. - [ ] I have updated the documentation to reflect these changes, if applicable. - [x] I have followed the project's coding style guidelines. - [ ] I have addressed the code review feedback from the previous submission, if applicable. ## UI Screenshots ![CleanShot 2024-04-18 at 12 26 11@2x](https://github.com/documenso/documenso/assets/25515812/ca80f625-befb-4cbc-a541-f2186379d2e8) ![CleanShot 2024-04-18 at 12 27 40@2x](https://github.com/documenso/documenso/assets/25515812/8bcbb6fc-ba98-4fa1-8538-2d062febd27b) ![CleanShot 2024-04-18 at 12 27 53@2x](https://github.com/documenso/documenso/assets/25515812/25d77d98-b5ec-4270-8ffa-43774fe70526) ![CleanShot 2024-04-18 at 12 30 00@2x](https://github.com/documenso/documenso/assets/25515812/a90bb8e3-3ea8-42ff-9971-559b3e81ae6f) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Summary by CodeRabbit - **New Features** - Enhanced the document invitation components to support scenarios where the recipient is also the sender, providing customized email content and subject lines. - Introduced new properties in email templates to improve clarity and relevance based on the user's role in the document signing process. - **Refactor** - Updated components to use a more flexible `headerContent` property for displaying invitation headers, replacing previous individual inviter details. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
2024-04-19 13:45:33 +03:00
customBody: renderCustomEmailTemplate(
selfSigner && !customEmail?.message ? selfSignerCustomEmail : customEmail?.message || '',
feat: update emails for self-signer (#1108) ## Description Updated the email content based on whether the document owner is a recipient or not. If the document owner is a recipient (self-signer): * the email subject will be `Please view/sign/approve your document` * the email header will be `Please view/sign/approve your document "<your-doc-title>"` * the email content will be `You have initiated the document "<your-doc-title>" that requires you to view/sign/approve it.` Otherwise: * the email subject will be `Please view/sign/approve this document` * the email header will be `<doc-owner> has invited you to view/sign/approve "<doc-title>"` * the email content will be `<doc-owner> has invited you to view/sign/approve the document "<doc-title>".` ## Related Issue Related to #1091 ## Testing Performed Tested the feature with a different number of recipients (including and excluding the document owner - self-signer). Tested both the sending and resending functionality. ## Checklist - [x] I have tested these changes locally and they work as expected. - [ ] I have added/updated tests that prove the effectiveness of these changes. - [ ] I have updated the documentation to reflect these changes, if applicable. - [x] I have followed the project's coding style guidelines. - [ ] I have addressed the code review feedback from the previous submission, if applicable. ## UI Screenshots ![CleanShot 2024-04-18 at 12 26 11@2x](https://github.com/documenso/documenso/assets/25515812/ca80f625-befb-4cbc-a541-f2186379d2e8) ![CleanShot 2024-04-18 at 12 27 40@2x](https://github.com/documenso/documenso/assets/25515812/8bcbb6fc-ba98-4fa1-8538-2d062febd27b) ![CleanShot 2024-04-18 at 12 27 53@2x](https://github.com/documenso/documenso/assets/25515812/25d77d98-b5ec-4270-8ffa-43774fe70526) ![CleanShot 2024-04-18 at 12 30 00@2x](https://github.com/documenso/documenso/assets/25515812/a90bb8e3-3ea8-42ff-9971-559b3e81ae6f) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Summary by CodeRabbit - **New Features** - Enhanced the document invitation components to support scenarios where the recipient is also the sender, providing customized email content and subject lines. - Introduced new properties in email templates to improve clarity and relevance based on the user's role in the document signing process. - **Refactor** - Updated components to use a more flexible `headerContent` property for displaying invitation headers, replacing previous individual inviter details. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
2024-04-19 13:45:33 +03:00
customEmailTemplate,
),
role: recipient.role,
feat: update emails for self-signer (#1108) ## Description Updated the email content based on whether the document owner is a recipient or not. If the document owner is a recipient (self-signer): * the email subject will be `Please view/sign/approve your document` * the email header will be `Please view/sign/approve your document "<your-doc-title>"` * the email content will be `You have initiated the document "<your-doc-title>" that requires you to view/sign/approve it.` Otherwise: * the email subject will be `Please view/sign/approve this document` * the email header will be `<doc-owner> has invited you to view/sign/approve "<doc-title>"` * the email content will be `<doc-owner> has invited you to view/sign/approve the document "<doc-title>".` ## Related Issue Related to #1091 ## Testing Performed Tested the feature with a different number of recipients (including and excluding the document owner - self-signer). Tested both the sending and resending functionality. ## Checklist - [x] I have tested these changes locally and they work as expected. - [ ] I have added/updated tests that prove the effectiveness of these changes. - [ ] I have updated the documentation to reflect these changes, if applicable. - [x] I have followed the project's coding style guidelines. - [ ] I have addressed the code review feedback from the previous submission, if applicable. ## UI Screenshots ![CleanShot 2024-04-18 at 12 26 11@2x](https://github.com/documenso/documenso/assets/25515812/ca80f625-befb-4cbc-a541-f2186379d2e8) ![CleanShot 2024-04-18 at 12 27 40@2x](https://github.com/documenso/documenso/assets/25515812/8bcbb6fc-ba98-4fa1-8538-2d062febd27b) ![CleanShot 2024-04-18 at 12 27 53@2x](https://github.com/documenso/documenso/assets/25515812/25d77d98-b5ec-4270-8ffa-43774fe70526) ![CleanShot 2024-04-18 at 12 30 00@2x](https://github.com/documenso/documenso/assets/25515812/a90bb8e3-3ea8-42ff-9971-559b3e81ae6f) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Summary by CodeRabbit - **New Features** - Enhanced the document invitation components to support scenarios where the recipient is also the sender, providing customized email content and subject lines. - Introduced new properties in email templates to improve clarity and relevance based on the user's role in the document signing process. - **Refactor** - Updated components to use a more flexible `headerContent` property for displaying invitation headers, replacing previous individual inviter details. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
2024-04-19 13:45:33 +03:00
selfSigner,
});
const { actionVerb } = RECIPIENT_ROLES_DESCRIPTION[recipient.role];
feat: update emails for self-signer (#1108) ## Description Updated the email content based on whether the document owner is a recipient or not. If the document owner is a recipient (self-signer): * the email subject will be `Please view/sign/approve your document` * the email header will be `Please view/sign/approve your document "<your-doc-title>"` * the email content will be `You have initiated the document "<your-doc-title>" that requires you to view/sign/approve it.` Otherwise: * the email subject will be `Please view/sign/approve this document` * the email header will be `<doc-owner> has invited you to view/sign/approve "<doc-title>"` * the email content will be `<doc-owner> has invited you to view/sign/approve the document "<doc-title>".` ## Related Issue Related to #1091 ## Testing Performed Tested the feature with a different number of recipients (including and excluding the document owner - self-signer). Tested both the sending and resending functionality. ## Checklist - [x] I have tested these changes locally and they work as expected. - [ ] I have added/updated tests that prove the effectiveness of these changes. - [ ] I have updated the documentation to reflect these changes, if applicable. - [x] I have followed the project's coding style guidelines. - [ ] I have addressed the code review feedback from the previous submission, if applicable. ## UI Screenshots ![CleanShot 2024-04-18 at 12 26 11@2x](https://github.com/documenso/documenso/assets/25515812/ca80f625-befb-4cbc-a541-f2186379d2e8) ![CleanShot 2024-04-18 at 12 27 40@2x](https://github.com/documenso/documenso/assets/25515812/8bcbb6fc-ba98-4fa1-8538-2d062febd27b) ![CleanShot 2024-04-18 at 12 27 53@2x](https://github.com/documenso/documenso/assets/25515812/25d77d98-b5ec-4270-8ffa-43774fe70526) ![CleanShot 2024-04-18 at 12 30 00@2x](https://github.com/documenso/documenso/assets/25515812/a90bb8e3-3ea8-42ff-9971-559b3e81ae6f) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Summary by CodeRabbit - **New Features** - Enhanced the document invitation components to support scenarios where the recipient is also the sender, providing customized email content and subject lines. - Introduced new properties in email templates to improve clarity and relevance based on the user's role in the document signing process. - **Refactor** - Updated components to use a more flexible `headerContent` property for displaying invitation headers, replacing previous individual inviter details. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
2024-04-19 13:45:33 +03:00
const emailSubject = selfSigner
? `Please ${actionVerb.toLowerCase()} your document`
: `Please ${actionVerb.toLowerCase()} this document`;
await prisma.$transaction(
async (tx) => {
await mailer.sendMail({
to: {
address: email,
name,
},
from: {
name: FROM_NAME,
address: FROM_ADDRESS,
},
subject: customEmail?.subject
? renderCustomEmailTemplate(customEmail.subject, customEmailTemplate)
feat: update emails for self-signer (#1108) ## Description Updated the email content based on whether the document owner is a recipient or not. If the document owner is a recipient (self-signer): * the email subject will be `Please view/sign/approve your document` * the email header will be `Please view/sign/approve your document "<your-doc-title>"` * the email content will be `You have initiated the document "<your-doc-title>" that requires you to view/sign/approve it.` Otherwise: * the email subject will be `Please view/sign/approve this document` * the email header will be `<doc-owner> has invited you to view/sign/approve "<doc-title>"` * the email content will be `<doc-owner> has invited you to view/sign/approve the document "<doc-title>".` ## Related Issue Related to #1091 ## Testing Performed Tested the feature with a different number of recipients (including and excluding the document owner - self-signer). Tested both the sending and resending functionality. ## Checklist - [x] I have tested these changes locally and they work as expected. - [ ] I have added/updated tests that prove the effectiveness of these changes. - [ ] I have updated the documentation to reflect these changes, if applicable. - [x] I have followed the project's coding style guidelines. - [ ] I have addressed the code review feedback from the previous submission, if applicable. ## UI Screenshots ![CleanShot 2024-04-18 at 12 26 11@2x](https://github.com/documenso/documenso/assets/25515812/ca80f625-befb-4cbc-a541-f2186379d2e8) ![CleanShot 2024-04-18 at 12 27 40@2x](https://github.com/documenso/documenso/assets/25515812/8bcbb6fc-ba98-4fa1-8538-2d062febd27b) ![CleanShot 2024-04-18 at 12 27 53@2x](https://github.com/documenso/documenso/assets/25515812/25d77d98-b5ec-4270-8ffa-43774fe70526) ![CleanShot 2024-04-18 at 12 30 00@2x](https://github.com/documenso/documenso/assets/25515812/a90bb8e3-3ea8-42ff-9971-559b3e81ae6f) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Summary by CodeRabbit - **New Features** - Enhanced the document invitation components to support scenarios where the recipient is also the sender, providing customized email content and subject lines. - Introduced new properties in email templates to improve clarity and relevance based on the user's role in the document signing process. - **Refactor** - Updated components to use a more flexible `headerContent` property for displaying invitation headers, replacing previous individual inviter details. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
2024-04-19 13:45:33 +03:00
: emailSubject,
html: render(template),
text: render(template, { plainText: true }),
});
await tx.recipient.update({
where: {
id: recipient.id,
},
data: {
sendStatus: SendStatus.SENT,
},
});
await tx.documentAuditLog.create({
data: createDocumentAuditLogData({
type: DOCUMENT_AUDIT_LOG_TYPE.EMAIL_SENT,
documentId: document.id,
user,
requestMetadata,
data: {
emailType: recipientEmailType,
recipientEmail: recipient.email,
recipientName: recipient.name,
recipientRole: recipient.role,
recipientId: recipient.id,
isResending: false,
},
}),
});
},
{ timeout: 30_000 },
);
}),
2023-12-02 12:43:43 +11:00
);
2024-04-26 02:17:56 +00:00
const allRecipientsHaveNoActionToTake = document.Recipient.every(
(recipient) => recipient.role === RecipientRole.CC,
);
2024-04-26 02:17:56 +00:00
if (allRecipientsHaveNoActionToTake) {
const updatedDocument = await updateDocument({
documentId,
userId,
teamId,
data: { status: DocumentStatus.COMPLETED },
});
2024-04-26 02:17:56 +00:00
await sealDocument({ documentId: updatedDocument.id, requestMetadata });
// Keep the return type the same for the `sendDocument` method
return await prisma.document.findFirstOrThrow({
where: {
id: documentId,
},
include: {
Recipient: true,
},
});
}
2024-02-15 18:20:10 +11:00
const updatedDocument = await prisma.$transaction(async (tx) => {
if (document.status === DocumentStatus.DRAFT) {
await tx.documentAuditLog.create({
data: createDocumentAuditLogData({
type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_SENT,
documentId: document.id,
requestMetadata,
user,
data: {},
}),
});
}
return await tx.document.update({
where: {
id: documentId,
},
data: {
status: DocumentStatus.PENDING,
2024-02-15 18:20:10 +11:00
},
include: {
Recipient: true,
},
2024-02-15 18:20:10 +11:00
});
});
2024-02-24 11:18:58 +02:00
await triggerWebhook({
event: WebhookTriggerEvents.DOCUMENT_SENT,
data: updatedDocument,
userId,
teamId,
2024-02-24 11:18:58 +02:00
});
return updatedDocument;
};