documenso/packages/lib/server-only/document/create-document.ts

172 lines
4.7 KiB
TypeScript
Raw Normal View History

2025-01-02 04:33:37 +00:00
import { DocumentSource, WebhookTriggerEvents } from '@prisma/client';
2025-06-10 01:49:52 +00:00
import type { DocumentVisibility } from '@prisma/client';
2024-12-17 21:14:14 +00:00
import { normalizePdf as makeNormalizedPdf } from '@documenso/lib/server-only/pdf/normalize-pdf';
import { DOCUMENT_AUDIT_LOG_TYPE } from '@documenso/lib/types/document-audit-logs';
import type { ApiRequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
import { createDocumentAuditLogData } from '@documenso/lib/utils/document-audit-logs';
import { prisma } from '@documenso/prisma';
2024-02-16 09:04:11 +00:00
2025-06-10 01:49:52 +00:00
import { AppError, AppErrorCode } from '../../errors/app-error';
2025-01-13 02:41:53 +00:00
import {
ZWebhookDocumentSchema,
mapDocumentToWebhookDocumentPayload,
} from '../../types/webhook-payload';
import { prefixedId } from '../../universal/id';
2025-01-02 04:33:37 +00:00
import { getFileServerSide } from '../../universal/upload/get-file.server';
import { putPdfFileServerSide } from '../../universal/upload/put-file.server';
import { extractDerivedDocumentMeta } from '../../utils/document';
import { determineDocumentVisibility } from '../../utils/document-visibility';
import { buildTeamWhereQuery } from '../../utils/teams';
2025-06-10 01:49:52 +00:00
import { getTeamById } from '../team/get-team';
import { getTeamSettings } from '../team/get-team-settings';
import { triggerWebhook } from '../webhooks/trigger/trigger-webhook';
export type CreateDocumentOptions = {
title: string;
externalId?: string | null;
userId: number;
2025-06-10 01:49:52 +00:00
teamId: number;
documentDataId: string;
formValues?: Record<string, string | number | boolean>;
2024-12-17 21:14:14 +00:00
normalizePdf?: boolean;
timezone?: string;
userTimezone?: string;
requestMetadata: ApiRequestMetadata;
2025-05-01 16:46:59 +00:00
folderId?: string;
};
export const createDocument = async ({
userId,
title,
externalId,
documentDataId,
teamId,
2024-12-17 21:14:14 +00:00
normalizePdf,
formValues,
requestMetadata,
timezone,
userTimezone,
2025-05-01 16:46:59 +00:00
folderId,
}: CreateDocumentOptions) => {
2025-06-10 01:49:52 +00:00
const team = await getTeamById({ userId, teamId });
feat: add global settings for teams (#1391) ## Description This PR introduces global settings for teams. At the moment, it allows team admins to configure the following: * The default visibility of the documents uploaded to the team account * Whether to include the document owner (sender) details when sending emails to the recipients. ### Include Sender Details If the Sender Details setting is enabled, the emails sent by the team will include the sender's name: > "Example User" on behalf of "Example Team" has invited you to sign "document.pdf" Otherwise, the email will say: > "Example Team" has invited you to sign "document.pdf" ### Default Document Visibility This new option allows users to set the default visibility for the documents uploaded to the team account. It can have the following values: * Everyone * Manager and above * Admins only If the default document visibility isn't set, the document will be set to the role of the user who created the document: * If a user with the "User" role creates a document, the document's visibility is set to "Everyone". * Manager role -> "Manager and above" * Admin role -> "Admins only" Otherwise, if there is a default document visibility value, it uses that value. #### Gotcha To avoid issues, the `document owner` and the `recipient` can access the document irrespective of their role. For example: * If a team member with the role "Member" uploads a document and the default document visibility is "Admins", only the document owner and admins can access the document. * Similar to the other scenarios. * If an admin uploads a document and the default document visibility is "Admins", the recipient can access the document. * The admins have access to all the documents. * Managers have access to documents with the visibility set to "Everyone" and "Manager and above" * Members have access only to the documents with the visibility set to "Everyone". ## Testing Performed Tested it locally.
2024-11-08 11:50:49 +00:00
2025-06-10 01:49:52 +00:00
const settings = await getTeamSettings({
userId,
teamId,
});
feat: add global settings for teams (#1391) ## Description This PR introduces global settings for teams. At the moment, it allows team admins to configure the following: * The default visibility of the documents uploaded to the team account * Whether to include the document owner (sender) details when sending emails to the recipients. ### Include Sender Details If the Sender Details setting is enabled, the emails sent by the team will include the sender's name: > "Example User" on behalf of "Example Team" has invited you to sign "document.pdf" Otherwise, the email will say: > "Example Team" has invited you to sign "document.pdf" ### Default Document Visibility This new option allows users to set the default visibility for the documents uploaded to the team account. It can have the following values: * Everyone * Manager and above * Admins only If the default document visibility isn't set, the document will be set to the role of the user who created the document: * If a user with the "User" role creates a document, the document's visibility is set to "Everyone". * Manager role -> "Manager and above" * Admin role -> "Admins only" Otherwise, if there is a default document visibility value, it uses that value. #### Gotcha To avoid issues, the `document owner` and the `recipient` can access the document irrespective of their role. For example: * If a team member with the role "Member" uploads a document and the default document visibility is "Admins", only the document owner and admins can access the document. * Similar to the other scenarios. * If an admin uploads a document and the default document visibility is "Admins", the recipient can access the document. * The admins have access to all the documents. * Managers have access to documents with the visibility set to "Everyone" and "Manager and above" * Members have access only to the documents with the visibility set to "Everyone". ## Testing Performed Tested it locally.
2024-11-08 11:50:49 +00:00
2025-05-01 16:46:59 +00:00
let folderVisibility: DocumentVisibility | undefined;
if (folderId) {
const folder = await prisma.folder.findFirst({
where: {
id: folderId,
team: buildTeamWhereQuery({
teamId,
userId,
}),
2025-05-01 16:46:59 +00:00
},
select: {
visibility: true,
},
});
if (!folder) {
throw new AppError(AppErrorCode.NOT_FOUND, {
message: 'Folder not found',
});
}
folderVisibility = folder.visibility;
}
2024-12-17 21:14:14 +00:00
if (normalizePdf) {
const documentData = await prisma.documentData.findFirst({
where: {
id: documentDataId,
},
});
if (documentData) {
2025-01-02 04:33:37 +00:00
const buffer = await getFileServerSide(documentData);
2024-12-17 21:14:14 +00:00
const normalizedPdf = await makeNormalizedPdf(Buffer.from(buffer));
2025-01-02 04:33:37 +00:00
const newDocumentData = await putPdfFileServerSide({
2024-12-17 21:14:14 +00:00
name: title.endsWith('.pdf') ? title : `${title}.pdf`,
type: 'application/pdf',
arrayBuffer: async () => Promise.resolve(normalizedPdf),
});
// eslint-disable-next-line require-atomic-updates
documentDataId = newDocumentData.id;
}
}
// userTimezone is last because it's always passed in regardless of the organisation/team settings
// for uploads from the frontend
const timezoneToUse = timezone || settings.documentTimezone || userTimezone;
return await prisma.$transaction(async (tx) => {
const document = await tx.document.create({
data: {
title,
qrToken: prefixedId('qr'),
externalId,
documentDataId,
userId,
teamId,
2025-05-01 16:46:59 +00:00
folderId,
visibility:
folderVisibility ??
2025-06-10 01:49:52 +00:00
determineDocumentVisibility(settings.documentVisibility, team.currentTeamRole),
formValues,
source: DocumentSource.DOCUMENT,
feat: add global settings for teams (#1391) ## Description This PR introduces global settings for teams. At the moment, it allows team admins to configure the following: * The default visibility of the documents uploaded to the team account * Whether to include the document owner (sender) details when sending emails to the recipients. ### Include Sender Details If the Sender Details setting is enabled, the emails sent by the team will include the sender's name: > "Example User" on behalf of "Example Team" has invited you to sign "document.pdf" Otherwise, the email will say: > "Example Team" has invited you to sign "document.pdf" ### Default Document Visibility This new option allows users to set the default visibility for the documents uploaded to the team account. It can have the following values: * Everyone * Manager and above * Admins only If the default document visibility isn't set, the document will be set to the role of the user who created the document: * If a user with the "User" role creates a document, the document's visibility is set to "Everyone". * Manager role -> "Manager and above" * Admin role -> "Admins only" Otherwise, if there is a default document visibility value, it uses that value. #### Gotcha To avoid issues, the `document owner` and the `recipient` can access the document irrespective of their role. For example: * If a team member with the role "Member" uploads a document and the default document visibility is "Admins", only the document owner and admins can access the document. * Similar to the other scenarios. * If an admin uploads a document and the default document visibility is "Admins", the recipient can access the document. * The admins have access to all the documents. * Managers have access to documents with the visibility set to "Everyone" and "Manager and above" * Members have access only to the documents with the visibility set to "Everyone". ## Testing Performed Tested it locally.
2024-11-08 11:50:49 +00:00
documentMeta: {
create: extractDerivedDocumentMeta(settings, {
timezone: timezoneToUse,
}),
feat: add global settings for teams (#1391) ## Description This PR introduces global settings for teams. At the moment, it allows team admins to configure the following: * The default visibility of the documents uploaded to the team account * Whether to include the document owner (sender) details when sending emails to the recipients. ### Include Sender Details If the Sender Details setting is enabled, the emails sent by the team will include the sender's name: > "Example User" on behalf of "Example Team" has invited you to sign "document.pdf" Otherwise, the email will say: > "Example Team" has invited you to sign "document.pdf" ### Default Document Visibility This new option allows users to set the default visibility for the documents uploaded to the team account. It can have the following values: * Everyone * Manager and above * Admins only If the default document visibility isn't set, the document will be set to the role of the user who created the document: * If a user with the "User" role creates a document, the document's visibility is set to "Everyone". * Manager role -> "Manager and above" * Admin role -> "Admins only" Otherwise, if there is a default document visibility value, it uses that value. #### Gotcha To avoid issues, the `document owner` and the `recipient` can access the document irrespective of their role. For example: * If a team member with the role "Member" uploads a document and the default document visibility is "Admins", only the document owner and admins can access the document. * Similar to the other scenarios. * If an admin uploads a document and the default document visibility is "Admins", the recipient can access the document. * The admins have access to all the documents. * Managers have access to documents with the visibility set to "Everyone" and "Manager and above" * Members have access only to the documents with the visibility set to "Everyone". ## Testing Performed Tested it locally.
2024-11-08 11:50:49 +00:00
},
},
});
2024-02-16 09:04:11 +00:00
await tx.documentAuditLog.create({
data: createDocumentAuditLogData({
type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_CREATED,
documentId: document.id,
metadata: requestMetadata,
data: {
title,
source: {
type: DocumentSource.DOCUMENT,
},
},
}),
});
const createdDocument = await tx.document.findFirst({
where: {
id: document.id,
},
include: {
documentMeta: true,
2025-01-13 02:41:53 +00:00
recipients: true,
},
});
if (!createdDocument) {
throw new Error('Document not found');
}
2024-02-16 09:04:11 +00:00
await triggerWebhook({
event: WebhookTriggerEvents.DOCUMENT_CREATED,
2025-01-13 02:41:53 +00:00
data: ZWebhookDocumentSchema.parse(mapDocumentToWebhookDocumentPayload(createdDocument)),
userId,
teamId,
2024-02-16 09:04:11 +00:00
});
return createdDocument;
});
};