Files
documenso/packages/trpc/server/envelope-router/replace-envelope-item-pdf.ts
T
Lucas Smith d5ce222482 feat: add CSC AES/QES signing (v1 instance-wide config) (#2874)
Adds Cloud Signature Consortium (CSC) integration for AES/QES signing
against a configured TSP. v1 ships as instance-wide configuration via
environment variables, with per-envelope signature level selection,
license gating, and an OAuth-driven signing flow (capture + FIFO
signers, SAD session, blocking/in-progress recipient pages).

Includes signature level compatibility checks (role, signing order,
dictate next signer), envelope mutability assertions, Prisma migration
for signature level and CSC tables, and docs for the new signing
certificate options.
2026-06-16 23:37:34 +10:00

109 lines
3.0 KiB
TypeScript

import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
import { assertEnvelopeMutable } from '@documenso/lib/server-only/envelope/assert-envelope-mutable';
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',
});
}
assertEnvelopeMutable(envelope);
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,
};
});