feat: add queue for creating audit logs

This commit is contained in:
Ephraim Atta-Duncan
2024-04-07 19:36:38 +00:00
parent 2819251ec4
commit 574098f103
22 changed files with 441 additions and 418 deletions

View File

@ -2,12 +2,11 @@
import { DOCUMENT_AUDIT_LOG_TYPE } from '@documenso/lib/types/document-audit-logs'; import { DOCUMENT_AUDIT_LOG_TYPE } from '@documenso/lib/types/document-audit-logs';
import type { RequestMetadata } from '@documenso/lib/universal/extract-request-metadata'; import type { RequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
import { import { diffDocumentMetaChanges } from '@documenso/lib/utils/document-audit-logs';
createDocumentAuditLogData,
diffDocumentMetaChanges,
} from '@documenso/lib/utils/document-audit-logs';
import { prisma } from '@documenso/prisma'; import { prisma } from '@documenso/prisma';
import { queueJob } from '../queue/job';
export type CreateDocumentMetaOptions = { export type CreateDocumentMetaOptions = {
documentId: number; documentId: number;
subject?: string; subject?: string;
@ -65,8 +64,7 @@ export const upsertDocumentMeta = async ({
}, },
}); });
return await prisma.$transaction(async (tx) => { const upsertedDocumentMeta = await prisma.documentMeta.upsert({
const upsertedDocumentMeta = await tx.documentMeta.upsert({
where: { where: {
documentId, documentId,
}, },
@ -92,8 +90,9 @@ export const upsertDocumentMeta = async ({
const changes = diffDocumentMetaChanges(originalDocumentMeta ?? {}, upsertedDocumentMeta); const changes = diffDocumentMetaChanges(originalDocumentMeta ?? {}, upsertedDocumentMeta);
if (changes.length > 0) { if (changes.length > 0) {
await tx.documentAuditLog.create({ await queueJob({
data: createDocumentAuditLogData({ job: 'create-document-audit-log',
args: {
type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_META_UPDATED, type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_META_UPDATED,
documentId, documentId,
user, user,
@ -101,10 +100,9 @@ export const upsertDocumentMeta = async ({
data: { data: {
changes: diffDocumentMetaChanges(originalDocumentMeta ?? {}, upsertedDocumentMeta), changes: diffDocumentMetaChanges(originalDocumentMeta ?? {}, upsertedDocumentMeta),
}, },
}), },
}); });
} }
return upsertedDocumentMeta; return upsertedDocumentMeta;
});
}; };

View File

@ -2,7 +2,6 @@
import { DOCUMENT_AUDIT_LOG_TYPE } from '@documenso/lib/types/document-audit-logs'; import { DOCUMENT_AUDIT_LOG_TYPE } from '@documenso/lib/types/document-audit-logs';
import type { RequestMetadata } from '@documenso/lib/universal/extract-request-metadata'; 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 { prisma } from '@documenso/prisma';
import { DocumentStatus, SigningStatus } from '@documenso/prisma/client'; import { DocumentStatus, SigningStatus } from '@documenso/prisma/client';
import { WebhookTriggerEvents } from '@documenso/prisma/client'; import { WebhookTriggerEvents } from '@documenso/prisma/client';
@ -93,8 +92,7 @@ export const completeDocumentWithToken = async ({
// throw new AppError(AppErrorCode.UNAUTHORIZED, 'Invalid authentication values'); // throw new AppError(AppErrorCode.UNAUTHORIZED, 'Invalid authentication values');
// } // }
await prisma.$transaction(async (tx) => { await prisma.recipient.update({
await tx.recipient.update({
where: { where: {
id: recipient.id, id: recipient.id,
}, },
@ -104,8 +102,9 @@ export const completeDocumentWithToken = async ({
}, },
}); });
await tx.documentAuditLog.create({ await queueJob({
data: createDocumentAuditLogData({ job: 'create-document-audit-log',
args: {
type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_RECIPIENT_COMPLETED, type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_RECIPIENT_COMPLETED,
documentId: document.id, documentId: document.id,
user: { user: {
@ -120,8 +119,7 @@ export const completeDocumentWithToken = async ({
recipientRole: recipient.role, recipientRole: recipient.role,
// actionAuth: derivedRecipientActionAuth || undefined, // actionAuth: derivedRecipientActionAuth || undefined,
}, },
}), },
});
}); });
const pendingRecipients = await prisma.recipient.count({ const pendingRecipients = await prisma.recipient.count({

View File

@ -3,10 +3,10 @@
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error'; import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
import { DOCUMENT_AUDIT_LOG_TYPE } from '@documenso/lib/types/document-audit-logs'; import { DOCUMENT_AUDIT_LOG_TYPE } from '@documenso/lib/types/document-audit-logs';
import type { RequestMetadata } from '@documenso/lib/universal/extract-request-metadata'; 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 { prisma } from '@documenso/prisma';
import { WebhookTriggerEvents } from '@documenso/prisma/client'; import { WebhookTriggerEvents } from '@documenso/prisma/client';
import { queueJob } from '../queue/job';
import { triggerWebhook } from '../webhooks/trigger/trigger-webhook'; import { triggerWebhook } from '../webhooks/trigger/trigger-webhook';
export type CreateDocumentOptions = { export type CreateDocumentOptions = {
@ -44,8 +44,7 @@ export const createDocument = async ({
throw new AppError(AppErrorCode.NOT_FOUND, 'Team not found'); throw new AppError(AppErrorCode.NOT_FOUND, 'Team not found');
} }
return await prisma.$transaction(async (tx) => { const document = await prisma.document.create({
const document = await tx.document.create({
data: { data: {
title, title,
documentDataId, documentDataId,
@ -54,8 +53,9 @@ export const createDocument = async ({
}, },
}); });
await tx.documentAuditLog.create({ await queueJob({
data: createDocumentAuditLogData({ job: 'create-document-audit-log',
args: {
type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_CREATED, type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_CREATED,
documentId: document.id, documentId: document.id,
user, user,
@ -63,7 +63,7 @@ export const createDocument = async ({
data: { data: {
title, title,
}, },
}), },
}); });
await triggerWebhook({ await triggerWebhook({
@ -74,5 +74,4 @@ export const createDocument = async ({
}); });
return document; return document;
});
}; };

View File

@ -12,7 +12,7 @@ import { NEXT_PUBLIC_WEBAPP_URL } from '../../constants/app';
import { FROM_ADDRESS, FROM_NAME } from '../../constants/email'; import { FROM_ADDRESS, FROM_NAME } from '../../constants/email';
import { DOCUMENT_AUDIT_LOG_TYPE } from '../../types/document-audit-logs'; import { DOCUMENT_AUDIT_LOG_TYPE } from '../../types/document-audit-logs';
import type { RequestMetadata } from '../../universal/extract-request-metadata'; import type { RequestMetadata } from '../../universal/extract-request-metadata';
import { createDocumentAuditLogData } from '../../utils/document-audit-logs'; import { queueJob } from '../queue/job';
export type DeleteDocumentOptions = { export type DeleteDocumentOptions = {
id: number; id: number;
@ -61,11 +61,11 @@ export const deleteDocument = async ({
// if the document is a draft, hard-delete // if the document is a draft, hard-delete
if (status === DocumentStatus.DRAFT) { if (status === DocumentStatus.DRAFT) {
return await prisma.$transaction(async (tx) => {
// Currently redundant since deleting a document will delete the audit logs. // Currently redundant since deleting a document will delete the audit logs.
// However may be useful if we disassociate audit lgos and documents if required. // However may be useful if we disassociate audit lgos and documents if required.
await tx.documentAuditLog.create({ await queueJob({
data: createDocumentAuditLogData({ job: 'create-document-audit-log',
args: {
documentId: id, documentId: id,
type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_DELETED, type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_DELETED,
user, user,
@ -73,11 +73,10 @@ export const deleteDocument = async ({
data: { data: {
type: 'HARD', type: 'HARD',
}, },
}), },
}); });
return await tx.document.delete({ where: { id, status: DocumentStatus.DRAFT } }); return await prisma.document.delete({ where: { id, status: DocumentStatus.DRAFT } });
});
} }
// if the document is pending, send cancellation emails to all recipients // if the document is pending, send cancellation emails to all recipients
@ -111,9 +110,9 @@ export const deleteDocument = async ({
} }
// If the document is not a draft, only soft-delete. // If the document is not a draft, only soft-delete.
return await prisma.$transaction(async (tx) => { await queueJob({
await tx.documentAuditLog.create({ job: 'create-document-audit-log',
data: createDocumentAuditLogData({ args: {
documentId: id, documentId: id,
type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_DELETED, type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_DELETED,
user, user,
@ -121,10 +120,10 @@ export const deleteDocument = async ({
data: { data: {
type: 'SOFT', type: 'SOFT',
}, },
}), },
}); });
return await tx.document.update({ return await prisma.document.update({
where: { where: {
id, id,
}, },
@ -132,5 +131,4 @@ export const deleteDocument = async ({
deletedAt: new Date().toISOString(), deletedAt: new Date().toISOString(),
}, },
}); });
});
}; };

View File

@ -10,13 +10,13 @@ import {
} from '@documenso/lib/constants/recipient-roles'; } from '@documenso/lib/constants/recipient-roles';
import { DOCUMENT_AUDIT_LOG_TYPE } from '@documenso/lib/types/document-audit-logs'; import { DOCUMENT_AUDIT_LOG_TYPE } from '@documenso/lib/types/document-audit-logs';
import type { RequestMetadata } from '@documenso/lib/universal/extract-request-metadata'; 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 { renderCustomEmailTemplate } from '@documenso/lib/utils/render-custom-email-template';
import { prisma } from '@documenso/prisma'; import { prisma } from '@documenso/prisma';
import { DocumentStatus, RecipientRole, SigningStatus } from '@documenso/prisma/client'; import { DocumentStatus, RecipientRole, SigningStatus } from '@documenso/prisma/client';
import type { Prisma } from '@documenso/prisma/client'; import type { Prisma } from '@documenso/prisma/client';
import { NEXT_PUBLIC_WEBAPP_URL } from '../../constants/app'; import { NEXT_PUBLIC_WEBAPP_URL } from '../../constants/app';
import { queueJob } from '../queue/job';
import { getDocumentWhereInput } from './get-document-by-id'; import { getDocumentWhereInput } from './get-document-by-id';
export type ResendDocumentOptions = { export type ResendDocumentOptions = {
@ -128,8 +128,9 @@ export const resendDocument = async ({
text: render(template, { plainText: true }), text: render(template, { plainText: true }),
}); });
await tx.documentAuditLog.create({ await queueJob({
data: createDocumentAuditLogData({ job: 'create-document-audit-log',
args: {
type: DOCUMENT_AUDIT_LOG_TYPE.EMAIL_SENT, type: DOCUMENT_AUDIT_LOG_TYPE.EMAIL_SENT,
documentId: document.id, documentId: document.id,
user, user,
@ -142,9 +143,10 @@ export const resendDocument = async ({
recipientId: recipient.id, recipientId: recipient.id,
isResending: true, isResending: true,
}, },
}), },
}); });
}, },
// Hopefully the queue makes this redundant
{ timeout: 30_000 }, { timeout: 30_000 },
); );
}), }),

View File

@ -5,13 +5,12 @@ import path from 'node:path';
import { PDFDocument } from 'pdf-lib'; import { PDFDocument } from 'pdf-lib';
import PostHogServerClient from '@documenso/lib/server-only/feature-flags/get-post-hog-server-client'; 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 { prisma } from '@documenso/prisma';
import { DocumentStatus, RecipientRole, SigningStatus } from '@documenso/prisma/client'; import { DocumentStatus, RecipientRole, SigningStatus } from '@documenso/prisma/client';
import { WebhookTriggerEvents } from '@documenso/prisma/client'; import { WebhookTriggerEvents } from '@documenso/prisma/client';
import { signPdf } from '@documenso/signing'; import { signPdf } from '@documenso/signing';
import { DOCUMENT_AUDIT_LOG_TYPE } from '../../types/document-audit-logs';
import type { RequestMetadata } from '../../universal/extract-request-metadata'; import type { RequestMetadata } from '../../universal/extract-request-metadata';
import { getFile } from '../../universal/upload/get-file'; import { getFile } from '../../universal/upload/get-file';
import { putFile } from '../../universal/upload/put-file'; import { putFile } from '../../universal/upload/put-file';
@ -126,8 +125,7 @@ export const sealDocument = async ({
}); });
} }
await prisma.$transaction(async (tx) => { await prisma.documentData.update({
await tx.documentData.update({
where: { where: {
id: documentData.id, id: documentData.id,
}, },
@ -136,8 +134,9 @@ export const sealDocument = async ({
}, },
}); });
await tx.documentAuditLog.create({ await queueJob({
data: createDocumentAuditLogData({ job: 'create-document-audit-log',
args: {
type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_COMPLETED, type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_COMPLETED,
documentId: document.id, documentId: document.id,
requestMetadata, requestMetadata,
@ -145,8 +144,7 @@ export const sealDocument = async ({
data: { data: {
transactionId: nanoid(), transactionId: nanoid(),
}, },
}), },
});
}); });
if (sendEmail && !isResealing) { if (sendEmail && !isResealing) {

View File

@ -9,7 +9,7 @@ import { NEXT_PUBLIC_WEBAPP_URL } from '../../constants/app';
import { DOCUMENT_AUDIT_LOG_TYPE } from '../../types/document-audit-logs'; import { DOCUMENT_AUDIT_LOG_TYPE } from '../../types/document-audit-logs';
import type { RequestMetadata } from '../../universal/extract-request-metadata'; import type { RequestMetadata } from '../../universal/extract-request-metadata';
import { getFile } from '../../universal/upload/get-file'; import { getFile } from '../../universal/upload/get-file';
import { createDocumentAuditLogData } from '../../utils/document-audit-logs'; import { queueJob } from '../queue/job';
export interface SendDocumentOptions { export interface SendDocumentOptions {
documentId: number; documentId: number;
@ -86,8 +86,9 @@ export const sendCompletedEmail = async ({ documentId, requestMetadata }: SendDo
], ],
}); });
await prisma.documentAuditLog.create({ await queueJob({
data: createDocumentAuditLogData({ job: 'create-document-audit-log',
args: {
type: DOCUMENT_AUDIT_LOG_TYPE.EMAIL_SENT, type: DOCUMENT_AUDIT_LOG_TYPE.EMAIL_SENT,
documentId: document.id, documentId: document.id,
user: null, user: null,
@ -100,7 +101,7 @@ export const sendCompletedEmail = async ({ documentId, requestMetadata }: SendDo
recipientRole: 'OWNER', recipientRole: 'OWNER',
isResending: false, isResending: false,
}, },
}), },
}); });
} }
@ -136,8 +137,9 @@ export const sendCompletedEmail = async ({ documentId, requestMetadata }: SendDo
], ],
}); });
await prisma.documentAuditLog.create({ await queueJob({
data: createDocumentAuditLogData({ job: 'create-document-audit-log',
args: {
type: DOCUMENT_AUDIT_LOG_TYPE.EMAIL_SENT, type: DOCUMENT_AUDIT_LOG_TYPE.EMAIL_SENT,
documentId: document.id, documentId: document.id,
user: null, user: null,
@ -150,7 +152,7 @@ export const sendCompletedEmail = async ({ documentId, requestMetadata }: SendDo
recipientRole: recipient.role, recipientRole: recipient.role,
isResending: false, isResending: false,
}, },
}), },
}); });
}), }),
); );

