feat: move email sending into background jobs for retry support

Each direct mailer.sendMail() call is replaced by a dedicated background
job so that email delivery failures can be retried independently.

New jobs: send-pending-email, send-completed-email, send-forgot-password-email,
send-document-super-delete-email, send-recipient-removed-email,
send-document-deleted-emails, send-2fa-token-email, send-resend-document-email,
send-direct-template-created-email.

Existing handlers (send-document-cancelled-emails, send-organisation-member-*,
send-recipient-signed-email) have io.runTask wrappers removed since they
interfere with the job scheduler. Job triggers are dispatched after
transactions commit to avoid race conditions with uncommitted data.
This commit is contained in:
Lucas Smith
2026-02-20 23:25:37 +11:00
parent 653ab3678a
commit 844af17ec2
28 changed files with 1080 additions and 441 deletions
@@ -3,7 +3,7 @@ import crypto from 'crypto';
import { prisma } from '@documenso/prisma';
import { ONE_DAY } from '../../constants/time';
import { sendForgotPassword } from '../auth/send-forgot-password';
import { jobs } from '../../jobs/client';
export const forgotPassword = async ({ email }: { email: string }) => {
const user = await prisma.user.findFirst({
@@ -46,7 +46,10 @@ export const forgotPassword = async ({ email }: { email: string }) => {
},
});
await sendForgotPassword({
userId: user.id,
}).catch((err) => console.error(err));
await jobs.triggerJob({
name: 'send.forgot.password.email',
payload: {
userId: user.id,
},
});
};