chore: remove duplicates

This commit is contained in:
Ephraim Atta-Duncan
2025-11-18 21:05:37 +00:00
parent 8e2ca94020
commit c8e254aff1
7 changed files with 104 additions and 107 deletions

View File

@ -12,7 +12,7 @@ import { match } from 'ts-pattern';
import { useCurrentEnvelopeEditor } from '@documenso/lib/client-only/providers/envelope-editor-provider'; import { useCurrentEnvelopeEditor } from '@documenso/lib/client-only/providers/envelope-editor-provider';
import { useCurrentEnvelopeRender } from '@documenso/lib/client-only/providers/envelope-render-provider'; import { useCurrentEnvelopeRender } from '@documenso/lib/client-only/providers/envelope-render-provider';
import { getPageCanvasRefs } from '@documenso/lib/client-only/utils/page-canvas-registry'; import { getPageCanvasRefs } from '@documenso/lib/client-only/utils/page-canvas-registry';
import type { TDetectedFormField } from '@documenso/lib/types/ai'; import type { TDetectedFormField } from '@documenso/lib/types/document-analysis';
import type { import type {
TCheckboxFieldMeta, TCheckboxFieldMeta,
TDateFieldMeta, TDateFieldMeta,

View File

@ -1,108 +1,30 @@
// sort-imports-ignore
// ---- PATCH pdfjs-dist's canvas require BEFORE importing it ----
import { createRequire } from 'node:module';
import { fileURLToPath } from 'node:url';
import { Canvas, Image } from 'skia-canvas';
const require = createRequire(import.meta.url || fileURLToPath(new URL('.', import.meta.url)));
const Module = require('node:module');
const originalRequire = Module.prototype.require;
Module.prototype.require = function (path: string) {
if (path === 'canvas') {
return {
createCanvas: (width: number, height: number) => new Canvas(width, height),
Image, // needed by pdfjs-dist
};
}
// eslint-disable-next-line prefer-rest-params, @typescript-eslint/consistent-type-assertions
return originalRequire.apply(this, arguments as unknown as [string]);
};
// Use dynamic require to bypass Vite SSR transformation
// eslint-disable-next-line @typescript-eslint/no-var-requires
const pdfjsLib = require('pdfjs-dist/legacy/build/pdf.js');
import { mkdir, writeFile } from 'node:fs/promises';
import { join } from 'node:path';
import { generateObject } from 'ai'; import { generateObject } from 'ai';
import { Hono } from 'hono'; import { Hono } from 'hono';
import sharp from 'sharp'; import { mkdir, writeFile } from 'node:fs/promises';
import { z } from 'zod'; import { join } from 'node:path';
import { Canvas, Image } from 'skia-canvas';
import { getSession } from '@documenso/auth/server/lib/utils/get-session'; import { getSession } from '@documenso/auth/server/lib/utils/get-session';
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error'; import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
import { resizeAndCompressImage } from '@documenso/lib/server-only/image/resize-and-compress-image';
import { renderPdfToImage } from '@documenso/lib/server-only/pdf/render-pdf-to-image';
import { getTeamById } from '@documenso/lib/server-only/team/get-team'; import { getTeamById } from '@documenso/lib/server-only/team/get-team';
import { getFileServerSide } from '@documenso/lib/universal/upload/get-file.server'; import { getFileServerSide } from '@documenso/lib/universal/upload/get-file.server';
import { env } from '@documenso/lib/utils/env'; import { env } from '@documenso/lib/utils/env';
import { resolveRecipientEmail } from '@documenso/lib/utils/recipients';
import { prisma } from '@documenso/prisma'; import { prisma } from '@documenso/prisma';
import { ANALYZE_RECIPIENTS_PROMPT, DETECT_OBJECTS_PROMPT } from './ai.prompts'; import type { HonoEnv } from '../../router';
import type { HonoEnv } from '../router'; import { ANALYZE_RECIPIENTS_PROMPT, DETECT_OBJECTS_PROMPT } from './prompts';
import { import {
type TAnalyzeRecipientsResponse, type TAnalyzeRecipientsResponse,
type TDetectedRecipient,
type TDetectFormFieldsResponse, type TDetectFormFieldsResponse,
type TDetectedRecipient,
ZAnalyzeRecipientsRequestSchema, ZAnalyzeRecipientsRequestSchema,
ZDetectedRecipientLLMSchema,
ZDetectedFormFieldSchema,
ZDetectFormFieldsRequestSchema, ZDetectFormFieldsRequestSchema,
} from './ai.types'; ZDetectedFormFieldSchema,
ZDetectedRecipientLLMSchema,
const renderPdfToImage = async (pdfBytes: Uint8Array) => { } from './types';
const loadingTask = pdfjsLib.getDocument({ data: pdfBytes });
const pdf = await loadingTask.promise;
try {
const scale = 4;
const pages = await Promise.all(
Array.from({ length: pdf.numPages }, async (_, index) => {
const pageNumber = index + 1;
const page = await pdf.getPage(pageNumber);
try {
const viewport = page.getViewport({ scale });
const virtualCanvas = new Canvas(viewport.width, viewport.height);
const context = virtualCanvas.getContext('2d');
context.imageSmoothingEnabled = false;
await page.render({ canvasContext: context, viewport }).promise;
return {
image: await virtualCanvas.toBuffer('png'),
pageNumber,
width: Math.floor(viewport.width),
height: Math.floor(viewport.height),
};
} finally {
page.cleanup();
}
}),
);
return pages;
} finally {
await pdf.destroy();
}
};
const resizeAndCompressImage = async (imageBuffer: Buffer): Promise<Buffer> => {
const metadata = await sharp(imageBuffer).metadata();
const originalWidth = metadata.width || 0;
if (originalWidth > 1000) {
return await sharp(imageBuffer)
.resize({ width: 1000, withoutEnlargement: true })
.jpeg({ quality: 70 })
.toBuffer();
}
return await sharp(imageBuffer).jpeg({ quality: 70 }).toBuffer();
};
type FieldDetectionRecipient = { type FieldDetectionRecipient = {
id: number; id: number;
@ -203,20 +125,6 @@ const runFormFieldDetection = async (
// Limit recipient detection to first 3 pages for performance and cost efficiency // Limit recipient detection to first 3 pages for performance and cost efficiency
const MAX_PAGES_FOR_RECIPIENT_ANALYSIS = 3; const MAX_PAGES_FOR_RECIPIENT_ANALYSIS = 3;
const recipientEmailSchema = z.string().email();
const resolveRecipientEmail = (candidateEmail: string | undefined) => {
if (candidateEmail) {
const trimmedEmail = candidateEmail.trim();
if (recipientEmailSchema.safeParse(trimmedEmail).success) {
return trimmedEmail;
}
}
return undefined;
};
const authorizeDocumentAccess = async (envelopeId: string, userId: number) => { const authorizeDocumentAccess = async (envelopeId: string, userId: number) => {
const envelope = await prisma.envelope.findUnique({ const envelope = await prisma.envelope.findUnique({
where: { id: envelopeId }, where: { id: envelopeId },

View File

@ -1,6 +1,6 @@
import { z } from 'zod'; import { z } from 'zod';
import type { TDetectedFormField } from '@documenso/lib/types/ai'; import type { TDetectedFormField } from '@documenso/lib/types/document-analysis';
export const ZGenerateTextRequestSchema = z.object({ export const ZGenerateTextRequestSchema = z.object({
prompt: z.string().min(1, 'Prompt is required').max(5000, 'Prompt is too long'), prompt: z.string().min(1, 'Prompt is required').max(5000, 'Prompt is too long'),

View File

@ -14,7 +14,7 @@ import { getIpAddress } from '@documenso/lib/universal/get-ip-address';
import { logger } from '@documenso/lib/utils/logger'; import { logger } from '@documenso/lib/utils/logger';
import { openApiDocument } from '@documenso/trpc/server/open-api'; import { openApiDocument } from '@documenso/trpc/server/open-api';
import { aiRoute } from './api/ai'; import { aiRoute } from './api/document-analysis/index';
import { downloadRoute } from './api/download/download'; import { downloadRoute } from './api/download/download';
import { filesRoute } from './api/files/files'; import { filesRoute } from './api/files/files';
import { type AppContext, appContext } from './context'; import { type AppContext, appContext } from './context';

View File

@ -0,0 +1,15 @@
import sharp from 'sharp';
export const resizeAndCompressImage = async (imageBuffer: Buffer): Promise<Buffer> => {
const metadata = await sharp(imageBuffer).metadata();
const originalWidth = metadata.width || 0;
if (originalWidth > 1000) {
return await sharp(imageBuffer)
.resize({ width: 1000, withoutEnlargement: true })
.jpeg({ quality: 70 })
.toBuffer();
}
return await sharp(imageBuffer).jpeg({ quality: 70 }).toBuffer();
};

View File

@ -0,0 +1,61 @@
import { createRequire } from 'node:module';
import { fileURLToPath } from 'node:url';
import { Canvas, Image } from 'skia-canvas';
const require = createRequire(import.meta.url || fileURLToPath(new URL('.', import.meta.url)));
const Module = require('node:module');
const originalRequire = Module.prototype.require;
Module.prototype.require = function (path: string) {
if (path === 'canvas') {
return {
createCanvas: (width: number, height: number) => new Canvas(width, height),
Image, // needed by pdfjs-dist
};
}
// eslint-disable-next-line prefer-rest-params, @typescript-eslint/consistent-type-assertions
return originalRequire.apply(this, arguments as unknown as [string]);
};
// Use dynamic require to bypass Vite SSR transformation
// eslint-disable-next-line @typescript-eslint/no-var-requires
const pdfjsLib = require('pdfjs-dist/legacy/build/pdf.js');
export const renderPdfToImage = async (pdfBytes: Uint8Array) => {
const loadingTask = pdfjsLib.getDocument({ data: pdfBytes });
const pdf = await loadingTask.promise;
try {
const scale = 4;
const pages = await Promise.all(
Array.from({ length: pdf.numPages }, async (_, index) => {
const pageNumber = index + 1;
const page = await pdf.getPage(pageNumber);
try {
const viewport = page.getViewport({ scale });
const virtualCanvas = new Canvas(viewport.width, viewport.height);
const context = virtualCanvas.getContext('2d');
context.imageSmoothingEnabled = false;
await page.render({ canvasContext: context, viewport }).promise;
return {
image: await virtualCanvas.toBuffer('png'),
pageNumber,
width: Math.floor(viewport.width),
height: Math.floor(viewport.height),
};
} finally {
page.cleanup();
}
}),
);
return pages;
} finally {
await pdf.destroy();
}
};

View File

@ -1,5 +1,6 @@
import type { Envelope } from '@prisma/client'; import type { Envelope } from '@prisma/client';
import { type Field, type Recipient, RecipientRole, SigningStatus } from '@prisma/client'; import { type Field, type Recipient, RecipientRole, SigningStatus } from '@prisma/client';
import { z } from 'zod';
import { NEXT_PUBLIC_WEBAPP_URL } from '../constants/app'; import { NEXT_PUBLIC_WEBAPP_URL } from '../constants/app';
import { extractLegacyIds } from '../universal/id'; import { extractLegacyIds } from '../universal/id';
@ -8,6 +9,18 @@ const UNKNOWN_RECIPIENT_NAME_PLACEHOLDER = '<UNKNOWN>';
export const formatSigningLink = (token: string) => `${NEXT_PUBLIC_WEBAPP_URL()}/sign/${token}`; export const formatSigningLink = (token: string) => `${NEXT_PUBLIC_WEBAPP_URL()}/sign/${token}`;
export const resolveRecipientEmail = (candidateEmail: string | undefined | null) => {
if (candidateEmail) {
const trimmedEmail = candidateEmail.trim();
if (z.string().email().safeParse(trimmedEmail).success) {
return trimmedEmail;
}
}
return undefined;
};
/** /**
* Whether a recipient can be modified by the document owner. * Whether a recipient can be modified by the document owner.
*/ */