feat: add envelopes api (#2105)

This commit is contained in:
David Nguyen
2025-11-07 14:17:52 +11:00
committed by GitHub
parent d2a009d52e
commit d05bfa9fed
230 changed files with 10066 additions and 2812 deletions
@@ -2,6 +2,7 @@ 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 { prefixedId } from '@documenso/lib/universal/id';
import { putNormalizedPdfFileServerSide } 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 { prisma } from '@documenso/prisma';
@@ -10,14 +11,17 @@ import { authenticatedProcedure } from '../trpc';
import {
ZCreateEnvelopeItemsRequestSchema,
ZCreateEnvelopeItemsResponseSchema,
createEnvelopeItemsMeta,
} from './create-envelope-items.types';
export const createEnvelopeItemsRoute = authenticatedProcedure
.meta(createEnvelopeItemsMeta)
.input(ZCreateEnvelopeItemsRequestSchema)
.output(ZCreateEnvelopeItemsResponseSchema)
.mutation(async ({ input, ctx }) => {
const { user, teamId, metadata } = ctx;
const { envelopeId, items } = input;
const { payload, files } = input;
const { envelopeId } = payload;
ctx.logger.info({
input: {
@@ -71,7 +75,7 @@ export const createEnvelopeItemsRoute = authenticatedProcedure
const organisationClaim = envelope.team.organisation.organisationClaim;
const remainingEnvelopeItems =
organisationClaim.envelopeItemCount - envelope.envelopeItems.length - items.length;
organisationClaim.envelopeItemCount - envelope.envelopeItems.length - files.length;
if (remainingEnvelopeItems < 0) {
throw new AppError('ENVELOPE_ITEM_LIMIT_EXCEEDED', {
@@ -80,41 +84,24 @@ export const createEnvelopeItemsRoute = authenticatedProcedure
});
}
const foundDocumentData = await prisma.documentData.findMany({
where: {
id: {
in: items.map((item) => item.documentDataId),
},
},
select: {
envelopeItem: {
select: {
id: true,
},
},
},
});
// For each file, stream to s3 and create the document data.
const envelopeItems = await Promise.all(
files.map(async (file) => {
const { id: documentDataId } = await putNormalizedPdfFileServerSide(file);
// Check that all the document data was found.
if (foundDocumentData.length !== items.length) {
throw new AppError(AppErrorCode.NOT_FOUND, {
message: 'Document data not found',
});
}
// Check that it doesn't already have an envelope item.
if (foundDocumentData.some((documentData) => documentData.envelopeItem?.id)) {
throw new AppError(AppErrorCode.INVALID_REQUEST, {
message: 'Document data not found',
});
}
return {
title: file.name,
documentDataId,
};
}),
);
const currentHighestOrderValue =
envelope.envelopeItems[envelope.envelopeItems.length - 1]?.order ?? 1;
const result = await prisma.$transaction(async (tx) => {
const createdItems = await tx.envelopeItem.createManyAndReturn({
data: items.map((item) => ({
data: envelopeItems.map((item) => ({
id: prefixedId('envelope_item'),
envelopeId,
title: item.title,
@@ -148,6 +135,6 @@ export const createEnvelopeItemsRoute = authenticatedProcedure
});
return {
createdEnvelopeItems: result,
data: result,
};
});