diff --git a/packages/lib/server-only/document-meta/upsert-document-meta.ts b/packages/lib/server-only/document-meta/upsert-document-meta.ts index d4781f280..7c78b54d1 100644 --- a/packages/lib/server-only/document-meta/upsert-document-meta.ts +++ b/packages/lib/server-only/document-meta/upsert-document-meta.ts @@ -2,12 +2,11 @@ import { DOCUMENT_AUDIT_LOG_TYPE } from '@documenso/lib/types/document-audit-logs'; import type { RequestMetadata } from '@documenso/lib/universal/extract-request-metadata'; -import { - createDocumentAuditLogData, - diffDocumentMetaChanges, -} from '@documenso/lib/utils/document-audit-logs'; +import { diffDocumentMetaChanges } from '@documenso/lib/utils/document-audit-logs'; import { prisma } from '@documenso/prisma'; +import { queueJob } from '../queue/job'; + export type CreateDocumentMetaOptions = { documentId: number; subject?: string; @@ -65,46 +64,45 @@ export const upsertDocumentMeta = async ({ }, }); - return await prisma.$transaction(async (tx) => { - const upsertedDocumentMeta = await tx.documentMeta.upsert({ - where: { + const upsertedDocumentMeta = await prisma.documentMeta.upsert({ + where: { + documentId, + }, + create: { + subject, + message, + password, + dateFormat, + timezone, + documentId, + redirectUrl, + }, + update: { + subject, + message, + password, + dateFormat, + timezone, + redirectUrl, + }, + }); + + const changes = diffDocumentMetaChanges(originalDocumentMeta ?? {}, upsertedDocumentMeta); + + if (changes.length > 0) { + await queueJob({ + job: 'create-document-audit-log', + args: { + type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_META_UPDATED, documentId, - }, - create: { - subject, - message, - password, - dateFormat, - timezone, - documentId, - redirectUrl, - }, - update: { - subject, - message, - password, - dateFormat, - timezone, - redirectUrl, + user, + requestMetadata, + data: { + changes: diffDocumentMetaChanges(originalDocumentMeta ?? {}, upsertedDocumentMeta), + }, }, }); + } - const changes = diffDocumentMetaChanges(originalDocumentMeta ?? {}, upsertedDocumentMeta); - - if (changes.length > 0) { - await tx.documentAuditLog.create({ - data: createDocumentAuditLogData({ - type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_META_UPDATED, - documentId, - user, - requestMetadata, - data: { - changes: diffDocumentMetaChanges(originalDocumentMeta ?? {}, upsertedDocumentMeta), - }, - }), - }); - } - - return upsertedDocumentMeta; - }); + return upsertedDocumentMeta; }; diff --git a/packages/lib/server-only/document/complete-document-with-token.ts b/packages/lib/server-only/document/complete-document-with-token.ts index 5e9f56a9a..d1aa31cd0 100644 --- a/packages/lib/server-only/document/complete-document-with-token.ts +++ b/packages/lib/server-only/document/complete-document-with-token.ts @@ -2,7 +2,6 @@ import { DOCUMENT_AUDIT_LOG_TYPE } from '@documenso/lib/types/document-audit-logs'; import type { RequestMetadata } from '@documenso/lib/universal/extract-request-metadata'; -import { createDocumentAuditLogData } from '@documenso/lib/utils/document-audit-logs'; import { prisma } from '@documenso/prisma'; import { DocumentStatus, SigningStatus } from '@documenso/prisma/client'; import { WebhookTriggerEvents } from '@documenso/prisma/client'; @@ -93,35 +92,34 @@ export const completeDocumentWithToken = async ({ // throw new AppError(AppErrorCode.UNAUTHORIZED, 'Invalid authentication values'); // } - await prisma.$transaction(async (tx) => { - await tx.recipient.update({ - where: { - id: recipient.id, - }, - data: { - signingStatus: SigningStatus.SIGNED, - signedAt: new Date(), - }, - }); + await prisma.recipient.update({ + where: { + id: recipient.id, + }, + data: { + signingStatus: SigningStatus.SIGNED, + signedAt: new Date(), + }, + }); - await tx.documentAuditLog.create({ - data: createDocumentAuditLogData({ - type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_RECIPIENT_COMPLETED, - documentId: document.id, - user: { - name: recipient.name, - email: recipient.email, - }, - requestMetadata, - data: { - recipientEmail: recipient.email, - recipientName: recipient.name, - recipientId: recipient.id, - recipientRole: recipient.role, - // actionAuth: derivedRecipientActionAuth || undefined, - }, - }), - }); + await queueJob({ + job: 'create-document-audit-log', + args: { + type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_RECIPIENT_COMPLETED, + documentId: document.id, + user: { + name: recipient.name, + email: recipient.email, + }, + requestMetadata, + data: { + recipientEmail: recipient.email, + recipientName: recipient.name, + recipientId: recipient.id, + recipientRole: recipient.role, + // actionAuth: derivedRecipientActionAuth || undefined, + }, + }, }); const pendingRecipients = await prisma.recipient.count({ diff --git a/packages/lib/server-only/document/create-document.ts b/packages/lib/server-only/document/create-document.ts index ce1f16670..e27d64b2a 100644 --- a/packages/lib/server-only/document/create-document.ts +++ b/packages/lib/server-only/document/create-document.ts @@ -3,10 +3,10 @@ import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error'; import { DOCUMENT_AUDIT_LOG_TYPE } from '@documenso/lib/types/document-audit-logs'; import type { RequestMetadata } from '@documenso/lib/universal/extract-request-metadata'; -import { createDocumentAuditLogData } from '@documenso/lib/utils/document-audit-logs'; import { prisma } from '@documenso/prisma'; import { WebhookTriggerEvents } from '@documenso/prisma/client'; +import { queueJob } from '../queue/job'; import { triggerWebhook } from '../webhooks/trigger/trigger-webhook'; export type CreateDocumentOptions = { @@ -44,35 +44,34 @@ export const createDocument = async ({ throw new AppError(AppErrorCode.NOT_FOUND, 'Team not found'); } - return await prisma.$transaction(async (tx) => { - const document = await tx.document.create({ - data: { - title, - documentDataId, - userId, - teamId, - }, - }); - - await tx.documentAuditLog.create({ - data: createDocumentAuditLogData({ - type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_CREATED, - documentId: document.id, - user, - requestMetadata, - data: { - title, - }, - }), - }); - - await triggerWebhook({ - event: WebhookTriggerEvents.DOCUMENT_CREATED, - data: document, + const document = await prisma.document.create({ + data: { + title, + documentDataId, userId, teamId, - }); - - return document; + }, }); + + await queueJob({ + job: 'create-document-audit-log', + args: { + type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_CREATED, + documentId: document.id, + user, + requestMetadata, + data: { + title, + }, + }, + }); + + await triggerWebhook({ + event: WebhookTriggerEvents.DOCUMENT_CREATED, + data: document, + userId, + teamId, + }); + + return document; }; diff --git a/packages/lib/server-only/document/delete-document.ts b/packages/lib/server-only/document/delete-document.ts index b0b1ad682..c3a9254cb 100644 --- a/packages/lib/server-only/document/delete-document.ts +++ b/packages/lib/server-only/document/delete-document.ts @@ -12,7 +12,7 @@ import { NEXT_PUBLIC_WEBAPP_URL } from '../../constants/app'; import { FROM_ADDRESS, FROM_NAME } from '../../constants/email'; import { DOCUMENT_AUDIT_LOG_TYPE } from '../../types/document-audit-logs'; import type { RequestMetadata } from '../../universal/extract-request-metadata'; -import { createDocumentAuditLogData } from '../../utils/document-audit-logs'; +import { queueJob } from '../queue/job'; export type DeleteDocumentOptions = { id: number; @@ -61,23 +61,22 @@ export const deleteDocument = async ({ // if the document is a draft, hard-delete if (status === DocumentStatus.DRAFT) { - return await prisma.$transaction(async (tx) => { - // Currently redundant since deleting a document will delete the audit logs. - // However may be useful if we disassociate audit lgos and documents if required. - await tx.documentAuditLog.create({ - data: createDocumentAuditLogData({ - documentId: id, - type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_DELETED, - user, - requestMetadata, - data: { - type: 'HARD', - }, - }), - }); - - return await tx.document.delete({ where: { id, status: DocumentStatus.DRAFT } }); + // Currently redundant since deleting a document will delete the audit logs. + // However may be useful if we disassociate audit lgos and documents if required. + await queueJob({ + job: 'create-document-audit-log', + args: { + documentId: id, + type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_DELETED, + user, + requestMetadata, + data: { + type: 'HARD', + }, + }, }); + + return await prisma.document.delete({ where: { id, status: DocumentStatus.DRAFT } }); } // if the document is pending, send cancellation emails to all recipients @@ -111,26 +110,25 @@ export const deleteDocument = async ({ } // If the document is not a draft, only soft-delete. - return await prisma.$transaction(async (tx) => { - await tx.documentAuditLog.create({ - data: createDocumentAuditLogData({ - documentId: id, - type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_DELETED, - user, - requestMetadata, - data: { - type: 'SOFT', - }, - }), - }); - - return await tx.document.update({ - where: { - id, - }, + await queueJob({ + job: 'create-document-audit-log', + args: { + documentId: id, + type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_DELETED, + user, + requestMetadata, data: { - deletedAt: new Date().toISOString(), + type: 'SOFT', }, - }); + }, + }); + + return await prisma.document.update({ + where: { + id, + }, + data: { + deletedAt: new Date().toISOString(), + }, }); }; diff --git a/packages/lib/server-only/document/resend-document.tsx b/packages/lib/server-only/document/resend-document.tsx index ebf140007..5b8fe8965 100644 --- a/packages/lib/server-only/document/resend-document.tsx +++ b/packages/lib/server-only/document/resend-document.tsx @@ -10,13 +10,13 @@ import { } from '@documenso/lib/constants/recipient-roles'; import { DOCUMENT_AUDIT_LOG_TYPE } from '@documenso/lib/types/document-audit-logs'; import type { RequestMetadata } from '@documenso/lib/universal/extract-request-metadata'; -import { createDocumentAuditLogData } from '@documenso/lib/utils/document-audit-logs'; import { renderCustomEmailTemplate } from '@documenso/lib/utils/render-custom-email-template'; import { prisma } from '@documenso/prisma'; import { DocumentStatus, RecipientRole, SigningStatus } from '@documenso/prisma/client'; import type { Prisma } from '@documenso/prisma/client'; import { NEXT_PUBLIC_WEBAPP_URL } from '../../constants/app'; +import { queueJob } from '../queue/job'; import { getDocumentWhereInput } from './get-document-by-id'; export type ResendDocumentOptions = { @@ -128,8 +128,9 @@ export const resendDocument = async ({ text: render(template, { plainText: true }), }); - await tx.documentAuditLog.create({ - data: createDocumentAuditLogData({ + await queueJob({ + job: 'create-document-audit-log', + args: { type: DOCUMENT_AUDIT_LOG_TYPE.EMAIL_SENT, documentId: document.id, user, @@ -142,9 +143,10 @@ export const resendDocument = async ({ recipientId: recipient.id, isResending: true, }, - }), + }, }); }, + // Hopefully the queue makes this redundant { timeout: 30_000 }, ); }), diff --git a/packages/lib/server-only/document/seal-document.ts b/packages/lib/server-only/document/seal-document.ts index d3c19b412..db4681430 100644 --- a/packages/lib/server-only/document/seal-document.ts +++ b/packages/lib/server-only/document/seal-document.ts @@ -5,13 +5,12 @@ import path from 'node:path'; import { PDFDocument } from 'pdf-lib'; import PostHogServerClient from '@documenso/lib/server-only/feature-flags/get-post-hog-server-client'; -import { DOCUMENT_AUDIT_LOG_TYPE } from '@documenso/lib/types/document-audit-logs'; -import { createDocumentAuditLogData } from '@documenso/lib/utils/document-audit-logs'; import { prisma } from '@documenso/prisma'; import { DocumentStatus, RecipientRole, SigningStatus } from '@documenso/prisma/client'; import { WebhookTriggerEvents } from '@documenso/prisma/client'; import { signPdf } from '@documenso/signing'; +import { DOCUMENT_AUDIT_LOG_TYPE } from '../../types/document-audit-logs'; import type { RequestMetadata } from '../../universal/extract-request-metadata'; import { getFile } from '../../universal/upload/get-file'; import { putFile } from '../../universal/upload/put-file'; @@ -126,27 +125,26 @@ export const sealDocument = async ({ }); } - await prisma.$transaction(async (tx) => { - await tx.documentData.update({ - where: { - id: documentData.id, - }, - data: { - data: newData, - }, - }); + await prisma.documentData.update({ + where: { + id: documentData.id, + }, + data: { + data: newData, + }, + }); - await tx.documentAuditLog.create({ - data: createDocumentAuditLogData({ - type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_COMPLETED, - documentId: document.id, - requestMetadata, - user: null, - data: { - transactionId: nanoid(), - }, - }), - }); + await queueJob({ + job: 'create-document-audit-log', + args: { + type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_COMPLETED, + documentId: document.id, + requestMetadata, + user: null, + data: { + transactionId: nanoid(), + }, + }, }); if (sendEmail && !isResealing) { diff --git a/packages/lib/server-only/document/send-completed-email.ts b/packages/lib/server-only/document/send-completed-email.ts index a397e47e7..1f383d5c7 100644 --- a/packages/lib/server-only/document/send-completed-email.ts +++ b/packages/lib/server-only/document/send-completed-email.ts @@ -9,7 +9,7 @@ import { NEXT_PUBLIC_WEBAPP_URL } from '../../constants/app'; import { DOCUMENT_AUDIT_LOG_TYPE } from '../../types/document-audit-logs'; import type { RequestMetadata } from '../../universal/extract-request-metadata'; import { getFile } from '../../universal/upload/get-file'; -import { createDocumentAuditLogData } from '../../utils/document-audit-logs'; +import { queueJob } from '../queue/job'; export interface SendDocumentOptions { documentId: number; @@ -86,8 +86,9 @@ export const sendCompletedEmail = async ({ documentId, requestMetadata }: SendDo ], }); - await prisma.documentAuditLog.create({ - data: createDocumentAuditLogData({ + await queueJob({ + job: 'create-document-audit-log', + args: { type: DOCUMENT_AUDIT_LOG_TYPE.EMAIL_SENT, documentId: document.id, user: null, @@ -100,7 +101,7 @@ export const sendCompletedEmail = async ({ documentId, requestMetadata }: SendDo recipientRole: 'OWNER', isResending: false, }, - }), + }, }); } @@ -136,8 +137,9 @@ export const sendCompletedEmail = async ({ documentId, requestMetadata }: SendDo ], }); - await prisma.documentAuditLog.create({ - data: createDocumentAuditLogData({ + await queueJob({ + job: 'create-document-audit-log', + args: { type: DOCUMENT_AUDIT_LOG_TYPE.EMAIL_SENT, documentId: document.id, user: null, @@ -150,7 +152,7 @@ export const sendCompletedEmail = async ({ documentId, requestMetadata }: SendDo recipientRole: recipient.role, isResending: false, }, - }), + }, }); }), ); diff --git a/packages/lib/server-only/document/send-document.tsx b/packages/lib/server-only/document/send-document.tsx index 7c928f9a9..1086b66a5 100644 --- a/packages/lib/server-only/document/send-document.tsx +++ b/packages/lib/server-only/document/send-document.tsx @@ -6,7 +6,6 @@ import { DocumentInviteEmailTemplate } from '@documenso/email/templates/document import { FROM_ADDRESS, FROM_NAME } from '@documenso/lib/constants/email'; import { DOCUMENT_AUDIT_LOG_TYPE } from '@documenso/lib/types/document-audit-logs'; import type { RequestMetadata } from '@documenso/lib/universal/extract-request-metadata'; -import { createDocumentAuditLogData } from '@documenso/lib/utils/document-audit-logs'; import { renderCustomEmailTemplate } from '@documenso/lib/utils/render-custom-email-template'; import { prisma } from '@documenso/prisma'; import { DocumentStatus, RecipientRole, SendStatus } from '@documenso/prisma/client'; @@ -17,6 +16,7 @@ import { RECIPIENT_ROLES_DESCRIPTION, RECIPIENT_ROLE_TO_EMAIL_TYPE, } from '../../constants/recipient-roles'; +import { queueJob } from '../queue/job'; import { triggerWebhook } from '../webhooks/trigger/trigger-webhook'; export type SendDocumentOptions = { @@ -113,79 +113,75 @@ export const sendDocument = async ({ const { actionVerb } = RECIPIENT_ROLES_DESCRIPTION[recipient.role]; - await prisma.$transaction( - async (tx) => { - await mailer.sendMail({ - to: { - address: email, - name, - }, - from: { - name: FROM_NAME, - address: FROM_ADDRESS, - }, - subject: customEmail?.subject - ? renderCustomEmailTemplate(customEmail.subject, customEmailTemplate) - : `Please ${actionVerb.toLowerCase()} this document`, - html: render(template), - text: render(template, { plainText: true }), - }); - - await tx.recipient.update({ - where: { - id: recipient.id, - }, - data: { - sendStatus: SendStatus.SENT, - }, - }); - - await tx.documentAuditLog.create({ - data: createDocumentAuditLogData({ - type: DOCUMENT_AUDIT_LOG_TYPE.EMAIL_SENT, - documentId: document.id, - user, - requestMetadata, - data: { - emailType: recipientEmailType, - recipientEmail: recipient.email, - recipientName: recipient.name, - recipientRole: recipient.role, - recipientId: recipient.id, - isResending: false, - }, - }), - }); + // TODO: Move this to a seperate queue of it's own + await mailer.sendMail({ + to: { + address: email, + name, }, - { timeout: 30_000 }, - ); + from: { + name: FROM_NAME, + address: FROM_ADDRESS, + }, + subject: customEmail?.subject + ? renderCustomEmailTemplate(customEmail.subject, customEmailTemplate) + : `Please ${actionVerb.toLowerCase()} this document`, + html: render(template), + text: render(template, { plainText: true }), + }); + + await prisma.recipient.update({ + where: { + id: recipient.id, + }, + data: { + sendStatus: SendStatus.SENT, + }, + }); + + await queueJob({ + job: 'create-document-audit-log', + args: { + type: DOCUMENT_AUDIT_LOG_TYPE.EMAIL_SENT, + documentId: document.id, + user, + requestMetadata, + data: { + emailType: recipientEmailType, + recipientEmail: recipient.email, + recipientName: recipient.name, + recipientRole: recipient.role, + recipientId: recipient.id, + isResending: false, + }, + }, + }); }), ); - const updatedDocument = await prisma.$transaction(async (tx) => { - if (document.status === DocumentStatus.DRAFT) { - await tx.documentAuditLog.create({ - data: createDocumentAuditLogData({ - type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_SENT, - documentId: document.id, - requestMetadata, - user, - data: {}, - }), - }); - } - - return await tx.document.update({ - where: { - id: documentId, - }, - data: { - status: DocumentStatus.PENDING, - }, - include: { - Recipient: true, + if (document.status === DocumentStatus.DRAFT) { + await queueJob({ + job: 'create-document-audit-log', + args: { + type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_SENT, + documentId: document.id, + requestMetadata, + user, + data: {}, }, }); + } + + const updatedDocument = await prisma.document.update({ + where: { + id: documentId, + }, + data: { + status: DocumentStatus.PENDING, + }, + include: { + Recipient: true, + }, }); await triggerWebhook({ diff --git a/packages/lib/server-only/document/super-delete-document.ts b/packages/lib/server-only/document/super-delete-document.ts index 0a5ed69c0..cce9a123c 100644 --- a/packages/lib/server-only/document/super-delete-document.ts +++ b/packages/lib/server-only/document/super-delete-document.ts @@ -12,7 +12,7 @@ import { NEXT_PUBLIC_WEBAPP_URL } from '../../constants/app'; import { FROM_ADDRESS, FROM_NAME } from '../../constants/email'; import { DOCUMENT_AUDIT_LOG_TYPE } from '../../types/document-audit-logs'; import type { RequestMetadata } from '../../universal/extract-request-metadata'; -import { createDocumentAuditLogData } from '../../utils/document-audit-logs'; +import { queueJob } from '../queue/job'; export type SuperDeleteDocumentOptions = { id: number; @@ -66,20 +66,19 @@ export const superDeleteDocument = async ({ id, requestMetadata }: SuperDeleteDo ); } - // always hard delete if deleted from admin - return await prisma.$transaction(async (tx) => { - await tx.documentAuditLog.create({ - data: createDocumentAuditLogData({ - documentId: id, - type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_DELETED, - user, - requestMetadata, - data: { - type: 'HARD', - }, - }), - }); - - return await tx.document.delete({ where: { id } }); + await queueJob({ + job: 'create-document-audit-log', + args: { + documentId: id, + type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_DELETED, + user, + requestMetadata, + data: { + type: 'HARD', + }, + }, }); + + // always hard delete if deleted from admin + return await prisma.document.delete({ where: { id } }); }; diff --git a/packages/lib/server-only/document/update-title.ts b/packages/lib/server-only/document/update-title.ts index 43e7c2d91..565a6a89e 100644 --- a/packages/lib/server-only/document/update-title.ts +++ b/packages/lib/server-only/document/update-title.ts @@ -2,9 +2,10 @@ import { DOCUMENT_AUDIT_LOG_TYPE } from '@documenso/lib/types/document-audit-logs'; import type { RequestMetadata } from '@documenso/lib/universal/extract-request-metadata'; -import { createDocumentAuditLogData } from '@documenso/lib/utils/document-audit-logs'; import { prisma } from '@documenso/prisma'; +import { queueJob } from '../queue/job'; + export type UpdateTitleOptions = { userId: number; teamId?: number; @@ -51,33 +52,32 @@ export const updateTitle = async ({ return document; } - return await prisma.$transaction(async (tx) => { - // Instead of doing everything in a transaction we can use our knowledge - // of the current document title to ensure we aren't performing a conflicting - // update. - const updatedDocument = await tx.document.update({ - where: { - id: documentId, - title: document.title, - }, - data: { - title, - }, - }); - - await tx.documentAuditLog.create({ - data: createDocumentAuditLogData({ - type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_TITLE_UPDATED, - documentId, - user, - requestMetadata, - data: { - from: document.title, - to: updatedDocument.title, - }, - }), - }); - - return updatedDocument; + // Instead of doing everything in a transaction we can use our knowledge + // of the current document title to ensure we aren't performing a conflicting + // update. + const updatedDocument = await prisma.document.update({ + where: { + id: documentId, + title: document.title, + }, + data: { + title, + }, }); + + await queueJob({ + job: 'create-document-audit-log', + args: { + type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_TITLE_UPDATED, + documentId, + user, + requestMetadata, + data: { + from: document.title, + to: updatedDocument.title, + }, + }, + }); + + return updatedDocument; }; diff --git a/packages/lib/server-only/document/viewed-document.ts b/packages/lib/server-only/document/viewed-document.ts index 73ca606cc..c0d92b245 100644 --- a/packages/lib/server-only/document/viewed-document.ts +++ b/packages/lib/server-only/document/viewed-document.ts @@ -1,11 +1,11 @@ import { DOCUMENT_AUDIT_LOG_TYPE } from '@documenso/lib/types/document-audit-logs'; import type { RequestMetadata } from '@documenso/lib/universal/extract-request-metadata'; -import { createDocumentAuditLogData } from '@documenso/lib/utils/document-audit-logs'; import { prisma } from '@documenso/prisma'; import { ReadStatus } from '@documenso/prisma/client'; import { WebhookTriggerEvents } from '@documenso/prisma/client'; import type { TDocumentAccessAuthTypes } from '../../types/document-auth'; +import { queueJob } from '../queue/job'; import { triggerWebhook } from '../webhooks/trigger/trigger-webhook'; import { getDocumentAndRecipientByToken } from './get-document-by-token'; @@ -33,34 +33,33 @@ export const viewedDocument = async ({ const { documentId } = recipient; - await prisma.$transaction(async (tx) => { - await tx.recipient.update({ - where: { - id: recipient.id, - }, - data: { - readStatus: ReadStatus.OPENED, - }, - }); + await prisma.recipient.update({ + where: { + id: recipient.id, + }, + data: { + readStatus: ReadStatus.OPENED, + }, + }); - await tx.documentAuditLog.create({ - data: createDocumentAuditLogData({ - type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_OPENED, - documentId, - user: { - name: recipient.name, - email: recipient.email, - }, - requestMetadata, - data: { - recipientEmail: recipient.email, - recipientId: recipient.id, - recipientName: recipient.name, - recipientRole: recipient.role, - accessAuth: recipientAccessAuth || undefined, - }, - }), - }); + await queueJob({ + job: 'create-document-audit-log', + args: { + type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_OPENED, + documentId, + user: { + name: recipient.name, + email: recipient.email, + }, + requestMetadata, + data: { + recipientEmail: recipient.email, + recipientId: recipient.id, + recipientName: recipient.name, + recipientRole: recipient.role, + accessAuth: recipientAccessAuth || undefined, + }, + }, }); const document = await getDocumentAndRecipientByToken({ token, requireAccessAuth: false }); diff --git a/packages/lib/server-only/field/create-field.ts b/packages/lib/server-only/field/create-field.ts index 7a3aa3959..9bd56b680 100644 --- a/packages/lib/server-only/field/create-field.ts +++ b/packages/lib/server-only/field/create-field.ts @@ -2,7 +2,7 @@ import { prisma } from '@documenso/prisma'; import type { FieldType, Team } from '@documenso/prisma/client'; import type { RequestMetadata } from '../../universal/extract-request-metadata'; -import { createDocumentAuditLogData } from '../../utils/document-audit-logs'; +import { queueJob } from '../queue/job'; export type CreateFieldOptions = { documentId: number; @@ -103,8 +103,9 @@ export const createField = async ({ }, }); - await prisma.documentAuditLog.create({ - data: createDocumentAuditLogData({ + await queueJob({ + job: 'create-document-audit-log', + args: { type: 'FIELD_CREATED', documentId, user: { @@ -119,7 +120,7 @@ export const createField = async ({ fieldType: field.type, }, requestMetadata, - }), + }, }); return field; diff --git a/packages/lib/server-only/field/delete-field.ts b/packages/lib/server-only/field/delete-field.ts index 67145de10..14b8bd84f 100644 --- a/packages/lib/server-only/field/delete-field.ts +++ b/packages/lib/server-only/field/delete-field.ts @@ -2,7 +2,7 @@ import { prisma } from '@documenso/prisma'; import type { Team } from '@documenso/prisma/client'; import type { RequestMetadata } from '../../universal/extract-request-metadata'; -import { createDocumentAuditLogData } from '../../utils/document-audit-logs'; +import { queueJob } from '../queue/job'; export type DeleteFieldOptions = { fieldId: number; @@ -67,8 +67,9 @@ export const deleteField = async ({ }); } - await prisma.documentAuditLog.create({ - data: createDocumentAuditLogData({ + await queueJob({ + job: 'create-document-audit-log', + args: { type: 'FIELD_DELETED', documentId, user: { @@ -83,7 +84,7 @@ export const deleteField = async ({ fieldType: field.type, }, requestMetadata, - }), + }, }); return field; diff --git a/packages/lib/server-only/field/remove-signed-field-with-token.ts b/packages/lib/server-only/field/remove-signed-field-with-token.ts index 6548ae0f1..db191ffb2 100644 --- a/packages/lib/server-only/field/remove-signed-field-with-token.ts +++ b/packages/lib/server-only/field/remove-signed-field-with-token.ts @@ -2,10 +2,11 @@ import { DOCUMENT_AUDIT_LOG_TYPE } from '@documenso/lib/types/document-audit-logs'; import type { RequestMetadata } from '@documenso/lib/universal/extract-request-metadata'; -import { createDocumentAuditLogData } from '@documenso/lib/utils/document-audit-logs'; import { prisma } from '@documenso/prisma'; import { DocumentStatus, SigningStatus } from '@documenso/prisma/client'; +import { queueJob } from '../queue/job'; + export type RemovedSignedFieldWithTokenOptions = { token: string; fieldId: number; @@ -65,21 +66,22 @@ export const removeSignedFieldWithToken = async ({ fieldId: field.id, }, }); + }); - await tx.documentAuditLog.create({ - data: createDocumentAuditLogData({ - type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_FIELD_UNINSERTED, - documentId: document.id, - user: { - name: recipient?.name, - email: recipient?.email, - }, - requestMetadata, - data: { - field: field.type, - fieldId: field.secondaryId, - }, - }), - }); + await queueJob({ + job: 'create-document-audit-log', + args: { + type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_FIELD_UNINSERTED, + documentId: document.id, + user: { + name: recipient?.name, + email: recipient?.email, + }, + requestMetadata, + data: { + field: field.type, + fieldId: field.secondaryId, + }, + }, }); }; diff --git a/packages/lib/server-only/field/set-fields-for-document.ts b/packages/lib/server-only/field/set-fields-for-document.ts index a5b1cfd8b..66d89dff3 100644 --- a/packages/lib/server-only/field/set-fields-for-document.ts +++ b/packages/lib/server-only/field/set-fields-for-document.ts @@ -8,6 +8,8 @@ import { prisma } from '@documenso/prisma'; import type { Field, FieldType } from '@documenso/prisma/client'; import { SendStatus, SigningStatus } from '@documenso/prisma/client'; +import { queueJob } from '../queue/job'; + export interface SetFieldsForDocumentOptions { userId: number; documentId: number; @@ -155,8 +157,9 @@ export const setFieldsForDocument = async ({ // Handle field updated audit log. if (field._persisted && changes.length > 0) { - await tx.documentAuditLog.create({ - data: createDocumentAuditLogData({ + await queueJob({ + job: 'create-document-audit-log', + args: { type: DOCUMENT_AUDIT_LOG_TYPE.FIELD_UPDATED, documentId: documentId, user, @@ -165,14 +168,15 @@ export const setFieldsForDocument = async ({ changes, ...baseAuditLog, }, - }), + }, }); } // Handle field created audit log. if (!field._persisted) { - await tx.documentAuditLog.create({ - data: createDocumentAuditLogData({ + await queueJob({ + job: 'create-document-audit-log', + args: { type: DOCUMENT_AUDIT_LOG_TYPE.FIELD_CREATED, documentId: documentId, user, @@ -180,7 +184,7 @@ export const setFieldsForDocument = async ({ data: { ...baseAuditLog, }, - }), + }, }); } diff --git a/packages/lib/server-only/field/sign-field-with-token.ts b/packages/lib/server-only/field/sign-field-with-token.ts index b8a5ccf8f..9c9904c56 100644 --- a/packages/lib/server-only/field/sign-field-with-token.ts +++ b/packages/lib/server-only/field/sign-field-with-token.ts @@ -12,9 +12,9 @@ import { AppError, AppErrorCode } from '../../errors/app-error'; import { DOCUMENT_AUDIT_LOG_TYPE } from '../../types/document-audit-logs'; import type { TRecipientActionAuth } from '../../types/document-auth'; import type { RequestMetadata } from '../../universal/extract-request-metadata'; -import { createDocumentAuditLogData } from '../../utils/document-audit-logs'; import { extractDocumentAuthMethods } from '../../utils/document-auth'; import { isRecipientAuthorized } from '../document/is-recipient-authorized'; +import { queueJob } from '../queue/job'; export type SignFieldWithTokenOptions = { token: string; @@ -168,8 +168,9 @@ export const signFieldWithToken = async ({ }); } - await tx.documentAuditLog.create({ - data: createDocumentAuditLogData({ + await queueJob({ + job: 'create-document-audit-log', + args: { type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_FIELD_INSERTED, documentId: document.id, user: { @@ -199,7 +200,7 @@ export const signFieldWithToken = async ({ } : undefined, }, - }), + }, }); return updatedField; diff --git a/packages/lib/server-only/field/update-field.ts b/packages/lib/server-only/field/update-field.ts index 84358d245..45b08b413 100644 --- a/packages/lib/server-only/field/update-field.ts +++ b/packages/lib/server-only/field/update-field.ts @@ -3,7 +3,8 @@ import type { FieldType, Team } from '@documenso/prisma/client'; import { DOCUMENT_AUDIT_LOG_TYPE } from '../../types/document-audit-logs'; import type { RequestMetadata } from '../../universal/extract-request-metadata'; -import { createDocumentAuditLogData, diffFieldChanges } from '../../utils/document-audit-logs'; +import { diffFieldChanges } from '../../utils/document-audit-logs'; +import { queueJob } from '../queue/job'; export type UpdateFieldOptions = { fieldId: number; @@ -77,8 +78,9 @@ export const updateField = async ({ }, }); - await tx.documentAuditLog.create({ - data: createDocumentAuditLogData({ + await queueJob({ + job: 'create-document-audit-log', + args: { type: DOCUMENT_AUDIT_LOG_TYPE.FIELD_UPDATED, documentId, user: { @@ -94,7 +96,7 @@ export const updateField = async ({ changes: diffFieldChanges(oldField, updatedField), }, requestMetadata, - }), + }, }); return updatedField; diff --git a/packages/lib/server-only/queue/job.ts b/packages/lib/server-only/queue/job.ts index 5d8704bba..3910c3e9a 100644 --- a/packages/lib/server-only/queue/job.ts +++ b/packages/lib/server-only/queue/job.ts @@ -1,6 +1,10 @@ import type { WorkHandler } from 'pg-boss'; +import { prisma } from '@documenso/prisma'; + import { initQueue } from '.'; +import type { CreateDocumentAuditLogDataOptions } from '../../utils/document-audit-logs'; +import { createDocumentAuditLogData } from '../../utils/document-audit-logs'; import { type SendDocumentOptions as SendCompletedDocumentOptions, sendCompletedEmail, @@ -10,6 +14,7 @@ import { type SendPendingEmailOptions, sendPendingEmail } from '../document/send type JobOptions = { 'send-completed-email': SendCompletedDocumentOptions; 'send-pending-email': SendPendingEmailOptions; + 'create-document-audit-log': CreateDocumentAuditLogDataOptions; }; export const jobHandlers: { @@ -27,6 +32,24 @@ export const jobHandlers: { recipientId, }); }, + + // Audit Logs Queue + 'create-document-audit-log': async ({ + data: { documentId, type, requestMetadata, user, data }, + id, + }) => { + console.log('Running Queue ID', id); + + await prisma.documentAuditLog.create({ + data: createDocumentAuditLogData({ + type, + documentId, + requestMetadata, + user, + data, + }), + }); + }, }; export const queueJob = async ({ diff --git a/packages/lib/server-only/recipient/delete-recipient.ts b/packages/lib/server-only/recipient/delete-recipient.ts index 74fb4a8d2..bd663c394 100644 --- a/packages/lib/server-only/recipient/delete-recipient.ts +++ b/packages/lib/server-only/recipient/delete-recipient.ts @@ -3,7 +3,7 @@ import type { Team } from '@documenso/prisma/client'; import { SendStatus } from '@documenso/prisma/client'; import type { RequestMetadata } from '../../universal/extract-request-metadata'; -import { createDocumentAuditLogData } from '../../utils/document-audit-logs'; +import { queueJob } from '../queue/job'; export type DeleteRecipientOptions = { documentId: number; @@ -73,33 +73,30 @@ export const deleteRecipient = async ({ }); } - const deletedRecipient = await prisma.$transaction(async (tx) => { - const deleted = await tx.recipient.delete({ - where: { - id: recipient.id, + const deletedRecipient = await prisma.recipient.delete({ + where: { + id: recipient.id, + }, + }); + + await queueJob({ + job: 'create-document-audit-log', + args: { + type: 'RECIPIENT_DELETED', + documentId, + user: { + id: team?.id ?? user.id, + email: team?.name ?? user.email, + name: team ? '' : user.name, }, - }); - - await tx.documentAuditLog.create({ - data: createDocumentAuditLogData({ - type: 'RECIPIENT_DELETED', - documentId, - user: { - id: team?.id ?? user.id, - email: team?.name ?? user.email, - name: team ? '' : user.name, - }, - data: { - recipientEmail: recipient.email, - recipientName: recipient.name, - recipientId: recipient.id, - recipientRole: recipient.role, - }, - requestMetadata, - }), - }); - - return deleted; + data: { + recipientEmail: recipient.email, + recipientName: recipient.name, + recipientId: recipient.id, + recipientRole: recipient.role, + }, + requestMetadata, + }, }); return deletedRecipient; diff --git a/packages/lib/server-only/recipient/set-recipients-for-document.ts b/packages/lib/server-only/recipient/set-recipients-for-document.ts index 2dfc599ef..538de8131 100644 --- a/packages/lib/server-only/recipient/set-recipients-for-document.ts +++ b/packages/lib/server-only/recipient/set-recipients-for-document.ts @@ -17,6 +17,7 @@ import { RecipientRole } from '@documenso/prisma/client'; import { SendStatus, SigningStatus } from '@documenso/prisma/client'; import { AppError, AppErrorCode } from '../../errors/app-error'; +import { queueJob } from '../queue/job'; export interface SetRecipientsForDocumentOptions { userId: number; @@ -203,8 +204,9 @@ export const setRecipientsForDocument = async ({ // Handle recipient updated audit log. if (recipient._persisted && changes.length > 0) { - await tx.documentAuditLog.create({ - data: createDocumentAuditLogData({ + await queueJob({ + job: 'create-document-audit-log', + args: { type: DOCUMENT_AUDIT_LOG_TYPE.RECIPIENT_UPDATED, documentId: documentId, user, @@ -213,14 +215,15 @@ export const setRecipientsForDocument = async ({ changes, ...baseAuditLog, }, - }), + }, }); } // Handle recipient created audit log. if (!recipient._persisted) { - await tx.documentAuditLog.create({ - data: createDocumentAuditLogData({ + await queueJob({ + job: 'create-document-audit-log', + args: { type: DOCUMENT_AUDIT_LOG_TYPE.RECIPIENT_CREATED, documentId: documentId, user, @@ -229,7 +232,7 @@ export const setRecipientsForDocument = async ({ ...baseAuditLog, actionAuth: recipient.actionAuth || undefined, }, - }), + }, }); } diff --git a/packages/lib/server-only/recipient/update-recipient.ts b/packages/lib/server-only/recipient/update-recipient.ts index 6d7d6c7e8..6759b7c52 100644 --- a/packages/lib/server-only/recipient/update-recipient.ts +++ b/packages/lib/server-only/recipient/update-recipient.ts @@ -3,7 +3,8 @@ import type { RecipientRole, Team } from '@documenso/prisma/client'; import { DOCUMENT_AUDIT_LOG_TYPE } from '../../types/document-audit-logs'; import type { RequestMetadata } from '../../universal/extract-request-metadata'; -import { createDocumentAuditLogData, diffRecipientChanges } from '../../utils/document-audit-logs'; +import { diffRecipientChanges } from '../../utils/document-audit-logs'; +import { queueJob } from '../queue/job'; export type UpdateRecipientOptions = { documentId: number; @@ -75,44 +76,43 @@ export const updateRecipient = async ({ throw new Error('Recipient not found'); } - const updatedRecipient = await prisma.$transaction(async (tx) => { - const persisted = await prisma.recipient.update({ - where: { - id: recipient.id, - }, - data: { - email: email?.toLowerCase() ?? recipient.email, - name: name ?? recipient.name, - role: role ?? recipient.role, + const updatedRecipient = await prisma.recipient.update({ + where: { + id: recipient.id, + }, + data: { + email: email?.toLowerCase() ?? recipient.email, + name: name ?? recipient.name, + role: role ?? recipient.role, + }, + }); + + const changes = diffRecipientChanges(recipient, updatedRecipient); + + if (changes.length > 0) { + await queueJob({ + job: 'create-document-audit-log', + args: { + type: DOCUMENT_AUDIT_LOG_TYPE.RECIPIENT_UPDATED, + documentId: documentId, + user: { + id: team?.id ?? user.id, + name: team?.name ?? user.name, + email: team ? '' : user.email, + }, + requestMetadata, + data: { + changes, + recipientId, + recipientEmail: updatedRecipient.email, + recipientName: updatedRecipient.name, + recipientRole: updatedRecipient.role, + }, }, }); - const changes = diffRecipientChanges(recipient, persisted); - - if (changes.length > 0) { - await tx.documentAuditLog.create({ - data: createDocumentAuditLogData({ - type: DOCUMENT_AUDIT_LOG_TYPE.RECIPIENT_UPDATED, - documentId: documentId, - user: { - id: team?.id ?? user.id, - name: team?.name ?? user.name, - email: team ? '' : user.email, - }, - requestMetadata, - data: { - changes, - recipientId, - recipientEmail: persisted.email, - recipientName: persisted.name, - recipientRole: persisted.role, - }, - }), - }); - - return persisted; - } - }); + return updatedRecipient; + } return updatedRecipient; }; diff --git a/packages/lib/utils/document-audit-logs.ts b/packages/lib/utils/document-audit-logs.ts index 97ef38c8b..67c288648 100644 --- a/packages/lib/utils/document-audit-logs.ts +++ b/packages/lib/utils/document-audit-logs.ts @@ -25,7 +25,7 @@ import { import { ZRecipientAuthOptionsSchema } from '../types/document-auth'; import type { RequestMetadata } from '../universal/extract-request-metadata'; -type CreateDocumentAuditLogDataOptions = { +export type CreateDocumentAuditLogDataOptions = { documentId: number; type: T; data: Extract['data'];