feat: migrate nextjs to rr7

This commit is contained in:
David Nguyen
2025-01-02 15:33:37 +11:00
parent 9183f668d3
commit 383b5f78f0
898 changed files with 31175 additions and 24615 deletions

View File

@ -0,0 +1,12 @@
import { sendResetPassword } from '../../../server-only/auth/send-reset-password';
import type { TSendPasswordResetSuccessEmailJobDefinition } from './send-password-reset-success-email';
export const run = async ({
payload,
}: {
payload: TSendPasswordResetSuccessEmailJobDefinition;
}) => {
await sendResetPassword({
userId: payload.userId,
});
};

View File

@ -0,0 +1,31 @@
import { z } from 'zod';
import type { JobDefinition } from '../../client/_internal/job';
const SEND_PASSWORD_RESET_SUCCESS_EMAIL_JOB_DEFINITION_ID = 'send.password.reset.success.email';
const SEND_PASSWORD_RESET_SUCCESS_EMAIL_JOB_DEFINITION_SCHEMA = z.object({
userId: z.number(),
});
export type TSendPasswordResetSuccessEmailJobDefinition = z.infer<
typeof SEND_PASSWORD_RESET_SUCCESS_EMAIL_JOB_DEFINITION_SCHEMA
>;
export const SEND_PASSWORD_RESET_SUCCESS_EMAIL_JOB_DEFINITION = {
id: SEND_PASSWORD_RESET_SUCCESS_EMAIL_JOB_DEFINITION_ID,
name: 'Send Password Reset Email',
version: '1.0.0',
trigger: {
name: SEND_PASSWORD_RESET_SUCCESS_EMAIL_JOB_DEFINITION_ID,
schema: SEND_PASSWORD_RESET_SUCCESS_EMAIL_JOB_DEFINITION_SCHEMA,
},
handler: async ({ payload }) => {
const handler = await import('./send-password-reset-success-email.handler');
await handler.run({ payload });
},
} as const satisfies JobDefinition<
typeof SEND_PASSWORD_RESET_SUCCESS_EMAIL_JOB_DEFINITION_ID,
TSendPasswordResetSuccessEmailJobDefinition
>;

View File

@ -0,0 +1,117 @@
import { createElement } from 'react';
import { msg } from '@lingui/core/macro';
import { mailer } from '@documenso/email/mailer';
import { DocumentRecipientSignedEmailTemplate } from '@documenso/email/templates/document-recipient-signed';
import { prisma } from '@documenso/prisma';
import { getI18nInstance } from '../../../client-only/providers/i18n-server';
import { NEXT_PUBLIC_WEBAPP_URL } from '../../../constants/app';
import { FROM_ADDRESS, FROM_NAME } from '../../../constants/email';
import { extractDerivedDocumentEmailSettings } from '../../../types/document-email';
import { renderEmailWithI18N } from '../../../utils/render-email-with-i18n';
import { teamGlobalSettingsToBranding } from '../../../utils/team-global-settings-to-branding';
import type { JobRunIO } from '../../client/_internal/job';
import type { TSendRecipientSignedEmailJobDefinition } from './send-recipient-signed-email';
export const run = async ({
payload,
io,
}: {
payload: TSendRecipientSignedEmailJobDefinition;
io: JobRunIO;
}) => {
const { documentId, recipientId } = payload;
const document = await prisma.document.findFirst({
where: {
id: documentId,
recipients: {
some: {
id: recipientId,
},
},
},
include: {
recipients: {
where: {
id: recipientId,
},
},
user: true,
documentMeta: true,
team: {
include: {
teamGlobalSettings: true,
},
},
},
});
if (!document) {
throw new Error('Document not found');
}
if (document.recipients.length === 0) {
throw new Error('Document has no recipients');
}
const isRecipientSignedEmailEnabled = extractDerivedDocumentEmailSettings(
document.documentMeta,
).recipientSigned;
if (!isRecipientSignedEmailEnabled) {
return;
}
const [recipient] = document.recipients;
const { email: recipientEmail, name: recipientName } = recipient;
const { user: owner } = document;
const recipientReference = recipientName || recipientEmail;
// Don't send notification if the owner is the one who signed
if (owner.email === recipientEmail) {
return;
}
const assetBaseUrl = NEXT_PUBLIC_WEBAPP_URL() || 'http://localhost:3000';
const i18n = await getI18nInstance(document.documentMeta?.language);
const template = createElement(DocumentRecipientSignedEmailTemplate, {
documentName: document.title,
recipientName,
recipientEmail,
assetBaseUrl,
});
await io.runTask('send-recipient-signed-email', async () => {
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: FROM_NAME,
address: FROM_ADDRESS,
},
subject: i18n._(msg`${recipientReference} has signed "${document.title}"`),
html,
text,
});
});
};

