mirror of
https://github.com/documenso/documenso.git
synced 2025-11-11 21:12:48 +10:00
Add ability to enable or disable allowed signature types: - Drawn - Typed - Uploaded **Tabbed style signature dialog**  **Document settings**  **Team preferences**  - Add multiselect to select allowed signatures in document and templates settings tab - Add multiselect to select allowed signatures in teams preferences - Removed "Enable typed signatures" from document/template edit page - Refactored signature pad to use tabs instead of an all in one signature pad Added E2E tests to check settings are applied correctly for documents and templates
178 lines
4.8 KiB
TypeScript
178 lines
4.8 KiB
TypeScript
import { DocumentSource, WebhookTriggerEvents } from '@prisma/client';
|
|
import type { Team, TeamGlobalSettings } from '@prisma/client';
|
|
import { TeamMemberRole } from '@prisma/client';
|
|
|
|
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
|
|
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';
|
|
|
|
import {
|
|
ZWebhookDocumentSchema,
|
|
mapDocumentToWebhookDocumentPayload,
|
|
} from '../../types/webhook-payload';
|
|
import { getFileServerSide } from '../../universal/upload/get-file.server';
|
|
import { putPdfFileServerSide } from '../../universal/upload/put-file.server';
|
|
import { determineDocumentVisibility } from '../../utils/document-visibility';
|
|
import { triggerWebhook } from '../webhooks/trigger/trigger-webhook';
|
|
|
|
export type CreateDocumentOptions = {
|
|
title: string;
|
|
externalId?: string | null;
|
|
userId: number;
|
|
teamId?: number;
|
|
documentDataId: string;
|
|
formValues?: Record<string, string | number | boolean>;
|
|
normalizePdf?: boolean;
|
|
timezone?: string;
|
|
requestMetadata: ApiRequestMetadata;
|
|
};
|
|
|
|
export const createDocument = async ({
|
|
userId,
|
|
title,
|
|
externalId,
|
|
documentDataId,
|
|
teamId,
|
|
normalizePdf,
|
|
formValues,
|
|
requestMetadata,
|
|
timezone,
|
|
}: CreateDocumentOptions) => {
|
|
const user = await prisma.user.findFirstOrThrow({
|
|
where: {
|
|
id: userId,
|
|
},
|
|
include: {
|
|
teamMembers: {
|
|
select: {
|
|
teamId: true,
|
|
},
|
|
},
|
|
},
|
|
});
|
|
|
|
if (
|
|
teamId !== undefined &&
|
|
!user.teamMembers.some((teamMember) => teamMember.teamId === teamId)
|
|
) {
|
|
throw new AppError(AppErrorCode.NOT_FOUND, {
|
|
message: 'Team not found',
|
|
});
|
|
}
|
|
|
|
let team: (Team & { teamGlobalSettings: TeamGlobalSettings | null }) | null = null;
|
|
let userTeamRole: TeamMemberRole | undefined;
|
|
|
|
if (teamId) {
|
|
const teamWithUserRole = await prisma.team.findFirstOrThrow({
|
|
where: {
|
|
id: teamId,
|
|
},
|
|
include: {
|
|
teamGlobalSettings: true,
|
|
members: {
|
|
where: {
|
|
userId: userId,
|
|
},
|
|
select: {
|
|
role: true,
|
|
},
|
|
},
|
|
},
|
|
});
|
|
|
|
team = teamWithUserRole;
|
|
userTeamRole = teamWithUserRole.members[0]?.role;
|
|
}
|
|
|
|
if (normalizePdf) {
|
|
const documentData = await prisma.documentData.findFirst({
|
|
where: {
|
|
id: documentDataId,
|
|
},
|
|
});
|
|
|
|
if (documentData) {
|
|
const buffer = await getFileServerSide(documentData);
|
|
|
|
const normalizedPdf = await makeNormalizedPdf(Buffer.from(buffer));
|
|
|
|
const newDocumentData = await putPdfFileServerSide({
|
|
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;
|
|
}
|
|
}
|
|
|
|
return await prisma.$transaction(async (tx) => {
|
|
const document = await tx.document.create({
|
|
data: {
|
|
title,
|
|
externalId,
|
|
documentDataId,
|
|
userId,
|
|
teamId,
|
|
visibility: determineDocumentVisibility(
|
|
team?.teamGlobalSettings?.documentVisibility,
|
|
userTeamRole ?? TeamMemberRole.MEMBER,
|
|
),
|
|
formValues,
|
|
source: DocumentSource.DOCUMENT,
|
|
documentMeta: {
|
|
create: {
|
|
language: team?.teamGlobalSettings?.documentLanguage,
|
|
timezone: timezone,
|
|
typedSignatureEnabled: team?.teamGlobalSettings?.typedSignatureEnabled ?? true,
|
|
uploadSignatureEnabled: team?.teamGlobalSettings?.uploadSignatureEnabled ?? true,
|
|
drawSignatureEnabled: team?.teamGlobalSettings?.drawSignatureEnabled ?? true,
|
|
},
|
|
},
|
|
},
|
|
});
|
|
|
|
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,
|
|
recipients: true,
|
|
},
|
|
});
|
|
|
|
if (!createdDocument) {
|
|
throw new Error('Document not found');
|
|
}
|
|
|
|
await triggerWebhook({
|
|
event: WebhookTriggerEvents.DOCUMENT_CREATED,
|
|
data: ZWebhookDocumentSchema.parse(mapDocumentToWebhookDocumentPayload(createdDocument)),
|
|
userId,
|
|
teamId,
|
|
});
|
|
|
|
return createdDocument;
|
|
});
|
|
};
|