View File

@ -6,7 +6,6 @@ import { DocumentInviteEmailTemplate } from '@documenso/email/templates/document
import { FROM_ADDRESS, FROM_NAME } from '@documenso/lib/constants/email'; import { FROM_ADDRESS, FROM_NAME } from '@documenso/lib/constants/email';
import { DOCUMENT_AUDIT_LOG_TYPE } from '@documenso/lib/types/document-audit-logs'; import { DOCUMENT_AUDIT_LOG_TYPE } from '@documenso/lib/types/document-audit-logs';
import type { RequestMetadata } from '@documenso/lib/universal/extract-request-metadata'; 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 { renderCustomEmailTemplate } from '@documenso/lib/utils/render-custom-email-template';
import { prisma } from '@documenso/prisma'; import { prisma } from '@documenso/prisma';
import { DocumentStatus, RecipientRole, SendStatus } from '@documenso/prisma/client'; import { DocumentStatus, RecipientRole, SendStatus } from '@documenso/prisma/client';
@ -17,6 +16,7 @@ import {
RECIPIENT_ROLES_DESCRIPTION, RECIPIENT_ROLES_DESCRIPTION,
RECIPIENT_ROLE_TO_EMAIL_TYPE, RECIPIENT_ROLE_TO_EMAIL_TYPE,
} from '../../constants/recipient-roles'; } from '../../constants/recipient-roles';
import { queueJob } from '../queue/job';
import { triggerWebhook } from '../webhooks/trigger/trigger-webhook'; import { triggerWebhook } from '../webhooks/trigger/trigger-webhook';
export type SendDocumentOptions = { export type SendDocumentOptions = {
@ -113,8 +113,7 @@ export const sendDocument = async ({
const { actionVerb } = RECIPIENT_ROLES_DESCRIPTION[recipient.role]; const { actionVerb } = RECIPIENT_ROLES_DESCRIPTION[recipient.role];
await prisma.$transaction( // TODO: Move this to a seperate queue of it's own
async (tx) => {
await mailer.sendMail({ await mailer.sendMail({
to: { to: {
address: email, address: email,
@ -131,7 +130,7 @@ export const sendDocument = async ({
text: render(template, { plainText: true }), text: render(template, { plainText: true }),
}); });
await tx.recipient.update({ await prisma.recipient.update({
where: { where: {
id: recipient.id, id: recipient.id,
}, },
@ -140,8 +139,9 @@ export const sendDocument = async ({
}, },
}); });
await tx.documentAuditLog.create({ await queueJob({
data: createDocumentAuditLogData({ job: 'create-document-audit-log',
args: {
type: DOCUMENT_AUDIT_LOG_TYPE.EMAIL_SENT, type: DOCUMENT_AUDIT_LOG_TYPE.EMAIL_SENT,
documentId: document.id, documentId: document.id,
user, user,
@ -154,28 +154,25 @@ export const sendDocument = async ({
recipientId: recipient.id, recipientId: recipient.id,
isResending: false, isResending: false,
}, },
}),
});
}, },
{ timeout: 30_000 }, });
);
}), }),
); );
const updatedDocument = await prisma.$transaction(async (tx) => {
if (document.status === DocumentStatus.DRAFT) { if (document.status === DocumentStatus.DRAFT) {
await tx.documentAuditLog.create({ await queueJob({
data: createDocumentAuditLogData({ job: 'create-document-audit-log',
args: {
type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_SENT, type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_SENT,
documentId: document.id, documentId: document.id,
requestMetadata, requestMetadata,
user, user,
data: {}, data: {},
}), },
}); });
} }
return await tx.document.update({ const updatedDocument = await prisma.document.update({
where: { where: {
id: documentId, id: documentId,
}, },
@ -186,7 +183,6 @@ export const sendDocument = async ({
Recipient: true, Recipient: true,
}, },
}); });
});
await triggerWebhook({ await triggerWebhook({
event: WebhookTriggerEvents.DOCUMENT_SENT, event: WebhookTriggerEvents.DOCUMENT_SENT,

View File

@ -12,7 +12,7 @@ import { NEXT_PUBLIC_WEBAPP_URL } from '../../constants/app';
import { FROM_ADDRESS, FROM_NAME } from '../../constants/email'; import { FROM_ADDRESS, FROM_NAME } from '../../constants/email';
import { DOCUMENT_AUDIT_LOG_TYPE } from '../../types/document-audit-logs'; import { DOCUMENT_AUDIT_LOG_TYPE } from '../../types/document-audit-logs';
import type { RequestMetadata } from '../../universal/extract-request-metadata'; import type { RequestMetadata } from '../../universal/extract-request-metadata';
import { createDocumentAuditLogData } from '../../utils/document-audit-logs'; import { queueJob } from '../queue/job';
export type SuperDeleteDocumentOptions = { export type SuperDeleteDocumentOptions = {
id: number; id: number;
@ -66,10 +66,9 @@ export const superDeleteDocument = async ({ id, requestMetadata }: SuperDeleteDo
); );
} }
// always hard delete if deleted from admin await queueJob({
return await prisma.$transaction(async (tx) => { job: 'create-document-audit-log',
await tx.documentAuditLog.create({ args: {
data: createDocumentAuditLogData({
documentId: id, documentId: id,
type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_DELETED, type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_DELETED,
user, user,
@ -77,9 +76,9 @@ export const superDeleteDocument = async ({ id, requestMetadata }: SuperDeleteDo
data: { data: {
type: 'HARD', type: 'HARD',
}, },
}), },
}); });
return await tx.document.delete({ where: { id } }); // always hard delete if deleted from admin
}); return await prisma.document.delete({ where: { id } });
}; };

View File

@ -2,9 +2,10 @@
import { DOCUMENT_AUDIT_LOG_TYPE } from '@documenso/lib/types/document-audit-logs'; import { DOCUMENT_AUDIT_LOG_TYPE } from '@documenso/lib/types/document-audit-logs';
import type { RequestMetadata } from '@documenso/lib/universal/extract-request-metadata'; 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 { prisma } from '@documenso/prisma';
import { queueJob } from '../queue/job';
export type UpdateTitleOptions = { export type UpdateTitleOptions = {
userId: number; userId: number;
teamId?: number; teamId?: number;
@ -51,11 +52,10 @@ export const updateTitle = async ({
return document; return document;
} }
return await prisma.$transaction(async (tx) => {
// Instead of doing everything in a transaction we can use our knowledge // 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 // of the current document title to ensure we aren't performing a conflicting
// update. // update.
const updatedDocument = await tx.document.update({ const updatedDocument = await prisma.document.update({
where: { where: {
id: documentId, id: documentId,
title: document.title, title: document.title,
@ -65,8 +65,9 @@ export const updateTitle = async ({
}, },
}); });
await tx.documentAuditLog.create({ await queueJob({
data: createDocumentAuditLogData({ job: 'create-document-audit-log',
args: {
type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_TITLE_UPDATED, type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_TITLE_UPDATED,
documentId, documentId,
user, user,
@ -75,9 +76,8 @@ export const updateTitle = async ({
from: document.title, from: document.title,
to: updatedDocument.title, to: updatedDocument.title,
}, },
}), },
}); });
return updatedDocument; return updatedDocument;
});
}; };

View File

@ -1,11 +1,11 @@
import { DOCUMENT_AUDIT_LOG_TYPE } from '@documenso/lib/types/document-audit-logs'; import { DOCUMENT_AUDIT_LOG_TYPE } from '@documenso/lib/types/document-audit-logs';
import type { RequestMetadata } from '@documenso/lib/universal/extract-request-metadata'; 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 { prisma } from '@documenso/prisma';
import { ReadStatus } from '@documenso/prisma/client'; import { ReadStatus } from '@documenso/prisma/client';
import { WebhookTriggerEvents } from '@documenso/prisma/client'; import { WebhookTriggerEvents } from '@documenso/prisma/client';
import type { TDocumentAccessAuthTypes } from '../../types/document-auth'; import type { TDocumentAccessAuthTypes } from '../../types/document-auth';
import { queueJob } from '../queue/job';
import { triggerWebhook } from '../webhooks/trigger/trigger-webhook'; import { triggerWebhook } from '../webhooks/trigger/trigger-webhook';
import { getDocumentAndRecipientByToken } from './get-document-by-token'; import { getDocumentAndRecipientByToken } from './get-document-by-token';
@ -33,8 +33,7 @@ export const viewedDocument = async ({
const { documentId } = recipient; const { documentId } = recipient;
await prisma.$transaction(async (tx) => { await prisma.recipient.update({
await tx.recipient.update({
where: { where: {
id: recipient.id, id: recipient.id,
}, },
@ -43,8 +42,9 @@ export const viewedDocument = async ({
}, },
}); });
await tx.documentAuditLog.create({ await queueJob({
data: createDocumentAuditLogData({ job: 'create-document-audit-log',
args: {
type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_OPENED, type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_OPENED,
documentId, documentId,
user: { user: {
@ -59,8 +59,7 @@ export const viewedDocument = async ({
recipientRole: recipient.role, recipientRole: recipient.role,
accessAuth: recipientAccessAuth || undefined, accessAuth: recipientAccessAuth || undefined,
}, },
}), },
});
}); });
const document = await getDocumentAndRecipientByToken({ token, requireAccessAuth: false }); const document = await getDocumentAndRecipientByToken({ token, requireAccessAuth: false });

View File

@ -2,7 +2,7 @@ import { prisma } from '@documenso/prisma';
import type { FieldType, Team } from '@documenso/prisma/client'; import type { FieldType, Team } from '@documenso/prisma/client';
import type { RequestMetadata } from '../../universal/extract-request-metadata'; import type { RequestMetadata } from '../../universal/extract-request-metadata';
import { createDocumentAuditLogData } from '../../utils/document-audit-logs'; import { queueJob } from '../queue/job';
export type CreateFieldOptions = { export type CreateFieldOptions = {
documentId: number; documentId: number;
@ -103,8 +103,9 @@ export const createField = async ({
}, },
}); });
await prisma.documentAuditLog.create({ await queueJob({
data: createDocumentAuditLogData({ job: 'create-document-audit-log',
args: {
type: 'FIELD_CREATED', type: 'FIELD_CREATED',
documentId, documentId,
user: { user: {
@ -119,7 +120,7 @@ export const createField = async ({
fieldType: field.type, fieldType: field.type,
}, },
requestMetadata, requestMetadata,
}), },
}); });
return field; return field;

View File

@ -2,7 +2,7 @@ import { prisma } from '@documenso/prisma';
import type { Team } from '@documenso/prisma/client'; import type { Team } from '@documenso/prisma/client';
import type { RequestMetadata } from '../../universal/extract-request-metadata'; import type { RequestMetadata } from '../../universal/extract-request-metadata';
import { createDocumentAuditLogData } from '../../utils/document-audit-logs'; import { queueJob } from '../queue/job';
export type DeleteFieldOptions = { export type DeleteFieldOptions = {
fieldId: number; fieldId: number;
@ -67,8 +67,9 @@ export const deleteField = async ({
}); });
} }
await prisma.documentAuditLog.create({ await queueJob({
data: createDocumentAuditLogData({ job: 'create-document-audit-log',
args: {
type: 'FIELD_DELETED', type: 'FIELD_DELETED',
documentId, documentId,
user: { user: {
@ -83,7 +84,7 @@ export const deleteField = async ({
fieldType: field.type, fieldType: field.type,
}, },
requestMetadata, requestMetadata,
}), },
}); });
return field; return field;

View File

@ -2,10 +2,11 @@
import { DOCUMENT_AUDIT_LOG_TYPE } from '@documenso/lib/types/document-audit-logs'; import { DOCUMENT_AUDIT_LOG_TYPE } from '@documenso/lib/types/document-audit-logs';
import type { RequestMetadata } from '@documenso/lib/universal/extract-request-metadata'; 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 { prisma } from '@documenso/prisma';
import { DocumentStatus, SigningStatus } from '@documenso/prisma/client'; import { DocumentStatus, SigningStatus } from '@documenso/prisma/client';
import { queueJob } from '../queue/job';
export type RemovedSignedFieldWithTokenOptions = { export type RemovedSignedFieldWithTokenOptions = {
token: string; token: string;
fieldId: number; fieldId: number;
@ -65,9 +66,11 @@ export const removeSignedFieldWithToken = async ({
fieldId: field.id, fieldId: field.id,
}, },
}); });
});
await tx.documentAuditLog.create({ await queueJob({
data: createDocumentAuditLogData({ job: 'create-document-audit-log',
args: {
type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_FIELD_UNINSERTED, type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_FIELD_UNINSERTED,
documentId: document.id, documentId: document.id,
user: { user: {
@ -79,7 +82,6 @@ export const removeSignedFieldWithToken = async ({
field: field.type, field: field.type,
fieldId: field.secondaryId, fieldId: field.secondaryId,
}, },
}), },
});
}); });
}; };

View File

@ -8,6 +8,8 @@ import { prisma } from '@documenso/prisma';
import type { Field, FieldType } from '@documenso/prisma/client'; import type { Field, FieldType } from '@documenso/prisma/client';
import { SendStatus, SigningStatus } from '@documenso/prisma/client'; import { SendStatus, SigningStatus } from '@documenso/prisma/client';
import { queueJob } from '../queue/job';
export interface SetFieldsForDocumentOptions { export interface SetFieldsForDocumentOptions {
userId: number; userId: number;
documentId: number; documentId: number;
@ -155,8 +157,9 @@ export const setFieldsForDocument = async ({
// Handle field updated audit log. // Handle field updated audit log.
if (field._persisted && changes.length > 0) { if (field._persisted && changes.length > 0) {
await tx.documentAuditLog.create({ await queueJob({
data: createDocumentAuditLogData({ job: 'create-document-audit-log',
args: {
type: DOCUMENT_AUDIT_LOG_TYPE.FIELD_UPDATED, type: DOCUMENT_AUDIT_LOG_TYPE.FIELD_UPDATED,
documentId: documentId, documentId: documentId,
user, user,
@ -165,14 +168,15 @@ export const setFieldsForDocument = async ({
changes, changes,
...baseAuditLog, ...baseAuditLog,
}, },
}), },
}); });
} }
// Handle field created audit log. // Handle field created audit log.
if (!field._persisted) { if (!field._persisted) {
await tx.documentAuditLog.create({ await queueJob({
data: createDocumentAuditLogData({ job: 'create-document-audit-log',
args: {
type: DOCUMENT_AUDIT_LOG_TYPE.FIELD_CREATED, type: DOCUMENT_AUDIT_LOG_TYPE.FIELD_CREATED,
documentId: documentId, documentId: documentId,
user, user,
@ -180,7 +184,7 @@ export const setFieldsForDocument = async ({
data: { data: {
...baseAuditLog, ...baseAuditLog,
}, },
}), },
}); });
} }

View File

@ -12,9 +12,9 @@ import { AppError, AppErrorCode } from '../../errors/app-error';
import { DOCUMENT_AUDIT_LOG_TYPE } from '../../types/document-audit-logs'; import { DOCUMENT_AUDIT_LOG_TYPE } from '../../types/document-audit-logs';
import type { TRecipientActionAuth } from '../../types/document-auth'; import type { TRecipientActionAuth } from '../../types/document-auth';
import type { RequestMetadata } from '../../universal/extract-request-metadata'; import type { RequestMetadata } from '../../universal/extract-request-metadata';
import { createDocumentAuditLogData } from '../../utils/document-audit-logs';
import { extractDocumentAuthMethods } from '../../utils/document-auth'; import { extractDocumentAuthMethods } from '../../utils/document-auth';
import { isRecipientAuthorized } from '../document/is-recipient-authorized'; import { isRecipientAuthorized } from '../document/is-recipient-authorized';
import { queueJob } from '../queue/job';
export type SignFieldWithTokenOptions = { export type SignFieldWithTokenOptions = {
token: string; token: string;
@ -168,8 +168,9 @@ export const signFieldWithToken = async ({
}); });
} }
await tx.documentAuditLog.create({ await queueJob({
data: createDocumentAuditLogData({ job: 'create-document-audit-log',
args: {
type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_FIELD_INSERTED, type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_FIELD_INSERTED,
documentId: document.id, documentId: document.id,
user: { user: {
@ -199,7 +200,7 @@ export const signFieldWithToken = async ({
} }
: undefined, : undefined,
}, },
}), },
}); });
return updatedField; return updatedField;

View File

@ -3,7 +3,8 @@ import type { FieldType, Team } from '@documenso/prisma/client';
import { DOCUMENT_AUDIT_LOG_TYPE } from '../../types/document-audit-logs'; import { DOCUMENT_AUDIT_LOG_TYPE } from '../../types/document-audit-logs';
import type { RequestMetadata } from '../../universal/extract-request-metadata'; 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 = { export type UpdateFieldOptions = {
fieldId: number; fieldId: number;
@ -77,8 +78,9 @@ export const updateField = async ({
}, },
}); });
await tx.documentAuditLog.create({ await queueJob({
data: createDocumentAuditLogData({ job: 'create-document-audit-log',
args: {
type: DOCUMENT_AUDIT_LOG_TYPE.FIELD_UPDATED, type: DOCUMENT_AUDIT_LOG_TYPE.FIELD_UPDATED,
documentId, documentId,
user: { user: {
@ -94,7 +96,7 @@ export const updateField = async ({
changes: diffFieldChanges(oldField, updatedField), changes: diffFieldChanges(oldField, updatedField),
}, },
requestMetadata, requestMetadata,
}), },
}); });
return updatedField; return updatedField;

View File

@ -1,6 +1,10 @@
import type { WorkHandler } from 'pg-boss'; import type { WorkHandler } from 'pg-boss';
import { prisma } from '@documenso/prisma';
import { initQueue } from '.'; import { initQueue } from '.';
import type { CreateDocumentAuditLogDataOptions } from '../../utils/document-audit-logs';
import { createDocumentAuditLogData } from '../../utils/document-audit-logs';
import { import {
type SendDocumentOptions as SendCompletedDocumentOptions, type SendDocumentOptions as SendCompletedDocumentOptions,
sendCompletedEmail, sendCompletedEmail,
@ -10,6 +14,7 @@ import { type SendPendingEmailOptions, sendPendingEmail } from '../document/send
type JobOptions = { type JobOptions = {
'send-completed-email': SendCompletedDocumentOptions; 'send-completed-email': SendCompletedDocumentOptions;
'send-pending-email': SendPendingEmailOptions; 'send-pending-email': SendPendingEmailOptions;
'create-document-audit-log': CreateDocumentAuditLogDataOptions;
}; };
export const jobHandlers: { export const jobHandlers: {
@ -27,6 +32,24 @@ export const jobHandlers: {
recipientId, 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 ({ export const queueJob = async ({

View File

@ -3,7 +3,7 @@ import type { Team } from '@documenso/prisma/client';
import { SendStatus } from '@documenso/prisma/client'; import { SendStatus } from '@documenso/prisma/client';
import type { RequestMetadata } from '../../universal/extract-request-metadata'; import type { RequestMetadata } from '../../universal/extract-request-metadata';
import { createDocumentAuditLogData } from '../../utils/document-audit-logs'; import { queueJob } from '../queue/job';
export type DeleteRecipientOptions = { export type DeleteRecipientOptions = {
documentId: number; documentId: number;
@ -73,15 +73,15 @@ export const deleteRecipient = async ({
}); });
} }
const deletedRecipient = await prisma.$transaction(async (tx) => { const deletedRecipient = await prisma.recipient.delete({
const deleted = await tx.recipient.delete({
where: { where: {
id: recipient.id, id: recipient.id,
}, },
}); });
await tx.documentAuditLog.create({ await queueJob({
data: createDocumentAuditLogData({ job: 'create-document-audit-log',
args: {
type: 'RECIPIENT_DELETED', type: 'RECIPIENT_DELETED',
documentId, documentId,
user: { user: {
@ -96,10 +96,7 @@ export const deleteRecipient = async ({
recipientRole: recipient.role, recipientRole: recipient.role,
}, },
requestMetadata, requestMetadata,
}), },
});
return deleted;
}); });
return deletedRecipient; return deletedRecipient;

View File

@ -17,6 +17,7 @@ import { RecipientRole } from '@documenso/prisma/client';
import { SendStatus, SigningStatus } from '@documenso/prisma/client'; import { SendStatus, SigningStatus } from '@documenso/prisma/client';
import { AppError, AppErrorCode } from '../../errors/app-error'; import { AppError, AppErrorCode } from '../../errors/app-error';
import { queueJob } from '../queue/job';
export interface SetRecipientsForDocumentOptions { export interface SetRecipientsForDocumentOptions {
userId: number; userId: number;
@ -203,8 +204,9 @@ export const setRecipientsForDocument = async ({
// Handle recipient updated audit log. // Handle recipient updated audit log.
if (recipient._persisted && changes.length > 0) { if (recipient._persisted && changes.length > 0) {
await tx.documentAuditLog.create({ await queueJob({
data: createDocumentAuditLogData({ job: 'create-document-audit-log',
args: {
type: DOCUMENT_AUDIT_LOG_TYPE.RECIPIENT_UPDATED, type: DOCUMENT_AUDIT_LOG_TYPE.RECIPIENT_UPDATED,
documentId: documentId, documentId: documentId,
user, user,
@ -213,14 +215,15 @@ export const setRecipientsForDocument = async ({
changes, changes,
...baseAuditLog, ...baseAuditLog,
}, },
}), },
}); });
} }
// Handle recipient created audit log. // Handle recipient created audit log.
if (!recipient._persisted) { if (!recipient._persisted) {
await tx.documentAuditLog.create({ await queueJob({
data: createDocumentAuditLogData({ job: 'create-document-audit-log',
args: {
type: DOCUMENT_AUDIT_LOG_TYPE.RECIPIENT_CREATED, type: DOCUMENT_AUDIT_LOG_TYPE.RECIPIENT_CREATED,
documentId: documentId, documentId: documentId,
user, user,
@ -229,7 +232,7 @@ export const setRecipientsForDocument = async ({
...baseAuditLog, ...baseAuditLog,
actionAuth: recipient.actionAuth || undefined, actionAuth: recipient.actionAuth || undefined,
}, },
}), },
}); });
} }

View File

@ -3,7 +3,8 @@ import type { RecipientRole, Team } from '@documenso/prisma/client';
import { DOCUMENT_AUDIT_LOG_TYPE } from '../../types/document-audit-logs'; import { DOCUMENT_AUDIT_LOG_TYPE } from '../../types/document-audit-logs';
import type { RequestMetadata } from '../../universal/extract-request-metadata'; 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 = { export type UpdateRecipientOptions = {
documentId: number; documentId: number;
@ -75,8 +76,7 @@ export const updateRecipient = async ({
throw new Error('Recipient not found'); throw new Error('Recipient not found');
} }
const updatedRecipient = await prisma.$transaction(async (tx) => { const updatedRecipient = await prisma.recipient.update({
const persisted = await prisma.recipient.update({
where: { where: {
id: recipient.id, id: recipient.id,
}, },
@ -87,11 +87,12 @@ export const updateRecipient = async ({
}, },
}); });
const changes = diffRecipientChanges(recipient, persisted); const changes = diffRecipientChanges(recipient, updatedRecipient);
if (changes.length > 0) { if (changes.length > 0) {
await tx.documentAuditLog.create({ await queueJob({
data: createDocumentAuditLogData({ job: 'create-document-audit-log',
args: {
type: DOCUMENT_AUDIT_LOG_TYPE.RECIPIENT_UPDATED, type: DOCUMENT_AUDIT_LOG_TYPE.RECIPIENT_UPDATED,
documentId: documentId, documentId: documentId,
user: { user: {
@ -103,16 +104,15 @@ export const updateRecipient = async ({
data: { data: {
changes, changes,
recipientId, recipientId,
recipientEmail: persisted.email, recipientEmail: updatedRecipient.email,
recipientName: persisted.name, recipientName: updatedRecipient.name,
recipientRole: persisted.role, recipientRole: updatedRecipient.role,
},
}, },
}),
});
return persisted;
}
}); });
return updatedRecipient;
}
return updatedRecipient; return updatedRecipient;
}; };

View File

@ -25,7 +25,7 @@ import {
import { ZRecipientAuthOptionsSchema } from '../types/document-auth'; import { ZRecipientAuthOptionsSchema } from '../types/document-auth';
import type { RequestMetadata } from '../universal/extract-request-metadata'; import type { RequestMetadata } from '../universal/extract-request-metadata';
type CreateDocumentAuditLogDataOptions<T = TDocumentAuditLog['type']> = { export type CreateDocumentAuditLogDataOptions<T = TDocumentAuditLog['type']> = {
documentId: number; documentId: number;
type: T; type: T;
data: Extract<TDocumentAuditLog, { type: T }>['data']; data: Extract<TDocumentAuditLog, { type: T }>['data'];