View File

@ -1,18 +1,5 @@
import { createElement } from 'react';
import { msg } from '@lingui/macro';
import { z } from 'zod';
import { mailer } from '@documenso/email/mailer';
import { DocumentRecipientSignedEmailTemplate } from '@documenso/email/templates/document-recipient-signed';
import { prisma } from '@documenso/prisma';
import { getI18nInstance } from '../../../client-only/providers/i18n.server';
import { NEXT_PUBLIC_WEBAPP_URL } from '../../../constants/app';
import { FROM_ADDRESS, FROM_NAME } from '../../../constants/email';
import { extractDerivedDocumentEmailSettings } from '../../../types/document-email';
import { renderEmailWithI18N } from '../../../utils/render-email-with-i18n';
import { teamGlobalSettingsToBranding } from '../../../utils/team-global-settings-to-branding';
import { type JobDefinition } from '../../client/_internal/job';
const SEND_RECIPIENT_SIGNED_EMAIL_JOB_DEFINITION_ID = 'send.recipient.signed.email';
@ -22,6 +9,10 @@ const SEND_RECIPIENT_SIGNED_EMAIL_JOB_DEFINITION_SCHEMA = z.object({
recipientId: z.number(),
});
export type TSendRecipientSignedEmailJobDefinition = z.infer<
typeof SEND_RECIPIENT_SIGNED_EMAIL_JOB_DEFINITION_SCHEMA
>;
export const SEND_RECIPIENT_SIGNED_EMAIL_JOB_DEFINITION = {
id: SEND_RECIPIENT_SIGNED_EMAIL_JOB_DEFINITION_ID,
name: 'Send Recipient Signed Email',
@ -31,98 +22,9 @@ export const SEND_RECIPIENT_SIGNED_EMAIL_JOB_DEFINITION = {
schema: SEND_RECIPIENT_SIGNED_EMAIL_JOB_DEFINITION_SCHEMA,
},
handler: async ({ payload, io }) => {
const { documentId, recipientId } = payload;
const handler = await import('./send-recipient-signed-email.handler');
const document = await prisma.document.findFirst({
where: {
id: documentId,
recipients: {
some: {
id: recipientId,
},
},
},
include: {
recipients: {
where: {
id: recipientId,
},
},
user: true,
documentMeta: true,
team: {
include: {
teamGlobalSettings: true,
},
},
},
});
if (!document) {
throw new Error('Document not found');
}
if (document.recipients.length === 0) {
throw new Error('Document has no recipients');
}
const isRecipientSignedEmailEnabled = extractDerivedDocumentEmailSettings(
document.documentMeta,
).recipientSigned;
if (!isRecipientSignedEmailEnabled) {
return;
}
const [recipient] = document.recipients;
const { email: recipientEmail, name: recipientName } = recipient;
const { user: owner } = document;
const recipientReference = recipientName || recipientEmail;
// Don't send notification if the owner is the one who signed
if (owner.email === recipientEmail) {
return;
}
const assetBaseUrl = NEXT_PUBLIC_WEBAPP_URL() || 'http://localhost:3000';
const i18n = await getI18nInstance(document.documentMeta?.language);
const template = createElement(DocumentRecipientSignedEmailTemplate, {
documentName: document.title,
recipientName,
recipientEmail,
assetBaseUrl,
});
await io.runTask('send-recipient-signed-email', async () => {
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: FROM_NAME,
address: FROM_ADDRESS,
},
subject: i18n._(msg`${recipientReference} has signed "${document.title}"`),
html,
text,
});
});
await handler.run({ payload, io });
},
} as const satisfies JobDefinition<
typeof SEND_RECIPIENT_SIGNED_EMAIL_JOB_DEFINITION_ID,

