mirror of
https://github.com/documenso/documenso.git
synced 2025-11-10 04:22:32 +10:00
Adds a new `ownerDocumentCompleted` to the email settings that controls whether a document will be sent to the owner upon completion. This was previously the only email you couldn't disable and didn't account for users integrating with just the API and Webhooks. Also adds a flag to the public `sendDocument` endpoint which will adjust this setting while sendint the document for users who aren't using `emailSettings` on the `createDocument` endpoint.
229 lines
7.2 KiB
TypeScript
229 lines
7.2 KiB
TypeScript
import { createElement } from 'react';
|
|
|
|
import { msg } from '@lingui/macro';
|
|
|
|
import { mailer } from '@documenso/email/mailer';
|
|
import { DocumentCompletedEmailTemplate } from '@documenso/email/templates/document-completed';
|
|
import { prisma } from '@documenso/prisma';
|
|
import { DocumentSource } from '@documenso/prisma/client';
|
|
|
|
import { getI18nInstance } from '../../client-only/providers/i18n.server';
|
|
import { NEXT_PUBLIC_WEBAPP_URL } from '../../constants/app';
|
|
import { DOCUMENT_AUDIT_LOG_TYPE } from '../../types/document-audit-logs';
|
|
import { extractDerivedDocumentEmailSettings } from '../../types/document-email';
|
|
import type { RequestMetadata } from '../../universal/extract-request-metadata';
|
|
import { getFile } from '../../universal/upload/get-file';
|
|
import { createDocumentAuditLogData } from '../../utils/document-audit-logs';
|
|
import { renderCustomEmailTemplate } from '../../utils/render-custom-email-template';
|
|
import { renderEmailWithI18N } from '../../utils/render-email-with-i18n';
|
|
import { teamGlobalSettingsToBranding } from '../../utils/team-global-settings-to-branding';
|
|
import { formatDocumentsPath } from '../../utils/teams';
|
|
|
|
export interface SendDocumentOptions {
|
|
documentId: number;
|
|
requestMetadata?: RequestMetadata;
|
|
}
|
|
|
|
export const sendCompletedEmail = async ({ documentId, requestMetadata }: SendDocumentOptions) => {
|
|
const document = await prisma.document.findUnique({
|
|
where: {
|
|
id: documentId,
|
|
},
|
|
include: {
|
|
documentData: true,
|
|
documentMeta: true,
|
|
Recipient: true,
|
|
User: true,
|
|
team: {
|
|
select: {
|
|
id: true,
|
|
url: true,
|
|
teamGlobalSettings: true,
|
|
},
|
|
},
|
|
},
|
|
});
|
|
|
|
if (!document) {
|
|
throw new Error('Document not found');
|
|
}
|
|
|
|
const isDirectTemplate = document?.source === DocumentSource.TEMPLATE_DIRECT_LINK;
|
|
|
|
if (document.Recipient.length === 0) {
|
|
throw new Error('Document has no recipients');
|
|
}
|
|
|
|
const { User: owner } = document;
|
|
|
|
const completedDocument = await getFile(document.documentData);
|
|
|
|
const assetBaseUrl = NEXT_PUBLIC_WEBAPP_URL() || 'http://localhost:3000';
|
|
|
|
let documentOwnerDownloadLink = `${NEXT_PUBLIC_WEBAPP_URL()}${formatDocumentsPath(
|
|
document.team?.url,
|
|
)}/${document.id}`;
|
|
|
|
if (document.team?.url) {
|
|
documentOwnerDownloadLink = `${NEXT_PUBLIC_WEBAPP_URL()}/t/${document.team.url}/documents/${
|
|
document.id
|
|
}`;
|
|
}
|
|
|
|
const i18n = await getI18nInstance(document.documentMeta?.language);
|
|
|
|
const emailSettings = extractDerivedDocumentEmailSettings(document.documentMeta);
|
|
const isDocumentCompletedEmailEnabled = emailSettings.documentCompleted;
|
|
const isOwnerDocumentCompletedEmailEnabled = emailSettings.ownerDocumentCompleted;
|
|
|
|
// Send email to document owner if:
|
|
// 1. Owner document completed emails are enabled AND
|
|
// 2. Either:
|
|
// - The owner is not a recipient, OR
|
|
// - Recipient emails are disabled
|
|
if (
|
|
isOwnerDocumentCompletedEmailEnabled &&
|
|
(!document.Recipient.find((recipient) => recipient.email === owner.email) ||
|
|
!isDocumentCompletedEmailEnabled)
|
|
) {
|
|
const template = createElement(DocumentCompletedEmailTemplate, {
|
|
documentName: document.title,
|
|
assetBaseUrl,
|
|
downloadLink: documentOwnerDownloadLink,
|
|
});
|
|
|
|
const branding = document.team?.teamGlobalSettings
|
|
? teamGlobalSettingsToBranding(document.team.teamGlobalSettings)
|
|
: undefined;
|
|
|
|
const [html, text] = await Promise.all([
|
|
renderEmailWithI18N(template, { lang: document.documentMeta?.language, branding }),
|
|
renderEmailWithI18N(template, {
|
|
lang: document.documentMeta?.language,
|
|
branding,
|
|
plainText: true,
|
|
}),
|
|
]);
|
|
|
|
await mailer.sendMail({
|
|
to: [
|
|
{
|
|
name: owner.name || '',
|
|
address: owner.email,
|
|
},
|
|
],
|
|
from: {
|
|
name: process.env.NEXT_PRIVATE_SMTP_FROM_NAME || 'Documenso',
|
|
address: process.env.NEXT_PRIVATE_SMTP_FROM_ADDRESS || 'noreply@documenso.com',
|
|
},
|
|
subject: i18n._(msg`Signing Complete!`),
|
|
html,
|
|
text,
|
|
attachments: [
|
|
{
|
|
filename: document.title.endsWith('.pdf') ? document.title : document.title + '.pdf',
|
|
content: Buffer.from(completedDocument),
|
|
},
|
|
],
|
|
});
|
|
|
|
await prisma.documentAuditLog.create({
|
|
data: createDocumentAuditLogData({
|
|
type: DOCUMENT_AUDIT_LOG_TYPE.EMAIL_SENT,
|
|
documentId: document.id,
|
|
user: null,
|
|
requestMetadata,
|
|
data: {
|
|
emailType: 'DOCUMENT_COMPLETED',
|
|
recipientEmail: owner.email,
|
|
recipientName: owner.name ?? '',
|
|
recipientId: owner.id,
|
|
recipientRole: 'OWNER',
|
|
isResending: false,
|
|
},
|
|
}),
|
|
});
|
|
}
|
|
|
|
if (!isDocumentCompletedEmailEnabled) {
|
|
return;
|
|
}
|
|
|
|
await Promise.all(
|
|
document.Recipient.map(async (recipient) => {
|
|
const customEmailTemplate = {
|
|
'signer.name': recipient.name,
|
|
'signer.email': recipient.email,
|
|
'document.name': document.title,
|
|
};
|
|
|
|
const downloadLink = `${NEXT_PUBLIC_WEBAPP_URL()}/sign/${recipient.token}/complete`;
|
|
|
|
const template = createElement(DocumentCompletedEmailTemplate, {
|
|
documentName: document.title,
|
|
assetBaseUrl,
|
|
downloadLink: recipient.email === owner.email ? documentOwnerDownloadLink : downloadLink,
|
|
customBody:
|
|
isDirectTemplate && document.documentMeta?.message
|
|
? renderCustomEmailTemplate(document.documentMeta.message, customEmailTemplate)
|
|
: undefined,
|
|
});
|
|
|
|
const branding = document.team?.teamGlobalSettings
|
|
? teamGlobalSettingsToBranding(document.team.teamGlobalSettings)
|
|
: undefined;
|
|
|
|
const [html, text] = await Promise.all([
|
|
renderEmailWithI18N(template, { lang: document.documentMeta?.language, branding }),
|
|
renderEmailWithI18N(template, {
|
|
lang: document.documentMeta?.language,
|
|
branding,
|
|
plainText: true,
|
|
}),
|
|
]);
|
|
|
|
await mailer.sendMail({
|
|
to: [
|
|
{
|
|
name: recipient.name,
|
|
address: recipient.email,
|
|
},
|
|
],
|
|
from: {
|
|
name: process.env.NEXT_PRIVATE_SMTP_FROM_NAME || 'Documenso',
|
|
address: process.env.NEXT_PRIVATE_SMTP_FROM_ADDRESS || 'noreply@documenso.com',
|
|
},
|
|
subject:
|
|
isDirectTemplate && document.documentMeta?.subject
|
|
? renderCustomEmailTemplate(document.documentMeta.subject, customEmailTemplate)
|
|
: i18n._(msg`Signing Complete!`),
|
|
html,
|
|
text,
|
|
attachments: [
|
|
{
|
|
filename: document.title.endsWith('.pdf') ? document.title : document.title + '.pdf',
|
|
content: Buffer.from(completedDocument),
|
|
},
|
|
],
|
|
});
|
|
|
|
await prisma.documentAuditLog.create({
|
|
data: createDocumentAuditLogData({
|
|
type: DOCUMENT_AUDIT_LOG_TYPE.EMAIL_SENT,
|
|
documentId: document.id,
|
|
user: null,
|
|
requestMetadata,
|
|
data: {
|
|
emailType: 'DOCUMENT_COMPLETED',
|
|
recipientEmail: recipient.email,
|
|
recipientName: recipient.name,
|
|
recipientId: recipient.id,
|
|
recipientRole: recipient.role,
|
|
isResending: false,
|
|
},
|
|
}),
|
|
});
|
|
}),
|
|
);
|
|
};
|