Files
documenso/packages/lib/server-only/admin/admin-super-delete-document.ts
Lucas Smith 844af17ec2 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.
2026-02-20 23:25:37 +11:00

83 lines
2.2 KiB
TypeScript

import { prisma } from '@documenso/prisma';
import { AppError, AppErrorCode } from '../../errors/app-error';
import { jobs } from '../../jobs/client';
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 { createDocumentAuditLogData } from '../../utils/document-audit-logs';
export type AdminSuperDeleteDocumentOptions = {
envelopeId: string;
reason: string;
requestMetadata?: RequestMetadata;
};
export const adminSuperDeleteDocument = async ({
envelopeId,
reason,
requestMetadata,
}: AdminSuperDeleteDocumentOptions) => {
const envelope = await prisma.envelope.findUnique({
where: {
id: envelopeId,
},
include: {
documentMeta: true,
user: {
select: {
id: true,
email: true,
name: true,
},
},
},
});
if (!envelope) {
throw new AppError(AppErrorCode.NOT_FOUND, {
message: 'Document not found',
});
}
const { user } = envelope;
const isDocumentDeletedEmailEnabled = extractDerivedDocumentEmailSettings(
envelope.documentMeta,
).documentDeleted;
// Always hard delete if deleted from admin.
const deletedEnvelope = await prisma.$transaction(async (tx) => {
await tx.documentAuditLog.create({
data: createDocumentAuditLogData({
envelopeId,
type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_DELETED,
user,
requestMetadata,
data: {
type: 'HARD',
},
}),
});
return await tx.envelope.delete({ where: { id: envelopeId } });
});
// Notify the document owner after the hard delete transaction commits.
// We only send the owner notification; recipient cancellation emails are
// omitted because the recipients are hard-deleted with the envelope.
if (isDocumentDeletedEmailEnabled) {
await jobs.triggerJob({
name: 'send.document.super.delete.email',
payload: {
userId: user.id,
documentTitle: envelope.title,
reason,
teamId: envelope.teamId,
},
});
}
return deletedEnvelope;
};