mirror of
https://github.com/documenso/documenso.git
synced 2026-07-25 17:35:05 +10:00
chore: merge main, resolve biome formatting conflicts
Merge origin/main into feat/external-2fa-codes. Resolve formatting conflicts caused by biome rollout; preserve both feature streams: PR's external 2FA token + signing-session 2FA proof additions plus main's RateLimit/RecipientExpired/signingReminders/date-auto-insert. In complete-document-with-token.ts, drop the duplicate early field-fetching block introduced when main moved that logic later with date auto-insert support; keep the EXTERNAL_TWO_FACTOR_AUTH check using derivedRecipientActionAuth.
This commit is contained in:
@@ -2,9 +2,9 @@ import { createAttachment } from '@documenso/lib/server-only/envelope-attachment
|
||||
|
||||
import { authenticatedProcedure } from '../../trpc';
|
||||
import {
|
||||
createAttachmentMeta,
|
||||
ZCreateAttachmentRequestSchema,
|
||||
ZCreateAttachmentResponseSchema,
|
||||
createAttachmentMeta,
|
||||
} from './create-attachment.types';
|
||||
|
||||
export const createAttachmentRoute = authenticatedProcedure
|
||||
|
||||
@@ -3,9 +3,9 @@ import { deleteAttachment } from '@documenso/lib/server-only/envelope-attachment
|
||||
import { ZGenericSuccessResponse } from '../../schema';
|
||||
import { authenticatedProcedure } from '../../trpc';
|
||||
import {
|
||||
deleteAttachmentMeta,
|
||||
ZDeleteAttachmentRequestSchema,
|
||||
ZDeleteAttachmentResponseSchema,
|
||||
deleteAttachmentMeta,
|
||||
} from './delete-attachment.types';
|
||||
|
||||
export const deleteAttachmentRoute = authenticatedProcedure
|
||||
|
||||
@@ -4,9 +4,9 @@ import { findAttachmentsByToken } from '@documenso/lib/server-only/envelope-atta
|
||||
|
||||
import { maybeAuthenticatedProcedure } from '../../trpc';
|
||||
import {
|
||||
findAttachmentsMeta,
|
||||
ZFindAttachmentsRequestSchema,
|
||||
ZFindAttachmentsResponseSchema,
|
||||
findAttachmentsMeta,
|
||||
} from './find-attachments.types';
|
||||
|
||||
export const findAttachmentsRoute = maybeAuthenticatedProcedure
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
import { ZEnvelopeAttachmentTypeSchema } from '@documenso/lib/types/envelope-attachment';
|
||||
import { z } from 'zod';
|
||||
|
||||
import type { TrpcRouteMeta } from '../../trpc';
|
||||
|
||||
|
||||
@@ -3,9 +3,9 @@ import { updateAttachment } from '@documenso/lib/server-only/envelope-attachment
|
||||
import { ZGenericSuccessResponse } from '../../schema';
|
||||
import { authenticatedProcedure } from '../../trpc';
|
||||
import {
|
||||
updateAttachmentMeta,
|
||||
ZUpdateAttachmentRequestSchema,
|
||||
ZUpdateAttachmentResponseSchema,
|
||||
updateAttachmentMeta,
|
||||
} from './update-attachment.types';
|
||||
|
||||
export const updateAttachmentRoute = authenticatedProcedure
|
||||
|
||||
@@ -1,16 +1,12 @@
|
||||
import { EnvelopeType } from '@prisma/client';
|
||||
import pMap from 'p-map';
|
||||
|
||||
import { deleteDocument } from '@documenso/lib/server-only/document/delete-document';
|
||||
import { getMultipleEnvelopeWhereInput } from '@documenso/lib/server-only/envelope/get-envelopes-by-ids';
|
||||
import { deleteTemplate } from '@documenso/lib/server-only/template/delete-template';
|
||||
import { prisma } from '@documenso/prisma';
|
||||
import { EnvelopeType } from '@prisma/client';
|
||||
import pMap from 'p-map';
|
||||
|
||||
import { authenticatedProcedure } from '../trpc';
|
||||
import {
|
||||
ZBulkDeleteEnvelopesRequestSchema,
|
||||
ZBulkDeleteEnvelopesResponseSchema,
|
||||
} from './bulk-delete-envelopes.types';
|
||||
import { ZBulkDeleteEnvelopesRequestSchema, ZBulkDeleteEnvelopesResponseSchema } from './bulk-delete-envelopes.types';
|
||||
|
||||
export const bulkDeleteEnvelopesRoute = authenticatedProcedure
|
||||
// .meta(bulkDeleteEnvelopesMeta) // Keeping this as a private API for a little while until we're sure it's stable and the request/response schemas are finalized.
|
||||
|
||||
@@ -17,9 +17,7 @@ export const ZBulkDeleteEnvelopesRequestSchema = z.object({
|
||||
.array(z.string())
|
||||
.min(1)
|
||||
.max(100)
|
||||
.describe(
|
||||
'The IDs of the envelopes to delete. The maximum number of envelopes you can delete at once is 100.',
|
||||
),
|
||||
.describe('The IDs of the envelopes to delete. The maximum number of envelopes you can delete at once is 100.'),
|
||||
});
|
||||
|
||||
export const ZBulkDeleteEnvelopesResponseSchema = z.object({
|
||||
|
||||
@@ -5,10 +5,7 @@ import { buildTeamWhereQuery } from '@documenso/lib/utils/teams';
|
||||
import { prisma } from '@documenso/prisma';
|
||||
|
||||
import { authenticatedProcedure } from '../trpc';
|
||||
import {
|
||||
ZBulkMoveEnvelopesRequestSchema,
|
||||
ZBulkMoveEnvelopesResponseSchema,
|
||||
} from './bulk-move-envelopes.types';
|
||||
import { ZBulkMoveEnvelopesRequestSchema, ZBulkMoveEnvelopesResponseSchema } from './bulk-move-envelopes.types';
|
||||
|
||||
export const bulkMoveEnvelopesRoute = authenticatedProcedure
|
||||
// .meta(bulkMoveEnvelopesMeta)
|
||||
|
||||
@@ -18,16 +18,12 @@ export const ZBulkMoveEnvelopesRequestSchema = z.object({
|
||||
.array(z.string())
|
||||
.min(1)
|
||||
.max(100)
|
||||
.describe(
|
||||
'The IDs of the envelopes to move. The maximum number of envelopes you can move at once is 100.',
|
||||
),
|
||||
.describe('The IDs of the envelopes to move. The maximum number of envelopes you can move at once is 100.'),
|
||||
envelopeType: z.nativeEnum(EnvelopeType).describe('The type of the envelopes being moved.'),
|
||||
folderId: z
|
||||
.string()
|
||||
.nullable()
|
||||
.describe(
|
||||
'The ID of the folder to move the envelopes to. If null envelopes will be moved to the root folder.',
|
||||
),
|
||||
.describe('The ID of the folder to move the envelopes to. If null envelopes will be moved to the root folder.'),
|
||||
});
|
||||
|
||||
export const ZBulkMoveEnvelopesResponseSchema = z.object({
|
||||
|
||||
@@ -1,24 +1,14 @@
|
||||
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
|
||||
import { getEnvelopeWhereInput } from '@documenso/lib/server-only/envelope/get-envelope-by-id';
|
||||
import {
|
||||
convertPlaceholdersToFieldInputs,
|
||||
extractPdfPlaceholders,
|
||||
} from '@documenso/lib/server-only/pdf/auto-place-fields';
|
||||
import { findRecipientByPlaceholder } from '@documenso/lib/server-only/pdf/helpers';
|
||||
import { insertFormValuesInPdf } from '@documenso/lib/server-only/pdf/insert-form-values-in-pdf';
|
||||
import { normalizePdf } from '@documenso/lib/server-only/pdf/normalize-pdf';
|
||||
import { DOCUMENT_AUDIT_LOG_TYPE } from '@documenso/lib/types/document-audit-logs';
|
||||
import { prefixedId } from '@documenso/lib/universal/id';
|
||||
import { putPdfFileServerSide } from '@documenso/lib/universal/upload/put-file.server';
|
||||
import { createDocumentAuditLogData } from '@documenso/lib/utils/document-audit-logs';
|
||||
import { canEnvelopeItemsBeModified } from '@documenso/lib/utils/envelope';
|
||||
import { UNSAFE_createEnvelopeItems } from '@documenso/lib/server-only/envelope-item/create-envelope-items';
|
||||
import { getEnvelopeItemPermissions } from '@documenso/lib/utils/envelope';
|
||||
import { prisma } from '@documenso/prisma';
|
||||
|
||||
import { authenticatedProcedure } from '../trpc';
|
||||
import {
|
||||
createEnvelopeItemsMeta,
|
||||
ZCreateEnvelopeItemsRequestSchema,
|
||||
ZCreateEnvelopeItemsResponseSchema,
|
||||
createEnvelopeItemsMeta,
|
||||
} from './create-envelope-items.types';
|
||||
|
||||
export const createEnvelopeItemsRoute = authenticatedProcedure
|
||||
@@ -73,7 +63,9 @@ export const createEnvelopeItemsRoute = authenticatedProcedure
|
||||
});
|
||||
}
|
||||
|
||||
if (!canEnvelopeItemsBeModified(envelope, envelope.recipients)) {
|
||||
const { canFileBeChanged } = getEnvelopeItemPermissions(envelope, envelope.recipients);
|
||||
|
||||
if (!canFileBeChanged) {
|
||||
throw new AppError(AppErrorCode.INVALID_REQUEST, {
|
||||
message: 'Envelope item is not editable',
|
||||
});
|
||||
@@ -81,8 +73,7 @@ export const createEnvelopeItemsRoute = authenticatedProcedure
|
||||
|
||||
const organisationClaim = envelope.team.organisation.organisationClaim;
|
||||
|
||||
const remainingEnvelopeItems =
|
||||
organisationClaim.envelopeItemCount - envelope.envelopeItems.length - files.length;
|
||||
const remainingEnvelopeItems = organisationClaim.envelopeItemCount - envelope.envelopeItems.length - files.length;
|
||||
|
||||
if (remainingEnvelopeItems < 0) {
|
||||
throw new AppError('ENVELOPE_ITEM_LIMIT_EXCEEDED', {
|
||||
@@ -91,130 +82,17 @@ export const createEnvelopeItemsRoute = authenticatedProcedure
|
||||
});
|
||||
}
|
||||
|
||||
// For each file: normalize, extract & clean placeholders, then upload.
|
||||
const envelopeItems = await Promise.all(
|
||||
files.map(async (file) => {
|
||||
let buffer = Buffer.from(await file.arrayBuffer());
|
||||
|
||||
if (envelope.formValues) {
|
||||
buffer = await insertFormValuesInPdf({ pdf: buffer, formValues: envelope.formValues });
|
||||
}
|
||||
|
||||
const normalized = await normalizePdf(buffer, {
|
||||
flattenForm: envelope.type !== 'TEMPLATE',
|
||||
});
|
||||
|
||||
const { cleanedPdf, placeholders } = await extractPdfPlaceholders(normalized);
|
||||
|
||||
const { id: documentDataId } = await putPdfFileServerSide({
|
||||
name: file.name,
|
||||
type: 'application/pdf',
|
||||
arrayBuffer: async () => Promise.resolve(cleanedPdf),
|
||||
});
|
||||
|
||||
return {
|
||||
title: file.name,
|
||||
documentDataId,
|
||||
placeholders,
|
||||
};
|
||||
}),
|
||||
);
|
||||
|
||||
const currentHighestOrderValue =
|
||||
envelope.envelopeItems[envelope.envelopeItems.length - 1]?.order ?? 1;
|
||||
|
||||
const result = await prisma.$transaction(async (tx) => {
|
||||
const createdItems = await tx.envelopeItem.createManyAndReturn({
|
||||
data: envelopeItems.map((item) => ({
|
||||
id: prefixedId('envelope_item'),
|
||||
envelopeId,
|
||||
title: item.title,
|
||||
documentDataId: item.documentDataId,
|
||||
order: currentHighestOrderValue + 1,
|
||||
})),
|
||||
include: {
|
||||
documentData: true,
|
||||
},
|
||||
});
|
||||
|
||||
await tx.documentAuditLog.createMany({
|
||||
data: createdItems.map((item) =>
|
||||
createDocumentAuditLogData({
|
||||
type: DOCUMENT_AUDIT_LOG_TYPE.ENVELOPE_ITEM_CREATED,
|
||||
envelopeId: envelope.id,
|
||||
data: {
|
||||
envelopeItemId: item.id,
|
||||
envelopeItemTitle: item.title,
|
||||
},
|
||||
user: {
|
||||
name: user.name,
|
||||
email: user.email,
|
||||
},
|
||||
requestMetadata: metadata.requestMetadata,
|
||||
}),
|
||||
),
|
||||
});
|
||||
|
||||
// Create fields from placeholders if the envelope already has recipients.
|
||||
if (envelope.recipients.length > 0) {
|
||||
const orderedRecipients = [...envelope.recipients].sort((a, b) => {
|
||||
const aOrder = a.signingOrder ?? Number.MAX_SAFE_INTEGER;
|
||||
const bOrder = b.signingOrder ?? Number.MAX_SAFE_INTEGER;
|
||||
|
||||
if (aOrder !== bOrder) {
|
||||
return aOrder - bOrder;
|
||||
}
|
||||
|
||||
return a.id - b.id;
|
||||
});
|
||||
|
||||
for (const uploadedItem of envelopeItems) {
|
||||
if (!uploadedItem.placeholders || uploadedItem.placeholders.length === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const createdItem = createdItems.find(
|
||||
(ci) => ci.documentDataId === uploadedItem.documentDataId,
|
||||
);
|
||||
|
||||
if (!createdItem) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const fieldsToCreate = convertPlaceholdersToFieldInputs(
|
||||
uploadedItem.placeholders,
|
||||
(recipientPlaceholder, placeholder) =>
|
||||
findRecipientByPlaceholder(
|
||||
recipientPlaceholder,
|
||||
placeholder,
|
||||
orderedRecipients,
|
||||
orderedRecipients,
|
||||
),
|
||||
createdItem.id,
|
||||
);
|
||||
|
||||
if (fieldsToCreate.length > 0) {
|
||||
await tx.field.createMany({
|
||||
data: fieldsToCreate.map((field) => ({
|
||||
envelopeId: envelope.id,
|
||||
envelopeItemId: createdItem.id,
|
||||
recipientId: field.recipientId,
|
||||
type: field.type,
|
||||
page: field.page,
|
||||
positionX: field.positionX,
|
||||
positionY: field.positionY,
|
||||
width: field.width,
|
||||
height: field.height,
|
||||
customText: '',
|
||||
inserted: false,
|
||||
fieldMeta: field.fieldMeta || undefined,
|
||||
})),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return createdItems;
|
||||
const result = await UNSAFE_createEnvelopeItems({
|
||||
files: files.map((file) => ({
|
||||
file,
|
||||
})),
|
||||
envelope,
|
||||
user: {
|
||||
id: user.id,
|
||||
name: user.name,
|
||||
email: user.email,
|
||||
},
|
||||
apiRequestMetadata: metadata,
|
||||
});
|
||||
|
||||
return {
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import EnvelopeItemSchema from '@documenso/prisma/generated/zod/modelSchema/EnvelopeItemSchema';
|
||||
import { z } from 'zod';
|
||||
import { zfd } from 'zod-form-data';
|
||||
|
||||
import EnvelopeItemSchema from '@documenso/prisma/generated/zod/modelSchema/EnvelopeItemSchema';
|
||||
|
||||
import { zodFormData } from '../../utils/zod-form-data';
|
||||
import { zfdFile, zodFormData } from '../../utils/zod-form-data';
|
||||
import type { TrpcRouteMeta } from '../trpc';
|
||||
|
||||
export const createEnvelopeItemsMeta: TrpcRouteMeta = {
|
||||
@@ -24,7 +23,7 @@ export const ZCreateEnvelopeItemsPayloadSchema = z.object({
|
||||
|
||||
export const ZCreateEnvelopeItemsRequestSchema = zodFormData({
|
||||
payload: zfd.json(ZCreateEnvelopeItemsPayloadSchema),
|
||||
files: zfd.repeatableOfType(zfd.file()),
|
||||
files: zfd.repeatableOfType(zfdFile()),
|
||||
});
|
||||
|
||||
export const ZCreateEnvelopeItemsResponseSchema = z.object({
|
||||
@@ -33,6 +32,7 @@ export const ZCreateEnvelopeItemsResponseSchema = z.object({
|
||||
title: true,
|
||||
envelopeId: true,
|
||||
order: true,
|
||||
documentDataId: true,
|
||||
}).array(),
|
||||
});
|
||||
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
import { EnvelopeType } from '@prisma/client';
|
||||
|
||||
import { getServerLimits } from '@documenso/ee/server-only/limits/server';
|
||||
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
|
||||
import { createEnvelope } from '@documenso/lib/server-only/envelope/create-envelope';
|
||||
import { extractPdfPlaceholders } from '@documenso/lib/server-only/pdf/auto-place-fields';
|
||||
import { normalizePdf } from '@documenso/lib/server-only/pdf/normalize-pdf';
|
||||
import type { ApiRequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
|
||||
import { putPdfFileServerSide } from '@documenso/lib/universal/upload/put-file.server';
|
||||
import { EnvelopeType } from '@prisma/client';
|
||||
|
||||
import { insertFormValuesInPdf } from '../../../lib/server-only/pdf/insert-form-values-in-pdf';
|
||||
import { authenticatedProcedure } from '../trpc';
|
||||
import type { TCreateEnvelopeRequest } from './create-envelope.types';
|
||||
import {
|
||||
createEnvelopeMeta,
|
||||
ZCreateEnvelopeRequestSchema,
|
||||
ZCreateEnvelopeResponseSchema,
|
||||
createEnvelopeMeta,
|
||||
} from './create-envelope.types';
|
||||
|
||||
export const createEnvelopeRoute = authenticatedProcedure
|
||||
@@ -20,150 +21,181 @@ export const createEnvelopeRoute = authenticatedProcedure
|
||||
.input(ZCreateEnvelopeRequestSchema)
|
||||
.output(ZCreateEnvelopeResponseSchema)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
const { user, teamId } = ctx;
|
||||
ctx.logger.info({
|
||||
input: {
|
||||
folderId: input.payload.folderId,
|
||||
},
|
||||
});
|
||||
|
||||
const { payload, files } = input;
|
||||
return await createEnvelopeRouteCaller({
|
||||
userId: ctx.user.id,
|
||||
teamId: ctx.teamId,
|
||||
input,
|
||||
apiRequestMetadata: ctx.metadata,
|
||||
});
|
||||
});
|
||||
|
||||
const {
|
||||
title,
|
||||
type CreateEnvelopeRouteOptions = {
|
||||
/**
|
||||
* Verified user ID.
|
||||
*/
|
||||
userId: number;
|
||||
|
||||
/**
|
||||
* Unverified team ID.
|
||||
*/
|
||||
teamId: number;
|
||||
input: TCreateEnvelopeRequest;
|
||||
apiRequestMetadata: ApiRequestMetadata;
|
||||
|
||||
options?: {
|
||||
bypassDefaultRecipients?: boolean;
|
||||
};
|
||||
};
|
||||
|
||||
export const createEnvelopeRouteCaller = async ({
|
||||
userId,
|
||||
teamId,
|
||||
input,
|
||||
apiRequestMetadata,
|
||||
options = {},
|
||||
}: CreateEnvelopeRouteOptions) => {
|
||||
const { payload, files } = input;
|
||||
|
||||
const {
|
||||
title,
|
||||
type,
|
||||
externalId,
|
||||
visibility,
|
||||
globalAccessAuth,
|
||||
globalActionAuth,
|
||||
formValues,
|
||||
recipients,
|
||||
folderId,
|
||||
meta,
|
||||
attachments,
|
||||
delegatedDocumentOwner,
|
||||
} = payload;
|
||||
|
||||
const { remaining, maximumEnvelopeItemCount } = await getServerLimits({
|
||||
userId,
|
||||
teamId,
|
||||
});
|
||||
|
||||
if (remaining.documents <= 0) {
|
||||
throw new AppError(AppErrorCode.LIMIT_EXCEEDED, {
|
||||
message: 'You have reached your document limit for this month. Please upgrade your plan.',
|
||||
statusCode: 400,
|
||||
});
|
||||
}
|
||||
|
||||
if (files.length > maximumEnvelopeItemCount) {
|
||||
throw new AppError('ENVELOPE_ITEM_LIMIT_EXCEEDED', {
|
||||
message: `You cannot upload more than ${maximumEnvelopeItemCount} envelope items per envelope`,
|
||||
statusCode: 400,
|
||||
});
|
||||
}
|
||||
|
||||
if (files.some((file) => !file.type.startsWith('application/pdf'))) {
|
||||
throw new AppError('INVALID_DOCUMENT_FILE', {
|
||||
message: 'You cannot upload non-PDF files',
|
||||
statusCode: 400,
|
||||
});
|
||||
}
|
||||
|
||||
// For each file: normalize, extract & clean placeholders, then upload.
|
||||
const envelopeItems = await Promise.all(
|
||||
files.map(async (file) => {
|
||||
let pdf = Buffer.from(await file.arrayBuffer());
|
||||
|
||||
if (formValues) {
|
||||
// eslint-disable-next-line require-atomic-updates
|
||||
pdf = await insertFormValuesInPdf({
|
||||
pdf,
|
||||
formValues,
|
||||
});
|
||||
}
|
||||
|
||||
const normalized = await normalizePdf(pdf, {
|
||||
flattenForm: type !== EnvelopeType.TEMPLATE,
|
||||
});
|
||||
|
||||
// Todo: Embeds - Might need to add this for client-side embeds in the future.
|
||||
const { cleanedPdf, placeholders } = await extractPdfPlaceholders(normalized);
|
||||
|
||||
const { documentData } = await putPdfFileServerSide({
|
||||
name: file.name,
|
||||
type: 'application/pdf',
|
||||
arrayBuffer: async () => Promise.resolve(cleanedPdf),
|
||||
});
|
||||
|
||||
return {
|
||||
title: file.name,
|
||||
documentDataId: documentData.id,
|
||||
placeholders,
|
||||
};
|
||||
}),
|
||||
);
|
||||
|
||||
const recipientsToCreate = recipients?.map((recipient) => ({
|
||||
email: recipient.email,
|
||||
name: recipient.name,
|
||||
role: recipient.role,
|
||||
signingOrder: recipient.signingOrder,
|
||||
accessAuth: recipient.accessAuth,
|
||||
actionAuth: recipient.actionAuth,
|
||||
fields: recipient.fields?.map((field) => {
|
||||
let documentDataId: string | undefined;
|
||||
|
||||
if (typeof field.identifier === 'string') {
|
||||
documentDataId = envelopeItems.find((item) => item.title === field.identifier)?.documentDataId;
|
||||
}
|
||||
|
||||
if (typeof field.identifier === 'number') {
|
||||
documentDataId = envelopeItems.at(field.identifier)?.documentDataId;
|
||||
}
|
||||
|
||||
if (field.identifier === undefined) {
|
||||
documentDataId = envelopeItems.at(0)?.documentDataId;
|
||||
}
|
||||
|
||||
if (!documentDataId) {
|
||||
throw new AppError(AppErrorCode.NOT_FOUND, {
|
||||
message: 'Document data not found',
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
...field,
|
||||
documentDataId,
|
||||
};
|
||||
}),
|
||||
}));
|
||||
|
||||
const envelope = await createEnvelope({
|
||||
userId,
|
||||
teamId,
|
||||
internalVersion: 2,
|
||||
data: {
|
||||
type,
|
||||
title,
|
||||
externalId,
|
||||
formValues,
|
||||
visibility,
|
||||
globalAccessAuth,
|
||||
globalActionAuth,
|
||||
formValues,
|
||||
recipients,
|
||||
recipients: recipientsToCreate,
|
||||
folderId,
|
||||
meta,
|
||||
attachments,
|
||||
envelopeItems,
|
||||
delegatedDocumentOwner,
|
||||
} = payload;
|
||||
|
||||
ctx.logger.info({
|
||||
input: {
|
||||
folderId,
|
||||
},
|
||||
});
|
||||
|
||||
const { remaining, maximumEnvelopeItemCount } = await getServerLimits({
|
||||
userId: user.id,
|
||||
teamId,
|
||||
});
|
||||
|
||||
if (remaining.documents <= 0) {
|
||||
throw new AppError(AppErrorCode.LIMIT_EXCEEDED, {
|
||||
message: 'You have reached your document limit for this month. Please upgrade your plan.',
|
||||
statusCode: 400,
|
||||
});
|
||||
}
|
||||
|
||||
if (files.length > maximumEnvelopeItemCount) {
|
||||
throw new AppError('ENVELOPE_ITEM_LIMIT_EXCEEDED', {
|
||||
message: `You cannot upload more than ${maximumEnvelopeItemCount} envelope items per envelope`,
|
||||
statusCode: 400,
|
||||
});
|
||||
}
|
||||
|
||||
if (files.some((file) => !file.type.startsWith('application/pdf'))) {
|
||||
throw new AppError('INVALID_DOCUMENT_FILE', {
|
||||
message: 'You cannot upload non-PDF files',
|
||||
statusCode: 400,
|
||||
});
|
||||
}
|
||||
|
||||
// For each file: normalize, extract & clean placeholders, then upload.
|
||||
const envelopeItems = await Promise.all(
|
||||
files.map(async (file) => {
|
||||
let pdf = Buffer.from(await file.arrayBuffer());
|
||||
|
||||
if (formValues) {
|
||||
// eslint-disable-next-line require-atomic-updates
|
||||
pdf = await insertFormValuesInPdf({
|
||||
pdf,
|
||||
formValues,
|
||||
});
|
||||
}
|
||||
|
||||
const normalized = await normalizePdf(pdf, {
|
||||
flattenForm: type !== EnvelopeType.TEMPLATE,
|
||||
});
|
||||
|
||||
const { cleanedPdf, placeholders } = await extractPdfPlaceholders(normalized);
|
||||
|
||||
const { id: documentDataId } = await putPdfFileServerSide({
|
||||
name: file.name,
|
||||
type: 'application/pdf',
|
||||
arrayBuffer: async () => Promise.resolve(cleanedPdf),
|
||||
});
|
||||
|
||||
return {
|
||||
title: file.name,
|
||||
documentDataId,
|
||||
placeholders,
|
||||
};
|
||||
}),
|
||||
);
|
||||
|
||||
const recipientsToCreate = recipients?.map((recipient) => ({
|
||||
email: recipient.email,
|
||||
name: recipient.name,
|
||||
role: recipient.role,
|
||||
signingOrder: recipient.signingOrder,
|
||||
accessAuth: recipient.accessAuth,
|
||||
actionAuth: recipient.actionAuth,
|
||||
fields: recipient.fields?.map((field) => {
|
||||
let documentDataId: string | undefined = undefined;
|
||||
|
||||
if (typeof field.identifier === 'string') {
|
||||
documentDataId = envelopeItems.find(
|
||||
(item) => item.title === field.identifier,
|
||||
)?.documentDataId;
|
||||
}
|
||||
|
||||
if (typeof field.identifier === 'number') {
|
||||
documentDataId = envelopeItems.at(field.identifier)?.documentDataId;
|
||||
}
|
||||
|
||||
if (field.identifier === undefined) {
|
||||
documentDataId = envelopeItems.at(0)?.documentDataId;
|
||||
}
|
||||
|
||||
if (!documentDataId) {
|
||||
throw new AppError(AppErrorCode.NOT_FOUND, {
|
||||
message: 'Document data not found',
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
...field,
|
||||
documentDataId,
|
||||
};
|
||||
}),
|
||||
}));
|
||||
|
||||
const envelope = await createEnvelope({
|
||||
userId: user.id,
|
||||
teamId,
|
||||
internalVersion: 2,
|
||||
data: {
|
||||
type,
|
||||
title,
|
||||
externalId,
|
||||
formValues,
|
||||
visibility,
|
||||
globalAccessAuth,
|
||||
globalActionAuth,
|
||||
recipients: recipientsToCreate,
|
||||
folderId,
|
||||
envelopeItems,
|
||||
delegatedDocumentOwner,
|
||||
},
|
||||
attachments,
|
||||
meta,
|
||||
requestMetadata: ctx.metadata,
|
||||
});
|
||||
|
||||
return {
|
||||
id: envelope.id,
|
||||
};
|
||||
},
|
||||
attachments,
|
||||
meta,
|
||||
requestMetadata: apiRequestMetadata,
|
||||
bypassDefaultRecipients: options.bypassDefaultRecipients,
|
||||
});
|
||||
|
||||
return {
|
||||
id: envelope.id,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,11 +1,4 @@
|
||||
import { EnvelopeType } from '@prisma/client';
|
||||
import { z } from 'zod';
|
||||
import { zfd } from 'zod-form-data';
|
||||
|
||||
import {
|
||||
ZDocumentAccessAuthTypesSchema,
|
||||
ZDocumentActionAuthTypesSchema,
|
||||
} from '@documenso/lib/types/document-auth';
|
||||
import { ZDocumentAccessAuthTypesSchema, ZDocumentActionAuthTypesSchema } from '@documenso/lib/types/document-auth';
|
||||
import { ZDocumentFormValuesSchema } from '@documenso/lib/types/document-form-values';
|
||||
import { ZDocumentMetaCreateSchema } from '@documenso/lib/types/document-meta';
|
||||
import { ZEnvelopeAttachmentTypeSchema } from '@documenso/lib/types/envelope-attachment';
|
||||
@@ -17,13 +10,13 @@ import {
|
||||
ZFieldPageNumberSchema,
|
||||
} from '@documenso/lib/types/field';
|
||||
import { ZEnvelopeFieldAndMetaSchema } from '@documenso/lib/types/field-meta';
|
||||
import { zEmail } from '@documenso/lib/utils/zod';
|
||||
import { EnvelopeType } from '@prisma/client';
|
||||
import { z } from 'zod';
|
||||
import { zfd } from 'zod-form-data';
|
||||
|
||||
import { zodFormData } from '../../utils/zod-form-data';
|
||||
import {
|
||||
ZDocumentExternalIdSchema,
|
||||
ZDocumentTitleSchema,
|
||||
ZDocumentVisibilitySchema,
|
||||
} from '../document-router/schema';
|
||||
import { zfdFile, zodFormData } from '../../utils/zod-form-data';
|
||||
import { ZDocumentExternalIdSchema, ZDocumentTitleSchema, ZDocumentVisibilitySchema } from '../document-router/schema';
|
||||
import type { TrpcRouteMeta } from '../trpc';
|
||||
import { ZCreateEnvelopeRecipientSchema } from './envelope-recipients/create-envelope-recipients.types';
|
||||
|
||||
@@ -41,11 +34,7 @@ export const createEnvelopeMeta: TrpcRouteMeta = {
|
||||
export const ZCreateEnvelopePayloadSchema = z.object({
|
||||
title: ZDocumentTitleSchema,
|
||||
type: z.nativeEnum(EnvelopeType),
|
||||
delegatedDocumentOwner: z
|
||||
.string()
|
||||
.email()
|
||||
.describe('The email of the user who will own the document.')
|
||||
.optional(),
|
||||
delegatedDocumentOwner: zEmail().describe('The email of the user who will own the document.').optional(),
|
||||
externalId: ZDocumentExternalIdSchema.optional(),
|
||||
visibility: ZDocumentVisibilitySchema.optional(),
|
||||
globalAccessAuth: z.array(ZDocumentAccessAuthTypesSchema).optional(),
|
||||
@@ -64,9 +53,7 @@ export const ZCreateEnvelopePayloadSchema = z.object({
|
||||
z.object({
|
||||
identifier: z
|
||||
.union([z.string(), z.number()])
|
||||
.describe(
|
||||
'Either the filename or the index of the file that was uploaded to attach the field to.',
|
||||
)
|
||||
.describe('Either the filename or the index of the file that was uploaded to attach the field to.')
|
||||
.optional(),
|
||||
page: ZFieldPageNumberSchema,
|
||||
positionX: ZClampedFieldPositionXSchema,
|
||||
@@ -94,7 +81,7 @@ export const ZCreateEnvelopePayloadSchema = z.object({
|
||||
|
||||
export const ZCreateEnvelopeRequestSchema = zodFormData({
|
||||
payload: zfd.json(ZCreateEnvelopePayloadSchema),
|
||||
files: zfd.repeatableOfType(zfd.file()),
|
||||
files: zfd.repeatableOfType(zfdFile()),
|
||||
});
|
||||
|
||||
export const ZCreateEnvelopeResponseSchema = z.object({
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
|
||||
import { getEnvelopeWhereInput } from '@documenso/lib/server-only/envelope/get-envelope-by-id';
|
||||
import { DOCUMENT_AUDIT_LOG_TYPE } from '@documenso/lib/types/document-audit-logs';
|
||||
import { createDocumentAuditLogData } from '@documenso/lib/utils/document-audit-logs';
|
||||
import { canEnvelopeItemsBeModified } from '@documenso/lib/utils/envelope';
|
||||
import { UNSAFE_deleteEnvelopeItem } from '@documenso/lib/server-only/envelope-item/delete-envelope-item';
|
||||
import { getEnvelopeItemPermissions } from '@documenso/lib/utils/envelope';
|
||||
import { prisma } from '@documenso/prisma';
|
||||
|
||||
import { ZGenericSuccessResponse } from '../schema';
|
||||
import { authenticatedProcedure } from '../trpc';
|
||||
import {
|
||||
deleteEnvelopeItemMeta,
|
||||
ZDeleteEnvelopeItemRequestSchema,
|
||||
ZDeleteEnvelopeItemResponseSchema,
|
||||
deleteEnvelopeItemMeta,
|
||||
} from './delete-envelope-item.types';
|
||||
|
||||
export const deleteEnvelopeItemRoute = authenticatedProcedure
|
||||
@@ -51,55 +50,19 @@ export const deleteEnvelopeItemRoute = authenticatedProcedure
|
||||
});
|
||||
}
|
||||
|
||||
if (!canEnvelopeItemsBeModified(envelope, envelope.recipients)) {
|
||||
const { canFileBeChanged } = getEnvelopeItemPermissions(envelope, envelope.recipients);
|
||||
|
||||
if (!canFileBeChanged) {
|
||||
throw new AppError(AppErrorCode.INVALID_REQUEST, {
|
||||
message: 'Envelope item is not editable',
|
||||
});
|
||||
}
|
||||
|
||||
const result = await prisma.$transaction(async (tx) => {
|
||||
const deletedEnvelopeItem = await tx.envelopeItem.delete({
|
||||
where: {
|
||||
id: envelopeItemId,
|
||||
envelopeId: envelope.id,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
title: true,
|
||||
documentData: {
|
||||
select: {
|
||||
id: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await tx.documentAuditLog.create({
|
||||
data: createDocumentAuditLogData({
|
||||
type: DOCUMENT_AUDIT_LOG_TYPE.ENVELOPE_ITEM_DELETED,
|
||||
envelopeId: envelope.id,
|
||||
data: {
|
||||
envelopeItemId: deletedEnvelopeItem.id,
|
||||
envelopeItemTitle: deletedEnvelopeItem.title,
|
||||
},
|
||||
user: {
|
||||
name: user.name,
|
||||
email: user.email,
|
||||
},
|
||||
requestMetadata: metadata.requestMetadata,
|
||||
}),
|
||||
});
|
||||
|
||||
return deletedEnvelopeItem;
|
||||
});
|
||||
|
||||
await prisma.documentData.delete({
|
||||
where: {
|
||||
id: result.documentData.id,
|
||||
envelopeItem: {
|
||||
is: null,
|
||||
},
|
||||
},
|
||||
await UNSAFE_deleteEnvelopeItem({
|
||||
envelopeId,
|
||||
envelopeItemId,
|
||||
user,
|
||||
apiRequestMetadata: metadata,
|
||||
});
|
||||
|
||||
return ZGenericSuccessResponse;
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
import { EnvelopeType } from '@prisma/client';
|
||||
import { match } from 'ts-pattern';
|
||||
|
||||
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
|
||||
import { deleteDocument } from '@documenso/lib/server-only/document/delete-document';
|
||||
import { deleteTemplate } from '@documenso/lib/server-only/template/delete-template';
|
||||
import { prisma } from '@documenso/prisma';
|
||||
import { EnvelopeType } from '@prisma/client';
|
||||
import { match } from 'ts-pattern';
|
||||
|
||||
import { ZGenericSuccessResponse } from '../schema';
|
||||
import { authenticatedProcedure } from '../trpc';
|
||||
import {
|
||||
deleteEnvelopeMeta,
|
||||
ZDeleteEnvelopeRequestSchema,
|
||||
ZDeleteEnvelopeResponseSchema,
|
||||
deleteEnvelopeMeta,
|
||||
} from './delete-envelope.types';
|
||||
|
||||
export const deleteEnvelopeRoute = authenticatedProcedure
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { updateDocumentMeta } from '@documenso/lib/server-only/document-meta/upsert-document-meta';
|
||||
import { sendDocument } from '@documenso/lib/server-only/document/send-document';
|
||||
import { updateDocumentMeta } from '@documenso/lib/server-only/document-meta/upsert-document-meta';
|
||||
import { formatSigningLink } from '@documenso/lib/utils/recipients';
|
||||
|
||||
import { authenticatedProcedure } from '../trpc';
|
||||
import {
|
||||
distributeEnvelopeMeta,
|
||||
ZDistributeEnvelopeRequestSchema,
|
||||
ZDistributeEnvelopeResponseSchema,
|
||||
distributeEnvelopeMeta,
|
||||
} from './distribute-envelope.types';
|
||||
|
||||
export const distributeEnvelopeRoute = authenticatedProcedure
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
import { ZDocumentMetaUpdateSchema } from '@documenso/lib/types/document-meta';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { ZSuccessResponseSchema } from '../schema';
|
||||
import type { TrpcRouteMeta } from '../trpc';
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { authenticatedProcedure } from '../trpc';
|
||||
import {
|
||||
downloadEnvelopeItemMeta,
|
||||
ZDownloadEnvelopeItemRequestSchema,
|
||||
ZDownloadEnvelopeItemResponseSchema,
|
||||
downloadEnvelopeItemMeta,
|
||||
} from './download-envelope-item.types';
|
||||
|
||||
export const downloadEnvelopeItemRoute = authenticatedProcedure
|
||||
|
||||
@@ -18,9 +18,9 @@ export const downloadEnvelopeItemMeta: TrpcRouteMeta = {
|
||||
export const ZDownloadEnvelopeItemRequestSchema = z.object({
|
||||
envelopeItemId: z.string().describe('The ID of the envelope item to download.'),
|
||||
version: z
|
||||
.enum(['original', 'signed'])
|
||||
.enum(['original', 'signed', 'pending'])
|
||||
.describe(
|
||||
'The version of the envelope item to download. "signed" returns the completed document with signatures, "original" returns the original uploaded document.',
|
||||
'The version of the envelope item to download. "signed" returns the completed document with all signatures and the audit trail, "original" returns the original uploaded document, "pending" returns the original document with currently-inserted fields burned in (only valid while the envelope is in PENDING status; not a final executed document).',
|
||||
)
|
||||
.default('signed'),
|
||||
});
|
||||
|
||||
@@ -2,9 +2,9 @@ import { duplicateEnvelope } from '@documenso/lib/server-only/envelope/duplicate
|
||||
|
||||
import { authenticatedProcedure } from '../trpc';
|
||||
import {
|
||||
duplicateEnvelopeMeta,
|
||||
ZDuplicateEnvelopeRequestSchema,
|
||||
ZDuplicateEnvelopeResponseSchema,
|
||||
duplicateEnvelopeMeta,
|
||||
} from './duplicate-envelope.types';
|
||||
|
||||
export const duplicateEnvelopeRoute = authenticatedProcedure
|
||||
|
||||
@@ -2,9 +2,9 @@ import { createEnvelopeFields } from '@documenso/lib/server-only/field/create-en
|
||||
|
||||
import { authenticatedProcedure } from '../../trpc';
|
||||
import {
|
||||
createEnvelopeFieldsMeta,
|
||||
ZCreateEnvelopeFieldsRequestSchema,
|
||||
ZCreateEnvelopeFieldsResponseSchema,
|
||||
createEnvelopeFieldsMeta,
|
||||
} from './create-envelope-fields.types';
|
||||
|
||||
export const createEnvelopeFieldsRoute = authenticatedProcedure
|
||||
|
||||
+7
-7
@@ -1,5 +1,3 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
import {
|
||||
ZClampedFieldHeightSchema,
|
||||
ZClampedFieldPositionXSchema,
|
||||
@@ -9,6 +7,7 @@ import {
|
||||
ZFieldSchema,
|
||||
} from '@documenso/lib/types/field';
|
||||
import { ZEnvelopeFieldAndMetaSchema } from '@documenso/lib/types/field-meta';
|
||||
import { z } from 'zod';
|
||||
|
||||
import type { TrpcRouteMeta } from '../../trpc';
|
||||
|
||||
@@ -37,7 +36,7 @@ const ZCreateFieldBaseSchema = ZEnvelopeFieldAndMetaSchema.and(
|
||||
/**
|
||||
* Position a field using explicit percentage-based coordinates.
|
||||
*/
|
||||
const ZCoordinatePositionSchema = z.object({
|
||||
export const ZCoordinatePositionSchema = z.object({
|
||||
page: ZFieldPageNumberSchema,
|
||||
positionX: ZClampedFieldPositionXSchema,
|
||||
positionY: ZClampedFieldPositionYSchema,
|
||||
@@ -52,7 +51,7 @@ const ZCoordinatePositionSchema = z.object({
|
||||
* placed at the bounding box of that match. Width and height can optionally be
|
||||
* overridden; when omitted the dimensions of the placeholder text are used.
|
||||
*/
|
||||
const ZPlaceholderPositionSchema = z.object({
|
||||
export const ZPlaceholderPositionSchema = z.object({
|
||||
placeholder: z
|
||||
.string()
|
||||
.describe(
|
||||
@@ -72,9 +71,10 @@ const ZPlaceholderPositionSchema = z.object({
|
||||
),
|
||||
});
|
||||
|
||||
const ZCreateFieldSchema = ZCreateFieldBaseSchema.and(
|
||||
z.union([ZCoordinatePositionSchema, ZPlaceholderPositionSchema]),
|
||||
);
|
||||
const ZCreateFieldSchema = z.union([
|
||||
ZCreateFieldBaseSchema.and(ZCoordinatePositionSchema),
|
||||
ZCreateFieldBaseSchema.and(ZPlaceholderPositionSchema),
|
||||
]);
|
||||
|
||||
export const ZCreateEnvelopeFieldsRequestSchema = z.object({
|
||||
envelopeId: z.string(),
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
import { EnvelopeType } from '@prisma/client';
|
||||
|
||||
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
|
||||
import { getEnvelopeWhereInput } from '@documenso/lib/server-only/envelope/get-envelope-by-id';
|
||||
import { DOCUMENT_AUDIT_LOG_TYPE } from '@documenso/lib/types/document-audit-logs';
|
||||
import { createDocumentAuditLogData } from '@documenso/lib/utils/document-audit-logs';
|
||||
import { canRecipientFieldsBeModified } from '@documenso/lib/utils/recipients';
|
||||
import { prisma } from '@documenso/prisma';
|
||||
import { EnvelopeType } from '@prisma/client';
|
||||
|
||||
import { ZGenericSuccessResponse } from '../../schema';
|
||||
import { authenticatedProcedure } from '../../trpc';
|
||||
import {
|
||||
deleteEnvelopeFieldMeta,
|
||||
ZDeleteEnvelopeFieldRequestSchema,
|
||||
ZDeleteEnvelopeFieldResponseSchema,
|
||||
deleteEnvelopeFieldMeta,
|
||||
} from './delete-envelope-field.types';
|
||||
|
||||
export const deleteEnvelopeFieldRoute = authenticatedProcedure
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
|
||||
import { getEnvelopeWhereInput } from '@documenso/lib/server-only/envelope/get-envelope-by-id';
|
||||
import { prisma } from '@documenso/prisma';
|
||||
import { FieldType } from '@prisma/client';
|
||||
|
||||
import { authenticatedProcedure } from '../../trpc';
|
||||
import {
|
||||
ZGetEnvelopeFieldSignaturesRequestSchema,
|
||||
ZGetEnvelopeFieldSignaturesResponseSchema,
|
||||
} from './get-envelope-field-signatures.types';
|
||||
|
||||
export const getEnvelopeFieldSignaturesRoute = authenticatedProcedure
|
||||
.input(ZGetEnvelopeFieldSignaturesRequestSchema)
|
||||
.output(ZGetEnvelopeFieldSignaturesResponseSchema)
|
||||
.query(async ({ input, ctx }) => {
|
||||
const { teamId, user } = ctx;
|
||||
const { envelopeId } = input;
|
||||
|
||||
ctx.logger.info({
|
||||
input: {
|
||||
envelopeId,
|
||||
},
|
||||
});
|
||||
|
||||
// Validate the user has access to the envelope.
|
||||
const { envelopeWhereInput } = await getEnvelopeWhereInput({
|
||||
id: {
|
||||
type: 'envelopeId',
|
||||
id: envelopeId,
|
||||
},
|
||||
type: null,
|
||||
userId: user.id,
|
||||
teamId,
|
||||
});
|
||||
|
||||
const envelope = await prisma.envelope.findFirst({
|
||||
where: envelopeWhereInput,
|
||||
include: {
|
||||
fields: {
|
||||
where: {
|
||||
inserted: true,
|
||||
type: FieldType.SIGNATURE,
|
||||
},
|
||||
include: {
|
||||
signature: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (!envelope) {
|
||||
throw new AppError(AppErrorCode.NOT_FOUND, {
|
||||
message: 'Envelope not found',
|
||||
});
|
||||
}
|
||||
|
||||
const signatures = envelope.fields.map((field) => ({
|
||||
fieldId: field.id,
|
||||
signatureImageAsBase64: field.signature?.signatureImageAsBase64 ?? null,
|
||||
typedSignature: field.signature?.typedSignature ?? null,
|
||||
}));
|
||||
|
||||
return signatures;
|
||||
});
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
export const ZGetEnvelopeFieldSignaturesRequestSchema = z.object({
|
||||
envelopeId: z.string().min(1),
|
||||
});
|
||||
|
||||
export const ZGetEnvelopeFieldSignaturesResponseSchema = z
|
||||
.object({
|
||||
fieldId: z.number(),
|
||||
signatureImageAsBase64: z.string().nullable(),
|
||||
typedSignature: z.string().nullable(),
|
||||
})
|
||||
.array();
|
||||
|
||||
export type TGetEnvelopeFieldSignaturesRequest = z.infer<typeof ZGetEnvelopeFieldSignaturesRequestSchema>;
|
||||
export type TGetEnvelopeFieldSignaturesResponse = z.infer<typeof ZGetEnvelopeFieldSignaturesResponseSchema>;
|
||||
@@ -2,9 +2,9 @@ import { getFieldById } from '@documenso/lib/server-only/field/get-field-by-id';
|
||||
|
||||
import { authenticatedProcedure } from '../../trpc';
|
||||
import {
|
||||
getEnvelopeFieldMeta,
|
||||
ZGetEnvelopeFieldRequestSchema,
|
||||
ZGetEnvelopeFieldResponseSchema,
|
||||
getEnvelopeFieldMeta,
|
||||
} from './get-envelope-field.types';
|
||||
|
||||
export const getEnvelopeFieldRoute = authenticatedProcedure
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
import { ZEnvelopeFieldSchema } from '@documenso/lib/types/field';
|
||||
import { z } from 'zod';
|
||||
|
||||
import type { TrpcRouteMeta } from '../../trpc';
|
||||
|
||||
|
||||
@@ -2,9 +2,9 @@ import { updateEnvelopeFields } from '@documenso/lib/server-only/field/update-en
|
||||
|
||||
import { authenticatedProcedure } from '../../trpc';
|
||||
import {
|
||||
updateEnvelopeFieldsMeta,
|
||||
ZUpdateEnvelopeFieldsRequestSchema,
|
||||
ZUpdateEnvelopeFieldsResponseSchema,
|
||||
updateEnvelopeFieldsMeta,
|
||||
} from './update-envelope-fields.types';
|
||||
|
||||
export const updateEnvelopeFieldsRoute = authenticatedProcedure
|
||||
|
||||
+13
-16
@@ -1,16 +1,9 @@
|
||||
import { ZFieldSchema } from '@documenso/lib/types/field';
|
||||
import { ZEnvelopeFieldAndMetaSchema } from '@documenso/lib/types/field-meta';
|
||||
import { z } from 'zod';
|
||||
|
||||
import {
|
||||
ZClampedFieldHeightSchema,
|
||||
ZClampedFieldPositionXSchema,
|
||||
ZClampedFieldPositionYSchema,
|
||||
ZClampedFieldWidthSchema,
|
||||
ZFieldPageNumberSchema,
|
||||
ZFieldSchema,
|
||||
} from '@documenso/lib/types/field';
|
||||
import { ZEnvelopeFieldAndMetaSchema } from '@documenso/lib/types/field-meta';
|
||||
|
||||
import type { TrpcRouteMeta } from '../../trpc';
|
||||
import { ZCoordinatePositionSchema } from './create-envelope-fields.types';
|
||||
|
||||
export const updateEnvelopeFieldsMeta: TrpcRouteMeta = {
|
||||
openapi: {
|
||||
@@ -22,7 +15,7 @@ export const updateEnvelopeFieldsMeta: TrpcRouteMeta = {
|
||||
},
|
||||
};
|
||||
|
||||
const ZUpdateFieldSchema = ZEnvelopeFieldAndMetaSchema.and(
|
||||
const ZUpdateFieldBaseSchema = ZEnvelopeFieldAndMetaSchema.and(
|
||||
z.object({
|
||||
id: z.number().describe('The ID of the field to update.'),
|
||||
envelopeItemId: z
|
||||
@@ -31,14 +24,18 @@ const ZUpdateFieldSchema = ZEnvelopeFieldAndMetaSchema.and(
|
||||
.describe(
|
||||
'The ID of the envelope item to put the field on. If not provided, field will be placed on the first item.',
|
||||
),
|
||||
page: ZFieldPageNumberSchema.optional(),
|
||||
positionX: ZClampedFieldPositionXSchema.optional(),
|
||||
positionY: ZClampedFieldPositionYSchema.optional(),
|
||||
width: ZClampedFieldWidthSchema.optional(),
|
||||
height: ZClampedFieldHeightSchema.optional(),
|
||||
}),
|
||||
);
|
||||
|
||||
// !: Later on we should support placeholders for updates, but since we didn't do the
|
||||
// !: neccesary plumbing for updates in the initial release, we can hold off for now.
|
||||
// const ZUpdateFieldSchema = z.union([
|
||||
// ZUpdateFieldBaseSchema.and(ZCoordinatePositionSchema),
|
||||
// ZUpdateFieldBaseSchema.and(ZPlaceholderPositionSchema),
|
||||
// ]);
|
||||
|
||||
const ZUpdateFieldSchema = ZUpdateFieldBaseSchema.and(ZCoordinatePositionSchema.partial());
|
||||
|
||||
export const ZUpdateEnvelopeFieldsRequestSchema = z.object({
|
||||
envelopeId: z.string(),
|
||||
data: ZUpdateFieldSchema.array(),
|
||||
|
||||
+1
-1
@@ -2,9 +2,9 @@ import { createEnvelopeRecipients } from '@documenso/lib/server-only/recipient/c
|
||||
|
||||
import { authenticatedProcedure } from '../../trpc';
|
||||
import {
|
||||
createEnvelopeRecipientsMeta,
|
||||
ZCreateEnvelopeRecipientsRequestSchema,
|
||||
ZCreateEnvelopeRecipientsResponseSchema,
|
||||
createEnvelopeRecipientsMeta,
|
||||
} from './create-envelope-recipients.types';
|
||||
|
||||
export const createEnvelopeRecipientsRoute = authenticatedProcedure
|
||||
|
||||
+4
-15
@@ -1,15 +1,8 @@
|
||||
import { ZRecipientAccessAuthTypesSchema, ZRecipientActionAuthTypesSchema } from '@documenso/lib/types/document-auth';
|
||||
import { ZEnvelopeRecipientLiteSchema, ZRecipientEmailSchema } from '@documenso/lib/types/recipient';
|
||||
import { RecipientRole } from '@prisma/client';
|
||||
import { z } from 'zod';
|
||||
|
||||
import {
|
||||
ZRecipientAccessAuthTypesSchema,
|
||||
ZRecipientActionAuthTypesSchema,
|
||||
} from '@documenso/lib/types/document-auth';
|
||||
import {
|
||||
ZEnvelopeRecipientLiteSchema,
|
||||
ZRecipientEmailSchema,
|
||||
} from '@documenso/lib/types/recipient';
|
||||
|
||||
import type { TrpcRouteMeta } from '../../trpc';
|
||||
|
||||
export const createEnvelopeRecipientsMeta: TrpcRouteMeta = {
|
||||
@@ -40,9 +33,5 @@ export const ZCreateEnvelopeRecipientsResponseSchema = z.object({
|
||||
data: ZEnvelopeRecipientLiteSchema.array(),
|
||||
});
|
||||
|
||||
export type TCreateEnvelopeRecipientsRequest = z.infer<
|
||||
typeof ZCreateEnvelopeRecipientsRequestSchema
|
||||
>;
|
||||
export type TCreateEnvelopeRecipientsResponse = z.infer<
|
||||
typeof ZCreateEnvelopeRecipientsResponseSchema
|
||||
>;
|
||||
export type TCreateEnvelopeRecipientsRequest = z.infer<typeof ZCreateEnvelopeRecipientsRequestSchema>;
|
||||
export type TCreateEnvelopeRecipientsResponse = z.infer<typeof ZCreateEnvelopeRecipientsResponseSchema>;
|
||||
|
||||
+1
-1
@@ -3,9 +3,9 @@ import { deleteEnvelopeRecipient } from '@documenso/lib/server-only/recipient/de
|
||||
import { ZGenericSuccessResponse } from '../../schema';
|
||||
import { authenticatedProcedure } from '../../trpc';
|
||||
import {
|
||||
deleteEnvelopeRecipientMeta,
|
||||
ZDeleteEnvelopeRecipientRequestSchema,
|
||||
ZDeleteEnvelopeRecipientResponseSchema,
|
||||
deleteEnvelopeRecipientMeta,
|
||||
} from './delete-envelope-recipient.types';
|
||||
|
||||
export const deleteEnvelopeRecipientRoute = authenticatedProcedure
|
||||
|
||||
+1
-3
@@ -20,6 +20,4 @@ export const ZDeleteEnvelopeRecipientRequestSchema = z.object({
|
||||
export const ZDeleteEnvelopeRecipientResponseSchema = ZSuccessResponseSchema;
|
||||
|
||||
export type TDeleteEnvelopeRecipientRequest = z.infer<typeof ZDeleteEnvelopeRecipientRequestSchema>;
|
||||
export type TDeleteEnvelopeRecipientResponse = z.infer<
|
||||
typeof ZDeleteEnvelopeRecipientResponseSchema
|
||||
>;
|
||||
export type TDeleteEnvelopeRecipientResponse = z.infer<typeof ZDeleteEnvelopeRecipientResponseSchema>;
|
||||
|
||||
@@ -4,9 +4,9 @@ import { prisma } from '@documenso/prisma';
|
||||
|
||||
import { authenticatedProcedure } from '../../trpc';
|
||||
import {
|
||||
getEnvelopeRecipientMeta,
|
||||
ZGetEnvelopeRecipientRequestSchema,
|
||||
ZGetEnvelopeRecipientResponseSchema,
|
||||
getEnvelopeRecipientMeta,
|
||||
} from './get-envelope-recipient.types';
|
||||
|
||||
export const getEnvelopeRecipientRoute = authenticatedProcedure
|
||||
|
||||
+1
-2
@@ -1,6 +1,5 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
import { ZEnvelopeRecipientSchema } from '@documenso/lib/types/recipient';
|
||||
import { z } from 'zod';
|
||||
|
||||
import type { TrpcRouteMeta } from '../../trpc';
|
||||
|
||||
|
||||
+1
-1
@@ -2,9 +2,9 @@ import { updateEnvelopeRecipients } from '@documenso/lib/server-only/recipient/u
|
||||
|
||||
import { authenticatedProcedure } from '../../trpc';
|
||||
import {
|
||||
updateEnvelopeRecipientsMeta,
|
||||
ZUpdateEnvelopeRecipientsRequestSchema,
|
||||
ZUpdateEnvelopeRecipientsResponseSchema,
|
||||
updateEnvelopeRecipientsMeta,
|
||||
} from './update-envelope-recipients.types';
|
||||
|
||||
export const updateEnvelopeRecipientsRoute = authenticatedProcedure
|
||||
|
||||
+4
-12
@@ -1,12 +1,8 @@
|
||||
import { ZRecipientAccessAuthTypesSchema, ZRecipientActionAuthTypesSchema } from '@documenso/lib/types/document-auth';
|
||||
import { ZRecipientEmailSchema, ZRecipientLiteSchema } from '@documenso/lib/types/recipient';
|
||||
import { RecipientRole } from '@prisma/client';
|
||||
import { z } from 'zod';
|
||||
|
||||
import {
|
||||
ZRecipientAccessAuthTypesSchema,
|
||||
ZRecipientActionAuthTypesSchema,
|
||||
} from '@documenso/lib/types/document-auth';
|
||||
import { ZRecipientEmailSchema, ZRecipientLiteSchema } from '@documenso/lib/types/recipient';
|
||||
|
||||
import type { TrpcRouteMeta } from '../../trpc';
|
||||
|
||||
export const updateEnvelopeRecipientsMeta: TrpcRouteMeta = {
|
||||
@@ -38,9 +34,5 @@ export const ZUpdateEnvelopeRecipientsResponseSchema = z.object({
|
||||
data: ZRecipientLiteSchema.array(),
|
||||
});
|
||||
|
||||
export type TUpdateEnvelopeRecipientsRequest = z.infer<
|
||||
typeof ZUpdateEnvelopeRecipientsRequestSchema
|
||||
>;
|
||||
export type TUpdateEnvelopeRecipientsResponse = z.infer<
|
||||
typeof ZUpdateEnvelopeRecipientsResponseSchema
|
||||
>;
|
||||
export type TUpdateEnvelopeRecipientsRequest = z.infer<typeof ZUpdateEnvelopeRecipientsRequestSchema>;
|
||||
export type TUpdateEnvelopeRecipientsResponse = z.infer<typeof ZUpdateEnvelopeRecipientsResponseSchema>;
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
import { EnvelopeType } from '@prisma/client';
|
||||
|
||||
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
|
||||
import { getEnvelopeWhereInput } from '@documenso/lib/server-only/envelope/get-envelope-by-id';
|
||||
import type { FindResultResponse } from '@documenso/lib/types/search-params';
|
||||
import { parseDocumentAuditLogData } from '@documenso/lib/utils/document-audit-logs';
|
||||
import { prisma } from '@documenso/prisma';
|
||||
import { EnvelopeType } from '@prisma/client';
|
||||
|
||||
import { authenticatedProcedure } from '../trpc';
|
||||
import {
|
||||
findEnvelopeAuditLogsMeta,
|
||||
ZFindEnvelopeAuditLogsRequestSchema,
|
||||
ZFindEnvelopeAuditLogsResponseSchema,
|
||||
findEnvelopeAuditLogsMeta,
|
||||
} from './find-envelope-audit-logs.types';
|
||||
|
||||
export const findEnvelopeAuditLogsRoute = authenticatedProcedure
|
||||
@@ -18,13 +17,7 @@ export const findEnvelopeAuditLogsRoute = authenticatedProcedure
|
||||
.input(ZFindEnvelopeAuditLogsRequestSchema)
|
||||
.output(ZFindEnvelopeAuditLogsResponseSchema)
|
||||
.query(async ({ input, ctx }) => {
|
||||
const {
|
||||
envelopeId,
|
||||
page = 1,
|
||||
perPage = 50,
|
||||
orderByColumn = 'createdAt',
|
||||
orderByDirection = 'desc',
|
||||
} = input;
|
||||
const { envelopeId, page = 1, perPage = 50, orderByColumn = 'createdAt', orderByDirection = 'desc' } = input;
|
||||
|
||||
ctx.logger.info({
|
||||
input: {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
import { ZDocumentAuditLogSchema } from '@documenso/lib/types/document-audit-logs';
|
||||
import { ZFindResultResponse, ZFindSearchParamsSchema } from '@documenso/lib/types/search-params';
|
||||
import { z } from 'zod';
|
||||
|
||||
import type { TrpcRouteMeta } from '../trpc';
|
||||
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
import { findEnvelopes } from '@documenso/lib/server-only/envelope/find-envelopes';
|
||||
|
||||
import { authenticatedProcedure } from '../trpc';
|
||||
import {
|
||||
ZFindEnvelopesRequestSchema,
|
||||
ZFindEnvelopesResponseSchema,
|
||||
findEnvelopesMeta,
|
||||
} from './find-envelopes.types';
|
||||
import { findEnvelopesMeta, ZFindEnvelopesRequestSchema, ZFindEnvelopesResponseSchema } from './find-envelopes.types';
|
||||
|
||||
export const findEnvelopesRoute = authenticatedProcedure
|
||||
.meta(findEnvelopesMeta)
|
||||
@@ -14,18 +10,7 @@ export const findEnvelopesRoute = authenticatedProcedure
|
||||
.query(async ({ input, ctx }) => {
|
||||
const { user, teamId } = ctx;
|
||||
|
||||
const {
|
||||
query,
|
||||
type,
|
||||
templateId,
|
||||
page,
|
||||
perPage,
|
||||
orderByDirection,
|
||||
orderByColumn,
|
||||
source,
|
||||
status,
|
||||
folderId,
|
||||
} = input;
|
||||
const { query, type, templateId, page, perPage, orderByDirection, orderByColumn, source, status, folderId } = input;
|
||||
|
||||
ctx.logger.info({
|
||||
input: {
|
||||
@@ -52,5 +37,6 @@ export const findEnvelopesRoute = authenticatedProcedure
|
||||
perPage,
|
||||
folderId,
|
||||
orderBy: orderByColumn ? { column: orderByColumn, direction: orderByDirection } : undefined,
|
||||
useWindowedCount: false,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { DocumentSource, DocumentStatus, EnvelopeType } from '@prisma/client';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { ZEnvelopeManySchema } from '@documenso/lib/types/envelope';
|
||||
import { ZFindResultResponse, ZFindSearchParamsSchema } from '@documenso/lib/types/search-params';
|
||||
import { DocumentSource, DocumentStatus, EnvelopeType } from '@prisma/client';
|
||||
import { z } from 'zod';
|
||||
|
||||
import type { TrpcRouteMeta } from '../trpc';
|
||||
|
||||
@@ -17,22 +16,10 @@ export const findEnvelopesMeta: TrpcRouteMeta = {
|
||||
};
|
||||
|
||||
export const ZFindEnvelopesRequestSchema = ZFindSearchParamsSchema.extend({
|
||||
type: z
|
||||
.nativeEnum(EnvelopeType)
|
||||
.describe('Filter envelopes by type (DOCUMENT or TEMPLATE).')
|
||||
.optional(),
|
||||
templateId: z
|
||||
.number()
|
||||
.describe('Filter envelopes by the template ID used to create it.')
|
||||
.optional(),
|
||||
source: z
|
||||
.nativeEnum(DocumentSource)
|
||||
.describe('Filter envelopes by how it was created.')
|
||||
.optional(),
|
||||
status: z
|
||||
.nativeEnum(DocumentStatus)
|
||||
.describe('Filter envelopes by the current status.')
|
||||
.optional(),
|
||||
type: z.nativeEnum(EnvelopeType).describe('Filter envelopes by type (DOCUMENT or TEMPLATE).').optional(),
|
||||
templateId: z.number().describe('Filter envelopes by the template ID used to create it.').optional(),
|
||||
source: z.nativeEnum(DocumentSource).describe('Filter envelopes by how it was created.').optional(),
|
||||
status: z.nativeEnum(DocumentStatus).describe('Filter envelopes by the current status.').optional(),
|
||||
folderId: z.string().describe('Filter envelopes by folder ID.').optional(),
|
||||
orderByColumn: z.enum(['createdAt']).optional(),
|
||||
orderByDirection: z.enum(['asc', 'desc']).describe('Sort direction.').default('desc'),
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
import { getEditorEnvelopeById } from '@documenso/lib/server-only/envelope/get-editor-envelope-by-id';
|
||||
|
||||
import { authenticatedProcedure } from '../trpc';
|
||||
import { ZGetEditorEnvelopeRequestSchema, ZGetEditorEnvelopeResponseSchema } from './get-editor-envelope.types';
|
||||
|
||||
export const getEditorEnvelopeRoute = authenticatedProcedure
|
||||
.input(ZGetEditorEnvelopeRequestSchema)
|
||||
.output(ZGetEditorEnvelopeResponseSchema)
|
||||
.query(async ({ input, ctx }) => {
|
||||
const { teamId, user } = ctx;
|
||||
const { envelopeId } = input;
|
||||
|
||||
ctx.logger.info({
|
||||
input: {
|
||||
envelopeId,
|
||||
},
|
||||
});
|
||||
|
||||
return await getEditorEnvelopeById({
|
||||
userId: user.id,
|
||||
teamId,
|
||||
id: {
|
||||
type: 'envelopeId',
|
||||
id: envelopeId,
|
||||
},
|
||||
type: null,
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,11 @@
|
||||
import { ZEditorEnvelopeSchema } from '@documenso/lib/types/envelope-editor';
|
||||
import { z } from 'zod';
|
||||
|
||||
export const ZGetEditorEnvelopeRequestSchema = z.object({
|
||||
envelopeId: z.string(),
|
||||
});
|
||||
|
||||
export const ZGetEditorEnvelopeResponseSchema = ZEditorEnvelopeSchema;
|
||||
|
||||
export type TGetEditorEnvelopeRequest = z.infer<typeof ZGetEditorEnvelopeRequestSchema>;
|
||||
export type TGetEditorEnvelopeResponse = z.infer<typeof ZGetEditorEnvelopeResponseSchema>;
|
||||
@@ -1,8 +1,8 @@
|
||||
import { EnvelopeType } from '@prisma/client';
|
||||
|
||||
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
|
||||
import { getEnvelopeWhereInput } from '@documenso/lib/server-only/envelope/get-envelope-by-id';
|
||||
import { getOrganisationTemplateWhereInput } from '@documenso/lib/server-only/template/get-organisation-template-by-id';
|
||||
import { prisma } from '@documenso/prisma';
|
||||
import { EnvelopeType } from '@prisma/client';
|
||||
|
||||
import { maybeAuthenticatedProcedure } from '../trpc';
|
||||
import {
|
||||
@@ -55,13 +55,7 @@ export const getEnvelopeItemsByTokenRoute = maybeAuthenticatedProcedure
|
||||
};
|
||||
});
|
||||
|
||||
const handleGetEnvelopeItemsByToken = async ({
|
||||
envelopeId,
|
||||
token,
|
||||
}: {
|
||||
envelopeId: string;
|
||||
token: string;
|
||||
}) => {
|
||||
const handleGetEnvelopeItemsByToken = async ({ envelopeId, token }: { envelopeId: string; token: string }) => {
|
||||
const envelope = await prisma.envelope.findFirst({
|
||||
where: {
|
||||
id: envelopeId,
|
||||
@@ -101,7 +95,7 @@ const handleGetEnvelopeItemsByUser = async ({
|
||||
userId: number;
|
||||
teamId: number;
|
||||
}) => {
|
||||
const { envelopeWhereInput } = await getEnvelopeWhereInput({
|
||||
const { envelopeWhereInput, team: callerTeam } = await getEnvelopeWhereInput({
|
||||
id: {
|
||||
type: 'envelopeId',
|
||||
id: envelopeId,
|
||||
@@ -111,7 +105,8 @@ const handleGetEnvelopeItemsByUser = async ({
|
||||
teamId,
|
||||
});
|
||||
|
||||
const envelope = await prisma.envelope.findUnique({
|
||||
// Try the standard team-scoped access path first (owner / current team / team email).
|
||||
let envelope = await prisma.envelope.findUnique({
|
||||
where: envelopeWhereInput,
|
||||
include: {
|
||||
envelopeItems: {
|
||||
@@ -122,6 +117,28 @@ const handleGetEnvelopeItemsByUser = async ({
|
||||
},
|
||||
});
|
||||
|
||||
// Fallback: if the envelope is an ORGANISATION template owned by a sibling team
|
||||
// in the caller's organisation, allow read access to the items metadata.
|
||||
// Mirrors the access logic used by `createDocumentFromTemplate` and the
|
||||
// file-download endpoint's `checkEnvelopeFileAccess` so this route stays in
|
||||
// sync with where actual file access is granted.
|
||||
if (!envelope) {
|
||||
envelope = await prisma.envelope.findFirst({
|
||||
where: getOrganisationTemplateWhereInput({
|
||||
id: { type: 'envelopeId', id: envelopeId },
|
||||
organisationId: callerTeam.organisationId,
|
||||
teamRole: callerTeam.currentTeamRole,
|
||||
}),
|
||||
include: {
|
||||
envelopeItems: {
|
||||
include: {
|
||||
documentData: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (!envelope) {
|
||||
throw new AppError(AppErrorCode.NOT_FOUND, {
|
||||
message: 'Envelope could not be found',
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
import EnvelopeItemSchema from '@documenso/prisma/generated/zod/modelSchema/EnvelopeItemSchema';
|
||||
import { z } from 'zod';
|
||||
|
||||
export const ZGetEnvelopeItemsByTokenRequestSchema = z.object({
|
||||
envelopeId: z.string(),
|
||||
|
||||
@@ -3,10 +3,7 @@ import { getEnvelopeWhereInput } from '@documenso/lib/server-only/envelope/get-e
|
||||
import { prisma } from '@documenso/prisma';
|
||||
|
||||
import { authenticatedProcedure } from '../trpc';
|
||||
import {
|
||||
ZGetEnvelopeItemsRequestSchema,
|
||||
ZGetEnvelopeItemsResponseSchema,
|
||||
} from './get-envelope-items.types';
|
||||
import { ZGetEnvelopeItemsRequestSchema, ZGetEnvelopeItemsResponseSchema } from './get-envelope-items.types';
|
||||
|
||||
// Not intended for V2 API usage.
|
||||
export const getEnvelopeItemsRoute = authenticatedProcedure
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
import DocumentDataSchema from '@documenso/prisma/generated/zod/modelSchema/DocumentDataSchema';
|
||||
import EnvelopeItemSchema from '@documenso/prisma/generated/zod/modelSchema/EnvelopeItemSchema';
|
||||
import { z } from 'zod';
|
||||
|
||||
export const ZGetEnvelopeItemsRequestSchema = z.object({
|
||||
envelopeId: z.string(),
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
import { getEnvelopeById } from '@documenso/lib/server-only/envelope/get-envelope-by-id';
|
||||
|
||||
import { authenticatedProcedure } from '../trpc';
|
||||
import {
|
||||
ZGetEnvelopeRequestSchema,
|
||||
ZGetEnvelopeResponseSchema,
|
||||
getEnvelopeMeta,
|
||||
} from './get-envelope.types';
|
||||
import { getEnvelopeMeta, ZGetEnvelopeRequestSchema, ZGetEnvelopeResponseSchema } from './get-envelope.types';
|
||||
|
||||
export const getEnvelopeRoute = authenticatedProcedure
|
||||
.meta(getEnvelopeMeta)
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
import { ZEnvelopeSchema } from '@documenso/lib/types/envelope';
|
||||
import { z } from 'zod';
|
||||
|
||||
import type { TrpcRouteMeta } from '../trpc';
|
||||
|
||||
|
||||
@@ -2,9 +2,9 @@ import { getEnvelopesByIds } from '@documenso/lib/server-only/envelope/get-envel
|
||||
|
||||
import { authenticatedProcedure } from '../trpc';
|
||||
import {
|
||||
getEnvelopesByIdsMeta,
|
||||
ZGetEnvelopesByIdsRequestSchema,
|
||||
ZGetEnvelopesByIdsResponseSchema,
|
||||
getEnvelopesByIdsMeta,
|
||||
} from './get-envelopes-by-ids.types';
|
||||
|
||||
export const getEnvelopesByIdsRoute = authenticatedProcedure
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
import { ZEnvelopeSchema } from '@documenso/lib/types/envelope';
|
||||
import { z } from 'zod';
|
||||
|
||||
import type { TrpcRouteMeta } from '../trpc';
|
||||
|
||||
|
||||
@@ -3,9 +3,9 @@ import { formatSigningLink } from '@documenso/lib/utils/recipients';
|
||||
|
||||
import { authenticatedProcedure } from '../trpc';
|
||||
import {
|
||||
redistributeEnvelopeMeta,
|
||||
ZRedistributeEnvelopeRequestSchema,
|
||||
ZRedistributeEnvelopeResponseSchema,
|
||||
redistributeEnvelopeMeta,
|
||||
} from './redistribute-envelope.types';
|
||||
|
||||
export const redistributeEnvelopeRoute = authenticatedProcedure
|
||||
|
||||
@@ -17,10 +17,7 @@ export const redistributeEnvelopeMeta: TrpcRouteMeta = {
|
||||
|
||||
export const ZRedistributeEnvelopeRequestSchema = z.object({
|
||||
envelopeId: z.string(),
|
||||
recipients: z
|
||||
.array(z.number())
|
||||
.min(1)
|
||||
.describe('The IDs of the recipients to redistribute the envelope to.'),
|
||||
recipients: z.array(z.number()).min(1).describe('The IDs of the recipients to redistribute the envelope to.'),
|
||||
});
|
||||
|
||||
export const ZRedistributeEnvelopeResponseSchema = ZSuccessResponseSchema.extend({
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
|
||||
import { getEnvelopeWhereInput } from '@documenso/lib/server-only/envelope/get-envelope-by-id';
|
||||
import { UNSAFE_replaceEnvelopeItemPdf } from '@documenso/lib/server-only/envelope-item/replace-envelope-item-pdf';
|
||||
import { getEnvelopeItemPermissions } from '@documenso/lib/utils/envelope';
|
||||
import { prisma } from '@documenso/prisma';
|
||||
|
||||
import { authenticatedProcedure } from '../trpc';
|
||||
import {
|
||||
ZReplaceEnvelopeItemPdfRequestSchema,
|
||||
ZReplaceEnvelopeItemPdfResponseSchema,
|
||||
} from './replace-envelope-item-pdf.types';
|
||||
|
||||
/**
|
||||
* Keep this internal for the envelope editor.
|
||||
*
|
||||
* If we want to make this public then create a separate one that only allows
|
||||
* the PDF to be replaced & doesn't return deleted fields, etc.
|
||||
*/
|
||||
export const replaceEnvelopeItemPdfRoute = authenticatedProcedure
|
||||
.input(ZReplaceEnvelopeItemPdfRequestSchema)
|
||||
.output(ZReplaceEnvelopeItemPdfResponseSchema)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
const { user, teamId, metadata } = ctx;
|
||||
const { payload, file } = input;
|
||||
const { envelopeId, envelopeItemId, title } = payload;
|
||||
|
||||
ctx.logger.info({
|
||||
input: {
|
||||
envelopeId,
|
||||
envelopeItemId,
|
||||
},
|
||||
});
|
||||
|
||||
const { envelopeWhereInput } = await getEnvelopeWhereInput({
|
||||
id: {
|
||||
type: 'envelopeId',
|
||||
id: envelopeId,
|
||||
},
|
||||
type: null,
|
||||
userId: user.id,
|
||||
teamId,
|
||||
});
|
||||
|
||||
const envelope = await prisma.envelope.findUnique({
|
||||
where: envelopeWhereInput,
|
||||
include: {
|
||||
recipients: true,
|
||||
envelopeItems: {
|
||||
orderBy: {
|
||||
order: 'asc',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (!envelope) {
|
||||
throw new AppError(AppErrorCode.NOT_FOUND, {
|
||||
message: 'Envelope not found',
|
||||
});
|
||||
}
|
||||
|
||||
if (envelope.internalVersion !== 2) {
|
||||
throw new AppError(AppErrorCode.INVALID_REQUEST, {
|
||||
message: 'PDF replacement is only supported for version 2 envelopes',
|
||||
});
|
||||
}
|
||||
|
||||
const { canFileBeChanged } = getEnvelopeItemPermissions(envelope, envelope.recipients);
|
||||
|
||||
if (!canFileBeChanged) {
|
||||
throw new AppError(AppErrorCode.INVALID_REQUEST, {
|
||||
message: 'Envelope item is not editable',
|
||||
});
|
||||
}
|
||||
|
||||
const envelopeItem = envelope.envelopeItems.find((item) => item.id === envelopeItemId);
|
||||
|
||||
if (!envelopeItem) {
|
||||
throw new AppError(AppErrorCode.NOT_FOUND, {
|
||||
message: 'Envelope item not found',
|
||||
});
|
||||
}
|
||||
|
||||
const { updatedItem, fields } = await UNSAFE_replaceEnvelopeItemPdf({
|
||||
envelope,
|
||||
recipients: envelope.recipients,
|
||||
envelopeItemId,
|
||||
oldDocumentDataId: envelopeItem.documentDataId,
|
||||
data: {
|
||||
file,
|
||||
title,
|
||||
},
|
||||
user: {
|
||||
id: user.id,
|
||||
name: user.name,
|
||||
email: user.email,
|
||||
},
|
||||
apiRequestMetadata: metadata,
|
||||
});
|
||||
|
||||
return {
|
||||
data: updatedItem,
|
||||
fields,
|
||||
};
|
||||
});
|
||||
@@ -0,0 +1,41 @@
|
||||
import { ZEnvelopeFieldSchema } from '@documenso/lib/types/field';
|
||||
import EnvelopeItemSchema from '@documenso/prisma/generated/zod/modelSchema/EnvelopeItemSchema';
|
||||
import { z } from 'zod';
|
||||
import { zfd } from 'zod-form-data';
|
||||
|
||||
import { zfdFile, zodFormData } from '../../utils/zod-form-data';
|
||||
import { ZDocumentTitleSchema } from '../document-router/schema';
|
||||
|
||||
export const ZReplaceEnvelopeItemPdfPayloadSchema = z.object({
|
||||
envelopeId: z.string(),
|
||||
envelopeItemId: z.string(),
|
||||
title: ZDocumentTitleSchema.optional(),
|
||||
});
|
||||
|
||||
export const ZReplaceEnvelopeItemPdfRequestSchema = zodFormData({
|
||||
payload: zfd.json(ZReplaceEnvelopeItemPdfPayloadSchema),
|
||||
file: zfdFile(),
|
||||
});
|
||||
|
||||
export const ZReplaceEnvelopeItemPdfResponseSchema = z.object({
|
||||
data: EnvelopeItemSchema.pick({
|
||||
id: true,
|
||||
title: true,
|
||||
envelopeId: true,
|
||||
order: true,
|
||||
documentDataId: true,
|
||||
}),
|
||||
/**
|
||||
* The full list of fields for the envelope after the replacement.
|
||||
*
|
||||
* This is only populated if fields have been changed or deleted. It will
|
||||
* return undefined otherwise.
|
||||
*
|
||||
* Done this way to reduce number of queries.
|
||||
*/
|
||||
fields: ZEnvelopeFieldSchema.array().optional(),
|
||||
});
|
||||
|
||||
export type TReplaceEnvelopeItemPdfPayload = z.infer<typeof ZReplaceEnvelopeItemPdfPayloadSchema>;
|
||||
export type TReplaceEnvelopeItemPdfRequest = z.infer<typeof ZReplaceEnvelopeItemPdfRequestSchema>;
|
||||
export type TReplaceEnvelopeItemPdfResponse = z.infer<typeof ZReplaceEnvelopeItemPdfResponseSchema>;
|
||||
@@ -15,6 +15,7 @@ import { duplicateEnvelopeRoute } from './duplicate-envelope';
|
||||
import { createEnvelopeFieldsRoute } from './envelope-fields/create-envelope-fields';
|
||||
import { deleteEnvelopeFieldRoute } from './envelope-fields/delete-envelope-field';
|
||||
import { getEnvelopeFieldRoute } from './envelope-fields/get-envelope-field';
|
||||
import { getEnvelopeFieldSignaturesRoute } from './envelope-fields/get-envelope-field-signatures';
|
||||
import { updateEnvelopeFieldsRoute } from './envelope-fields/update-envelope-fields';
|
||||
import { createEnvelopeRecipientsRoute } from './envelope-recipients/create-envelope-recipients';
|
||||
import { deleteEnvelopeRecipientRoute } from './envelope-recipients/delete-envelope-recipient';
|
||||
@@ -22,11 +23,14 @@ import { getEnvelopeRecipientRoute } from './envelope-recipients/get-envelope-re
|
||||
import { updateEnvelopeRecipientsRoute } from './envelope-recipients/update-envelope-recipients';
|
||||
import { findEnvelopeAuditLogsRoute } from './find-envelope-audit-logs';
|
||||
import { findEnvelopesRoute } from './find-envelopes';
|
||||
import { getEditorEnvelopeRoute } from './get-editor-envelope';
|
||||
import { getEnvelopeRoute } from './get-envelope';
|
||||
import { getEnvelopeItemsRoute } from './get-envelope-items';
|
||||
import { getEnvelopeItemsByTokenRoute } from './get-envelope-items-by-token';
|
||||
import { getEnvelopesByIdsRoute } from './get-envelopes-by-ids';
|
||||
import { redistributeEnvelopeRoute } from './redistribute-envelope';
|
||||
import { replaceEnvelopeItemPdfRoute } from './replace-envelope-item-pdf';
|
||||
import { saveAsTemplateRoute } from './save-as-template';
|
||||
import { setEnvelopeFieldsRoute } from './set-envelope-fields';
|
||||
import { setEnvelopeRecipientsRoute } from './set-envelope-recipients';
|
||||
import { signEnvelopeFieldRoute } from './sign-envelope-field';
|
||||
@@ -57,6 +61,7 @@ export const envelopeRouter = router({
|
||||
updateMany: updateEnvelopeItemsRoute,
|
||||
delete: deleteEnvelopeItemRoute,
|
||||
download: downloadEnvelopeItemRoute,
|
||||
replacePdf: replaceEnvelopeItemPdfRoute,
|
||||
},
|
||||
recipient: {
|
||||
get: getEnvelopeRecipientRoute,
|
||||
@@ -67,6 +72,7 @@ export const envelopeRouter = router({
|
||||
},
|
||||
field: {
|
||||
get: getEnvelopeFieldRoute,
|
||||
getSignatures: getEnvelopeFieldSignaturesRoute,
|
||||
createMany: createEnvelopeFieldsRoute,
|
||||
updateMany: updateEnvelopeFieldsRoute,
|
||||
delete: deleteEnvelopeFieldRoute,
|
||||
@@ -81,6 +87,9 @@ export const envelopeRouter = router({
|
||||
move: bulkMoveEnvelopesRoute,
|
||||
delete: bulkDeleteEnvelopesRoute,
|
||||
},
|
||||
editor: {
|
||||
get: getEditorEnvelopeRoute,
|
||||
},
|
||||
get: getEnvelopeRoute,
|
||||
getMany: getEnvelopesByIdsRoute,
|
||||
create: createEnvelopeRoute,
|
||||
@@ -88,6 +97,7 @@ export const envelopeRouter = router({
|
||||
update: updateEnvelopeRoute,
|
||||
delete: deleteEnvelopeRoute,
|
||||
duplicate: duplicateEnvelopeRoute,
|
||||
saveAsTemplate: saveAsTemplateRoute,
|
||||
distribute: distributeEnvelopeRoute,
|
||||
redistribute: redistributeEnvelopeRoute,
|
||||
signing2fa: {
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
import { duplicateEnvelope } from '@documenso/lib/server-only/envelope/duplicate-envelope';
|
||||
|
||||
import { authenticatedProcedure } from '../trpc';
|
||||
import { ZSaveAsTemplateRequestSchema, ZSaveAsTemplateResponseSchema } from './save-as-template.types';
|
||||
|
||||
export const saveAsTemplateRoute = authenticatedProcedure
|
||||
.input(ZSaveAsTemplateRequestSchema)
|
||||
.output(ZSaveAsTemplateResponseSchema)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
const { envelopeId, includeRecipients, includeFields } = input;
|
||||
const { teamId } = ctx;
|
||||
|
||||
ctx.logger.info({
|
||||
input: {
|
||||
envelopeId,
|
||||
},
|
||||
});
|
||||
|
||||
const result = await duplicateEnvelope({
|
||||
userId: ctx.user.id,
|
||||
teamId,
|
||||
id: {
|
||||
type: 'envelopeId',
|
||||
id: envelopeId,
|
||||
},
|
||||
overrides: {
|
||||
duplicateAsTemplate: true,
|
||||
includeRecipients,
|
||||
includeFields,
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
id: result.id,
|
||||
};
|
||||
});
|
||||
@@ -0,0 +1,14 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
export const ZSaveAsTemplateRequestSchema = z.object({
|
||||
envelopeId: z.string().min(1).describe('The ID of the envelope to save as a template.'),
|
||||
includeRecipients: z.boolean(),
|
||||
includeFields: z.boolean(),
|
||||
});
|
||||
|
||||
export const ZSaveAsTemplateResponseSchema = z.object({
|
||||
id: z.string().describe('The ID of the newly created template envelope.'),
|
||||
});
|
||||
|
||||
export type TSaveAsTemplateRequest = z.infer<typeof ZSaveAsTemplateRequestSchema>;
|
||||
export type TSaveAsTemplateResponse = z.infer<typeof ZSaveAsTemplateResponseSchema>;
|
||||
@@ -1,6 +1,5 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
import RecipientSchema from '@documenso/prisma/generated/zod/modelSchema/RecipientSchema';
|
||||
import { z } from 'zod';
|
||||
|
||||
// Common schemas between envelope routes.
|
||||
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
import { setFieldsForDocument } from '@documenso/lib/server-only/field/set-fields-for-document';
|
||||
import { setFieldsForTemplate } from '@documenso/lib/server-only/field/set-fields-for-template';
|
||||
import { EnvelopeType } from '@prisma/client';
|
||||
import { match } from 'ts-pattern';
|
||||
|
||||
import { setFieldsForDocument } from '@documenso/lib/server-only/field/set-fields-for-document';
|
||||
import { setFieldsForTemplate } from '@documenso/lib/server-only/field/set-fields-for-template';
|
||||
|
||||
import { authenticatedProcedure } from '../trpc';
|
||||
import {
|
||||
ZSetEnvelopeFieldsRequestSchema,
|
||||
ZSetEnvelopeFieldsResponseSchema,
|
||||
} from './set-envelope-fields.types';
|
||||
import { ZSetEnvelopeFieldsRequestSchema, ZSetEnvelopeFieldsResponseSchema } from './set-envelope-fields.types';
|
||||
|
||||
// Note: This is intended to always be an internal route.
|
||||
export const setEnvelopeFieldsRoute = authenticatedProcedure
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
import { EnvelopeType, FieldType } from '@prisma/client';
|
||||
import { z } from 'zod';
|
||||
|
||||
import {
|
||||
ZClampedFieldHeightSchema,
|
||||
ZClampedFieldPositionXSchema,
|
||||
@@ -9,6 +6,8 @@ import {
|
||||
ZEnvelopeFieldSchema,
|
||||
} from '@documenso/lib/types/field';
|
||||
import { ZFieldMetaSchema } from '@documenso/lib/types/field-meta';
|
||||
import { EnvelopeType, FieldType } from '@prisma/client';
|
||||
import { z } from 'zod';
|
||||
|
||||
export const ZSetEnvelopeFieldsRequestSchema = z.object({
|
||||
envelopeId: z.string(),
|
||||
@@ -16,18 +15,12 @@ export const ZSetEnvelopeFieldsRequestSchema = z.object({
|
||||
fields: z.array(
|
||||
// Todo: Envelopes - Use strict schema for types + field meta.
|
||||
z.object({
|
||||
id: z
|
||||
.number()
|
||||
.optional()
|
||||
.describe('The id of the field. If not provided, a new field will be created.'),
|
||||
id: z.number().optional().describe('The id of the field. If not provided, a new field will be created.'),
|
||||
formId: z.string().optional().describe('A temporary ID to keep track of new fields created'),
|
||||
envelopeItemId: z.string().describe('The id of the envelope item to put the field on'),
|
||||
recipientId: z.number(),
|
||||
type: z.nativeEnum(FieldType),
|
||||
page: z
|
||||
.number()
|
||||
.min(1)
|
||||
.describe('The page number of the field on the envelope. Starts from 1.'),
|
||||
page: z.number().min(1).describe('The page number of the field on the envelope. Starts from 1.'),
|
||||
positionX: ZClampedFieldPositionXSchema,
|
||||
positionY: ZClampedFieldPositionYSchema,
|
||||
width: ZClampedFieldWidthSchema,
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { EnvelopeType } from '@prisma/client';
|
||||
import { match } from 'ts-pattern';
|
||||
|
||||
import { setDocumentRecipients } from '@documenso/lib/server-only/recipient/set-document-recipients';
|
||||
import { setTemplateRecipients } from '@documenso/lib/server-only/recipient/set-template-recipients';
|
||||
import { EnvelopeType } from '@prisma/client';
|
||||
import { match } from 'ts-pattern';
|
||||
|
||||
import { authenticatedProcedure } from '../trpc';
|
||||
import {
|
||||
|
||||
@@ -1,22 +1,21 @@
|
||||
import { ZRecipientActionAuthTypesSchema } from '@documenso/lib/types/document-auth';
|
||||
import { ZRecipientEmailSchema, ZRecipientLiteSchema } from '@documenso/lib/types/recipient';
|
||||
import { EnvelopeType, RecipientRole } from '@prisma/client';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { ZRecipientActionAuthTypesSchema } from '@documenso/lib/types/document-auth';
|
||||
import { ZRecipientEmailSchema, ZRecipientLiteSchema } from '@documenso/lib/types/recipient';
|
||||
export const ZSetEnvelopeRecipientSchema = z.object({
|
||||
id: z.number().optional(),
|
||||
email: ZRecipientEmailSchema,
|
||||
name: z.string().max(255),
|
||||
role: z.nativeEnum(RecipientRole),
|
||||
signingOrder: z.number().optional(),
|
||||
actionAuth: z.array(ZRecipientActionAuthTypesSchema).optional().default([]),
|
||||
});
|
||||
|
||||
export const ZSetEnvelopeRecipientsRequestSchema = z.object({
|
||||
envelopeId: z.string(),
|
||||
envelopeType: z.nativeEnum(EnvelopeType),
|
||||
recipients: z.array(
|
||||
z.object({
|
||||
id: z.number().optional(),
|
||||
email: ZRecipientEmailSchema,
|
||||
name: z.string().max(255),
|
||||
role: z.nativeEnum(RecipientRole),
|
||||
signingOrder: z.number().optional(),
|
||||
actionAuth: z.array(ZRecipientActionAuthTypesSchema).optional().default([]),
|
||||
}),
|
||||
),
|
||||
recipients: ZSetEnvelopeRecipientSchema.array(),
|
||||
});
|
||||
|
||||
export const ZSetEnvelopeRecipientsResponseSchema = z.object({
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
import { DocumentStatus, FieldType, RecipientRole, SigningStatus } from '@prisma/client';
|
||||
import { match } from 'ts-pattern';
|
||||
|
||||
import { isBase64Image } from '@documenso/lib/constants/signatures';
|
||||
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
|
||||
import { validateFieldAuth } from '@documenso/lib/server-only/document/validate-field-auth';
|
||||
@@ -8,12 +5,11 @@ import { DOCUMENT_AUDIT_LOG_TYPE } from '@documenso/lib/types/document-audit-log
|
||||
import { createDocumentAuditLogData } from '@documenso/lib/utils/document-audit-logs';
|
||||
import { extractFieldInsertionValues } from '@documenso/lib/utils/envelope-signing';
|
||||
import { prisma } from '@documenso/prisma';
|
||||
import { DocumentStatus, FieldType, RecipientRole, SigningStatus } from '@prisma/client';
|
||||
import { match } from 'ts-pattern';
|
||||
|
||||
import { procedure } from '../trpc';
|
||||
import {
|
||||
ZSignEnvelopeFieldRequestSchema,
|
||||
ZSignEnvelopeFieldResponseSchema,
|
||||
} from './sign-envelope-field.types';
|
||||
import { ZSignEnvelopeFieldRequestSchema, ZSignEnvelopeFieldResponseSchema } from './sign-envelope-field.types';
|
||||
|
||||
// Note that this is an unauthenticated public procedure route.
|
||||
export const signEnvelopeFieldRoute = procedure
|
||||
@@ -51,6 +47,7 @@ export const signEnvelopeFieldRoute = procedure
|
||||
signingOrder: {
|
||||
gte: recipient.signingOrder ?? 0,
|
||||
},
|
||||
envelopeId: recipient.envelopeId,
|
||||
}
|
||||
: {
|
||||
id: recipient.id,
|
||||
@@ -111,10 +108,7 @@ export const signEnvelopeFieldRoute = procedure
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
recipient.signingStatus === SigningStatus.SIGNED ||
|
||||
field.recipient.signingStatus === SigningStatus.SIGNED
|
||||
) {
|
||||
if (recipient.signingStatus === SigningStatus.SIGNED || field.recipient.signingStatus === SigningStatus.SIGNED) {
|
||||
throw new AppError(AppErrorCode.INVALID_REQUEST, {
|
||||
message: `Recipient ${recipient.id} has already signed`,
|
||||
});
|
||||
@@ -265,27 +259,14 @@ export const signEnvelopeFieldRoute = procedure
|
||||
type,
|
||||
data: signatureImageAsBase64 || typedSignature || '',
|
||||
}))
|
||||
.with(
|
||||
FieldType.DATE,
|
||||
FieldType.EMAIL,
|
||||
FieldType.NAME,
|
||||
FieldType.TEXT,
|
||||
FieldType.INITIALS,
|
||||
(type) => ({
|
||||
type,
|
||||
data: updatedField.customText,
|
||||
}),
|
||||
)
|
||||
.with(
|
||||
FieldType.NUMBER,
|
||||
FieldType.RADIO,
|
||||
FieldType.CHECKBOX,
|
||||
FieldType.DROPDOWN,
|
||||
(type) => ({
|
||||
type,
|
||||
data: updatedField.customText,
|
||||
}),
|
||||
)
|
||||
.with(FieldType.DATE, FieldType.EMAIL, FieldType.NAME, FieldType.TEXT, FieldType.INITIALS, (type) => ({
|
||||
type,
|
||||
data: updatedField.customText,
|
||||
}))
|
||||
.with(FieldType.NUMBER, FieldType.RADIO, FieldType.CHECKBOX, FieldType.DROPDOWN, (type) => ({
|
||||
type,
|
||||
data: updatedField.customText,
|
||||
}))
|
||||
.exhaustive(),
|
||||
fieldSecurity: derivedRecipientActionAuth
|
||||
? {
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
import { ZRecipientActionAuthSchema } from '@documenso/lib/types/document-auth';
|
||||
import { ZFieldSchema } from '@documenso/lib/types/field';
|
||||
import { FieldType } from '@documenso/prisma/client';
|
||||
import SignatureSchema from '@documenso/prisma/generated/zod/modelSchema/SignatureSchema';
|
||||
import { z } from 'zod';
|
||||
|
||||
export const ZSignEnvelopeFieldValue = z.discriminatedUnion('type', [
|
||||
z.object({
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { DocumentStatus, EnvelopeType, RecipientRole, SigningStatus } from '@prisma/client';
|
||||
|
||||
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
|
||||
import { prisma } from '@documenso/prisma';
|
||||
import { DocumentStatus, EnvelopeType, RecipientRole, SigningStatus } from '@prisma/client';
|
||||
|
||||
import { maybeAuthenticatedProcedure } from '../trpc';
|
||||
import {
|
||||
@@ -66,8 +65,7 @@ export const signingStatusEnvelopeRoute = maybeAuthenticatedProcedure
|
||||
const isComplete =
|
||||
envelope.recipients.some((recipient) => recipient.signingStatus === SigningStatus.REJECTED) ||
|
||||
envelope.recipients.every(
|
||||
(recipient) =>
|
||||
recipient.role === RecipientRole.CC || recipient.signingStatus === SigningStatus.SIGNED,
|
||||
(recipient) => recipient.role === RecipientRole.CC || recipient.signingStatus === SigningStatus.SIGNED,
|
||||
);
|
||||
|
||||
if (isComplete) {
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
|
||||
import { getEnvelopeWhereInput } from '@documenso/lib/server-only/envelope/get-envelope-by-id';
|
||||
import { canEnvelopeItemsBeModified } from '@documenso/lib/utils/envelope';
|
||||
import { UNSAFE_updateEnvelopeItems } from '@documenso/lib/server-only/envelope-item/update-envelope-items';
|
||||
import { getEnvelopeItemPermissions } from '@documenso/lib/utils/envelope';
|
||||
import { prisma } from '@documenso/prisma';
|
||||
|
||||
import { authenticatedProcedure } from '../trpc';
|
||||
import {
|
||||
updateEnvelopeItemsMeta,
|
||||
ZUpdateEnvelopeItemsRequestSchema,
|
||||
ZUpdateEnvelopeItemsResponseSchema,
|
||||
updateEnvelopeItemsMeta,
|
||||
} from './update-envelope-items.types';
|
||||
|
||||
export const updateEnvelopeItemsRoute = authenticatedProcedure
|
||||
@@ -54,9 +55,40 @@ export const updateEnvelopeItemsRoute = authenticatedProcedure
|
||||
});
|
||||
}
|
||||
|
||||
if (!canEnvelopeItemsBeModified(envelope, envelope.recipients)) {
|
||||
const permissions = getEnvelopeItemPermissions(envelope, envelope.recipients);
|
||||
|
||||
const hasOrderChange = data.some((item) => {
|
||||
if (item.order === undefined) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const existingItem = envelope.envelopeItems.find((e) => e.id === item.envelopeItemId);
|
||||
|
||||
return !existingItem || existingItem.order !== item.order;
|
||||
});
|
||||
|
||||
const hasTitleChange = data.some((item) => item.title !== undefined);
|
||||
|
||||
if (!hasTitleChange && !hasOrderChange) {
|
||||
return {
|
||||
data: envelope.envelopeItems.map((item) => ({
|
||||
id: item.id,
|
||||
order: item.order,
|
||||
title: item.title,
|
||||
envelopeId: item.envelopeId,
|
||||
})),
|
||||
};
|
||||
}
|
||||
|
||||
if (hasTitleChange && !permissions.canTitleBeChanged) {
|
||||
throw new AppError(AppErrorCode.INVALID_REQUEST, {
|
||||
message: 'Envelope item is not editable',
|
||||
message: 'Envelope item title is not editable',
|
||||
});
|
||||
}
|
||||
|
||||
if (hasOrderChange && !permissions.canOrderBeChanged) {
|
||||
throw new AppError(AppErrorCode.INVALID_REQUEST, {
|
||||
message: 'Envelope item order is not editable',
|
||||
});
|
||||
}
|
||||
|
||||
@@ -71,28 +103,17 @@ export const updateEnvelopeItemsRoute = authenticatedProcedure
|
||||
});
|
||||
}
|
||||
|
||||
const updatedEnvelopeItems = await Promise.all(
|
||||
data.map(async ({ envelopeItemId, order, title }) =>
|
||||
prisma.envelopeItem.update({
|
||||
where: {
|
||||
envelopeId: envelope.id,
|
||||
id: envelopeItemId,
|
||||
},
|
||||
data: {
|
||||
order,
|
||||
title,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
order: true,
|
||||
title: true,
|
||||
envelopeId: true,
|
||||
},
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
// Todo: Envelope [AUDIT_LOGS]
|
||||
const updatedEnvelopeItems = await UNSAFE_updateEnvelopeItems({
|
||||
envelopeId,
|
||||
envelopeType: envelope.type,
|
||||
existingEnvelopeItems: envelope.envelopeItems,
|
||||
data,
|
||||
user: {
|
||||
name: user.name,
|
||||
email: user.email,
|
||||
},
|
||||
apiRequestMetadata: ctx.metadata,
|
||||
});
|
||||
|
||||
return {
|
||||
data: updatedEnvelopeItems,
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
import EnvelopeItemSchema from '@documenso/prisma/generated/zod/modelSchema/EnvelopeItemSchema';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { ZDocumentTitleSchema } from '../document-router/schema';
|
||||
import type { TrpcRouteMeta } from '../trpc';
|
||||
|
||||
@@ -2,9 +2,9 @@ import { updateEnvelope } from '@documenso/lib/server-only/envelope/update-envel
|
||||
|
||||
import { authenticatedProcedure } from '../trpc';
|
||||
import {
|
||||
updateEnvelopeMeta,
|
||||
ZUpdateEnvelopeRequestSchema,
|
||||
ZUpdateEnvelopeResponseSchema,
|
||||
updateEnvelopeMeta,
|
||||
} from './update-envelope.types';
|
||||
|
||||
export const updateEnvelopeRoute = authenticatedProcedure
|
||||
|
||||
@@ -1,17 +1,10 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
import {
|
||||
ZDocumentAccessAuthTypesSchema,
|
||||
ZDocumentActionAuthTypesSchema,
|
||||
} from '@documenso/lib/types/document-auth';
|
||||
import { ZDocumentAccessAuthTypesSchema, ZDocumentActionAuthTypesSchema } from '@documenso/lib/types/document-auth';
|
||||
import { ZDocumentMetaUpdateSchema } from '@documenso/lib/types/document-meta';
|
||||
import { ZEnvelopeLiteSchema } from '@documenso/lib/types/envelope';
|
||||
import { TemplateType } from '@prisma/client';
|
||||
import { z } from 'zod';
|
||||
|
||||
import {
|
||||
ZDocumentExternalIdSchema,
|
||||
ZDocumentTitleSchema,
|
||||
ZDocumentVisibilitySchema,
|
||||
} from '../document-router/schema';
|
||||
import { ZDocumentExternalIdSchema, ZDocumentTitleSchema, ZDocumentVisibilitySchema } from '../document-router/schema';
|
||||
import type { TrpcRouteMeta } from '../trpc';
|
||||
|
||||
export const updateEnvelopeMeta: TrpcRouteMeta = {
|
||||
@@ -33,6 +26,7 @@ export const ZUpdateEnvelopeRequestSchema = z.object({
|
||||
globalAccessAuth: z.array(ZDocumentAccessAuthTypesSchema).optional(),
|
||||
globalActionAuth: z.array(ZDocumentActionAuthTypesSchema).optional(),
|
||||
folderId: z.string().nullish(),
|
||||
templateType: z.nativeEnum(TemplateType).optional(),
|
||||
})
|
||||
.optional(),
|
||||
meta: ZDocumentMetaUpdateSchema.optional(),
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import { EnvelopeType } from '@prisma/client';
|
||||
|
||||
import { getServerLimits } from '@documenso/ee/server-only/limits/server';
|
||||
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
|
||||
import { sendDocument } from '@documenso/lib/server-only/document/send-document';
|
||||
@@ -7,13 +5,10 @@ import { getEnvelopeById } from '@documenso/lib/server-only/envelope/get-envelop
|
||||
import { createDocumentFromTemplate } from '@documenso/lib/server-only/template/create-document-from-template';
|
||||
import { putNormalizedPdfFileServerSide } from '@documenso/lib/universal/upload/put-file.server';
|
||||
import { formatSigningLink } from '@documenso/lib/utils/recipients';
|
||||
import { EnvelopeType } from '@prisma/client';
|
||||
|
||||
import { authenticatedProcedure } from '../trpc';
|
||||
import {
|
||||
ZUseEnvelopeRequestSchema,
|
||||
ZUseEnvelopeResponseSchema,
|
||||
useEnvelopeMeta,
|
||||
} from './use-envelope.types';
|
||||
import { useEnvelopeMeta, ZUseEnvelopeRequestSchema, ZUseEnvelopeResponseSchema } from './use-envelope.types';
|
||||
|
||||
export const useEnvelopeRoute = authenticatedProcedure
|
||||
.meta(useEnvelopeMeta)
|
||||
@@ -72,9 +67,7 @@ export const useEnvelopeRoute = authenticatedProcedure
|
||||
const filesToUpload = files.filter(
|
||||
(file, index) =>
|
||||
payload.customDocumentData &&
|
||||
payload.customDocumentData.some(
|
||||
(mapping) => mapping.identifier === file.name || mapping.identifier === index,
|
||||
),
|
||||
payload.customDocumentData.some((mapping) => mapping.identifier === file.name || mapping.identifier === index),
|
||||
);
|
||||
|
||||
// Process uploaded files and create document data for them
|
||||
@@ -98,9 +91,7 @@ export const useEnvelopeRoute = authenticatedProcedure
|
||||
|
||||
// Find the uploaded file by identifier
|
||||
if (typeof mapping.identifier === 'string') {
|
||||
documentDataId = uploadedFiles.find(
|
||||
(file) => file.name === mapping.identifier,
|
||||
)?.documentDataId;
|
||||
documentDataId = uploadedFiles.find((file) => file.name === mapping.identifier)?.documentDataId;
|
||||
}
|
||||
|
||||
if (typeof mapping.identifier === 'number') {
|
||||
@@ -118,9 +109,7 @@ export const useEnvelopeRoute = authenticatedProcedure
|
||||
}
|
||||
|
||||
// Verify that the envelopeItemId exists in the template
|
||||
const envelopeItem = envelope.envelopeItems.find(
|
||||
(item) => item.id === mapping.envelopeItemId,
|
||||
);
|
||||
const envelopeItem = envelope.envelopeItems.find((item) => item.id === mapping.envelopeItemId);
|
||||
|
||||
if (!envelopeItem) {
|
||||
throw new AppError(AppErrorCode.NOT_FOUND, {
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import { z } from 'zod';
|
||||
import { zfd } from 'zod-form-data';
|
||||
|
||||
import { ZEnvelopeExpirationPeriod } from '@documenso/lib/constants/envelope-expiration';
|
||||
import { ZDocumentEmailSettingsSchema } from '@documenso/lib/types/document-email';
|
||||
import { ZDocumentFormValuesSchema } from '@documenso/lib/types/document-form-values';
|
||||
import {
|
||||
@@ -18,8 +16,10 @@ import {
|
||||
import { ZEnvelopeAttachmentTypeSchema } from '@documenso/lib/types/envelope-attachment';
|
||||
import { ZFieldMetaPrefillFieldsSchema } from '@documenso/lib/types/field-meta';
|
||||
import { ZRecipientEmailSchema } from '@documenso/lib/types/recipient';
|
||||
import { z } from 'zod';
|
||||
import { zfd } from 'zod-form-data';
|
||||
|
||||
import { zodFormData } from '../../utils/zod-form-data';
|
||||
import { zfdFile, zodFormData } from '../../utils/zod-form-data';
|
||||
import type { TrpcRouteMeta } from '../trpc';
|
||||
import { ZRecipientWithSigningUrlSchema } from './schema';
|
||||
|
||||
@@ -66,9 +66,7 @@ export const ZUseEnvelopePayloadSchema = z.object({
|
||||
.describe('The envelope item ID from the template to replace with the uploaded file.'),
|
||||
}),
|
||||
)
|
||||
.describe(
|
||||
'Map uploaded files to specific envelope items in the template. If not provided, files will be ignored.',
|
||||
)
|
||||
.describe('Map uploaded files to specific envelope items in the template. If not provided, files will be ignored.')
|
||||
.optional(),
|
||||
folderId: z
|
||||
.string()
|
||||
@@ -97,6 +95,7 @@ export const ZUseEnvelopePayloadSchema = z.object({
|
||||
uploadSignatureEnabled: ZDocumentMetaUploadSignatureEnabledSchema.optional(),
|
||||
drawSignatureEnabled: ZDocumentMetaDrawSignatureEnabledSchema.optional(),
|
||||
allowDictateNextSigner: z.boolean().optional(),
|
||||
envelopeExpirationPeriod: ZEnvelopeExpirationPeriod.nullish(),
|
||||
})
|
||||
.describe('Override values from the template for the created document.')
|
||||
.optional(),
|
||||
@@ -115,7 +114,7 @@ export const ZUseEnvelopePayloadSchema = z.object({
|
||||
|
||||
export const ZUseEnvelopeRequestSchema = zodFormData({
|
||||
payload: zfd.json(ZUseEnvelopePayloadSchema),
|
||||
files: zfd.repeatableOfType(zfd.file()).optional(),
|
||||
files: zfd.repeatableOfType(zfdFile()).optional(),
|
||||
});
|
||||
|
||||
export const ZUseEnvelopeResponseSchema = z.object({
|
||||
|
||||
Reference in New Issue
Block a user