View File

@ -1,14 +1,14 @@
import { createElement } from 'react';
import { msg } from '@lingui/macro';
import { msg } from '@lingui/core/macro';
import { SendStatus, SigningStatus } from '@prisma/client';
import { mailer } from '@documenso/email/mailer';
import DocumentRejectedEmail from '@documenso/email/templates/document-rejected';
import DocumentRejectionConfirmedEmail from '@documenso/email/templates/document-rejection-confirmed';
import { prisma } from '@documenso/prisma';
import { SendStatus, SigningStatus } from '@documenso/prisma/client';
import { getI18nInstance } from '../../../client-only/providers/i18n.server';
import { getI18nInstance } from '../../../client-only/providers/i18n-server';
import { NEXT_PUBLIC_WEBAPP_URL } from '../../../constants/app';
import { FROM_ADDRESS, FROM_NAME } from '../../../constants/email';
import { extractDerivedDocumentEmailSettings } from '../../../types/document-email';

View File

@ -1,18 +1,13 @@
import { createElement } from 'react';
import { msg } from '@lingui/macro';
import { msg } from '@lingui/core/macro';
import { DocumentSource, DocumentStatus, RecipientRole, SendStatus } from '@prisma/client';
import { mailer } from '@documenso/email/mailer';
import DocumentInviteEmailTemplate from '@documenso/email/templates/document-invite';
import { prisma } from '@documenso/prisma';
import {
DocumentSource,
DocumentStatus,
RecipientRole,
SendStatus,
} from '@documenso/prisma/client';
import { getI18nInstance } from '../../../client-only/providers/i18n.server';
import { getI18nInstance } from '../../../client-only/providers/i18n-server';
import { NEXT_PUBLIC_WEBAPP_URL } from '../../../constants/app';
import { FROM_ADDRESS, FROM_NAME } from '../../../constants/email';
import {
@ -119,9 +114,11 @@ export const run = async ({
emailMessage = customEmail?.message ?? '';
if (!emailMessage) {
const inviterName = user.name || '';
emailMessage = i18n._(
team.teamGlobalSettings?.includeSenderDetails
? msg`${user.name} on behalf of "${team.name}" has invited you to ${recipientActionVerb} the document "${document.title}".`
? msg`${inviterName} on behalf of "${team.name}" has invited you to ${recipientActionVerb} the document "${document.title}".`
: msg`${team.name} has invited you to ${recipientActionVerb} the document "${document.title}".`,
);
}

View File

@ -1,7 +1,6 @@
import { DocumentVisibility } from '@prisma/client';
import { z } from 'zod';
import { DocumentVisibility } from '@documenso/prisma/client';
import type { JobDefinition } from '../../client/_internal/job';
const SEND_TEAM_DELETED_EMAIL_JOB_DEFINITION_ID = 'send.team-deleted.email';

View File

@ -1,14 +1,14 @@
import { createElement } from 'react';
import { msg } from '@lingui/macro';
import { msg } from '@lingui/core/macro';
import { TeamMemberRole } from '@prisma/client';
import { mailer } from '@documenso/email/mailer';
import TeamJoinEmailTemplate from '@documenso/email/templates/team-join';
import { prisma } from '@documenso/prisma';
import { TeamMemberRole } from '@documenso/prisma/client';
import { getI18nInstance } from '../../../client-only/providers/i18n.server';
import { WEBAPP_BASE_URL } from '../../../constants/app';
import { getI18nInstance } from '../../../client-only/providers/i18n-server';
import { NEXT_PUBLIC_WEBAPP_URL } from '../../../constants/app';
import { FROM_ADDRESS, FROM_NAME } from '../../../constants/email';
import { renderEmailWithI18N } from '../../../utils/render-email-with-i18n';
import { teamGlobalSettingsToBranding } from '../../../utils/team-global-settings-to-branding';
@ -60,8 +60,8 @@ export const run = async ({
`send-team-member-joined-email--${invitedMember.id}_${member.id}`,
async () => {
const emailContent = createElement(TeamJoinEmailTemplate, {
assetBaseUrl: WEBAPP_BASE_URL,
baseUrl: WEBAPP_BASE_URL,
assetBaseUrl: NEXT_PUBLIC_WEBAPP_URL(),
baseUrl: NEXT_PUBLIC_WEBAPP_URL(),
memberName: invitedMember.user.name || '',
memberEmail: invitedMember.user.email,
teamName: team.name,

View File

@ -1,14 +1,14 @@
import { createElement } from 'react';
import { msg } from '@lingui/macro';
import { msg } from '@lingui/core/macro';
import { TeamMemberRole } from '@prisma/client';
import { mailer } from '@documenso/email/mailer';
import TeamJoinEmailTemplate from '@documenso/email/templates/team-join';
import { prisma } from '@documenso/prisma';
import { TeamMemberRole } from '@documenso/prisma/client';
import { getI18nInstance } from '../../../client-only/providers/i18n.server';
import { WEBAPP_BASE_URL } from '../../../constants/app';
import { getI18nInstance } from '../../../client-only/providers/i18n-server';
import { NEXT_PUBLIC_WEBAPP_URL } from '../../../constants/app';
import { FROM_ADDRESS, FROM_NAME } from '../../../constants/email';
import { renderEmailWithI18N } from '../../../utils/render-email-with-i18n';
import { teamGlobalSettingsToBranding } from '../../../utils/team-global-settings-to-branding';
@ -50,8 +50,8 @@ export const run = async ({
for (const member of team.members) {
await io.runTask(`send-team-member-left-email--${oldMember.id}_${member.id}`, async () => {
const emailContent = createElement(TeamJoinEmailTemplate, {
assetBaseUrl: WEBAPP_BASE_URL,
baseUrl: WEBAPP_BASE_URL,
assetBaseUrl: NEXT_PUBLIC_WEBAPP_URL(),
baseUrl: NEXT_PUBLIC_WEBAPP_URL(),
memberName: oldMember.name || '',
memberEmail: oldMember.email,
teamName: team.name,

View File

@ -1,14 +1,9 @@
import { DocumentStatus, RecipientRole, SigningStatus, WebhookTriggerEvents } from '@prisma/client';
import { nanoid } from 'nanoid';
import path from 'node:path';
import { PDFDocument } from 'pdf-lib';
import { prisma } from '@documenso/prisma';
import {
DocumentStatus,
RecipientRole,
SigningStatus,
WebhookTriggerEvents,
} from '@documenso/prisma/client';
import { signPdf } from '@documenso/signing';
import { sendCompletedEmail } from '../../../server-only/document/send-completed-email';
@ -24,8 +19,8 @@ import {
ZWebhookDocumentSchema,
mapDocumentToWebhookDocumentPayload,
} from '../../../types/webhook-payload';
import { getFile } from '../../../universal/upload/get-file';
import { putPdfFile } from '../../../universal/upload/put-file';
import { getFileServerSide } from '../../../universal/upload/get-file.server';
import { putPdfFileServerSide } from '../../../universal/upload/put-file.server';
import { fieldsContainUnsignedRequiredField } from '../../../utils/advanced-fields-helpers';
import { createDocumentAuditLogData } from '../../../utils/document-audit-logs';
import type { JobRunIO } from '../../client/_internal/job';
@ -119,7 +114,7 @@ export const run = async ({
documentData.data = documentData.initialData;
}
const pdfData = await getFile(documentData);
const pdfData = await getFileServerSide(documentData);
const certificateData =
(document.team?.teamGlobalSettings?.includeSigningCertificate ?? true)
@ -165,7 +160,7 @@ export const run = async ({
const { name } = path.parse(document.title);
const documentData = await putPdfFile({
const documentData = await putPdfFileServerSide({
name: `${name}_signed.pdf`,
type: 'application/pdf',
arrayBuffer: async () => Promise.resolve(pdfBuffer),