Merge branch 'main' into feat/document-file-conversion

Resolved conflicts:
- create-document-data.ts: merged initialData + originalData/originalMimeType
- files.helpers.ts: kept both imports
- envelope-drop-zone-wrapper.tsx: adopted buildDropzoneRejectionDescription
- create-envelope-items.ts: adopted UNSAFE_createEnvelopeItems
- create-envelope.ts: integrated convertToPdfIfNeeded into createEnvelopeRouteCaller

Extended putPdfFileServerSide to accept { initialData, originalData,
originalMimeType } options; updated seal-document.handler call site.
This commit is contained in:
ephraimduncan
2026-04-20 10:11:35 +00:00
1127 changed files with 107456 additions and 22991 deletions
@@ -2,7 +2,7 @@ import Konva from 'konva';
import {
DEFAULT_RECT_BACKGROUND,
RECIPIENT_COLOR_STYLES,
getRecipientColorStyles,
} from '@documenso/ui/lib/recipient-colors';
import type { FieldToRender, RenderFieldElementOptions } from './field-renderer';
@@ -70,7 +70,7 @@ export const upsertFieldRect = (
width: fieldWidth,
height: fieldHeight,
fill: DEFAULT_RECT_BACKGROUND,
stroke: color ? RECIPIENT_COLOR_STYLES[color].baseRing : '#e5e7eb',
stroke: color ? getRecipientColorStyles(color).baseRing : '#e5e7eb',
strokeWidth: 2,
cornerRadius: 2,
strokeScaleEnabled: false,
@@ -150,7 +150,7 @@ export const createFieldHoverInteraction = ({
return;
}
const hoverColor = RECIPIENT_COLOR_STYLES[options.color].baseRingHover;
const hoverColor = getRecipientColorStyles(options.color).baseRingHover;
fieldGroup.on('mouseover', () => {
const layer = fieldRect.getLayer();
@@ -1,4 +1,4 @@
import { PDFDocument } from '@cantoo/pdf-lib';
import { PDF } from '@libpdf/core';
import { DocumentDataType } from '@prisma/client';
import { base64 } from '@scure/base';
import { match } from 'ts-pattern';
@@ -16,16 +16,22 @@ type File = {
arrayBuffer: () => Promise<ArrayBuffer>;
};
type PutPdfFileOptions = {
initialData?: string;
originalData?: string;
originalMimeType?: string;
};
/**
* Uploads a document file to the appropriate storage location and creates
* a document data record.
*/
export const putPdfFileServerSide = async (file: File) => {
export const putPdfFileServerSide = async (file: File, options: PutPdfFileOptions = {}) => {
const isEncryptedDocumentsAllowed = false; // Was feature flag.
const arrayBuffer = await file.arrayBuffer();
const pdf = await PDFDocument.load(arrayBuffer).catch((e) => {
const pdf = await PDF.load(new Uint8Array(arrayBuffer)).catch((e) => {
console.error(`PDF upload parse error: ${e.message}`);
throw new AppError('INVALID_DOCUMENT_FILE');
@@ -41,7 +47,18 @@ export const putPdfFileServerSide = async (file: File) => {
const { type, data } = await putFileServerSide(file);
return await createDocumentData({ type, data });
const createdData = await createDocumentData({
type,
data,
initialData: options.initialData,
originalData: options.originalData,
originalMimeType: options.originalMimeType,
});
return {
documentData: createdData,
filePageCount: pdf.getPageCount(),
};
};
type PutNormalizedPdfOptions = {
@@ -20,7 +20,20 @@ export const getPresignPostUrl = async (fileName: string, contentType: string, u
// Get the basename and extension for the file
const { name, ext } = path.parse(fileName);
let key = `${alphaid(12)}/${slugify(name)}${ext}`;
let slugified = slugify(name);
// If the slugified name is empty or too long, generate a random string instead
//
// This is fine since we don't really need the filename in s3 since we store it
// in the database and can always get the original filename from there.
//
// The slugified name can be empty when a string contains only CJK or other
// special characters.
if (slugified.length === 0 || slugified.length > 100) {
slugified = alphaid(8);
}
let key = `${alphaid(12)}/${slugified}${ext}`;
if (userId) {
key = `${userId}/${key}`;