Merge branch 'main' into test/sign-redirect-url

This commit is contained in:
Ephraim Atta-Duncan
2024-03-21 16:36:48 +00:00
71 changed files with 4930 additions and 927 deletions

View File

@ -24,6 +24,13 @@ export const sendCompletedEmail = async ({ documentId, requestMetadata }: SendDo
include: {
documentData: true,
Recipient: true,
User: true,
team: {
select: {
id: true,
url: true,
},
},
},
});
@ -35,61 +42,116 @@ export const sendCompletedEmail = async ({ documentId, requestMetadata }: SendDo
throw new Error('Document has no recipients');
}
const buffer = await getFile(document.documentData);
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()}/documents/${document.id}`;
if (document.team?.url) {
documentOwnerDownloadLink = `${NEXT_PUBLIC_WEBAPP_URL()}/t/${document.team.url}/documents/${
document.id
}`;
}
// If the document owner is not a recipient then send the email to them separately
if (!document.Recipient.find((recipient) => recipient.email === owner.email)) {
const template = createElement(DocumentCompletedEmailTemplate, {
documentName: document.title,
assetBaseUrl,
downloadLink: documentOwnerDownloadLink,
});
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: 'Signing Complete!',
html: render(template),
text: render(template, { plainText: true }),
attachments: [
{
filename: document.title,
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,
},
}),
});
}
await Promise.all(
document.Recipient.map(async (recipient) => {
const { email, name, token } = recipient;
const assetBaseUrl = NEXT_PUBLIC_WEBAPP_URL() || 'http://localhost:3000';
const downloadLink = `${NEXT_PUBLIC_WEBAPP_URL()}/sign/${recipient.token}/complete`;
const template = createElement(DocumentCompletedEmailTemplate, {
documentName: document.title,
assetBaseUrl,
downloadLink: `${NEXT_PUBLIC_WEBAPP_URL()}/sign/${token}/complete`,
downloadLink: recipient.email === owner.email ? documentOwnerDownloadLink : downloadLink,
});
await prisma.$transaction(
async (tx) => {
await mailer.sendMail({
to: {
address: email,
name,
},
from: {
name: process.env.NEXT_PRIVATE_SMTP_FROM_NAME || 'Documenso',
address: process.env.NEXT_PRIVATE_SMTP_FROM_ADDRESS || 'noreply@documenso.com',
},
subject: 'Signing Complete!',
html: render(template),
text: render(template, { plainText: true }),
attachments: [
{
filename: document.title,
content: Buffer.from(buffer),
},
],
});
await tx.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,
},
}),
});
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',
},
{ timeout: 30_000 },
);
subject: 'Signing Complete!',
html: render(template),
text: render(template, { plainText: true }),
attachments: [
{
filename: document.title,
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,
},
}),
});
}),
);
};

View File

@ -37,6 +37,12 @@ export const findTemplates = async ({
where: whereFilter,
include: {
templateDocumentData: true,
team: {
select: {
id: true,
url: true,
},
},
Field: true,
Recipient: true,
},

View File

@ -0,0 +1,35 @@
import { DateTime } from 'luxon';
import { prisma } from '@documenso/prisma';
export type GetCompletedDocumentsMonthlyResult = Array<{
month: string;
count: number;
cume_count: number;
}>;
type GetCompletedDocumentsMonthlyQueryResult = Array<{
month: Date;
count: bigint;
cume_count: bigint;
}>;
export const getCompletedDocumentsMonthly = async () => {
const result = await prisma.$queryRaw<GetCompletedDocumentsMonthlyQueryResult>`
SELECT
DATE_TRUNC('month', "updatedAt") AS "month",
COUNT("id") as "count",
SUM(COUNT("id")) OVER (ORDER BY DATE_TRUNC('month', "updatedAt")) as "cume_count"
FROM "Document"
WHERE "status" = 'COMPLETED'
GROUP BY "month"
ORDER BY "month" DESC
LIMIT 12
`;
return result.map((row) => ({
month: DateTime.fromJSDate(row.month).toFormat('yyyy-MM'),
count: Number(row.count),
cume_count: Number(row.cume_count),
}));
};