From 19c7765484282bbcc0d45aa3771fb840596099a2 Mon Sep 17 00:00:00 2001 From: SoSweetHam Date: Wed, 28 Jan 2026 13:47:20 +0530 Subject: [PATCH 1/5] fix(e-signer): file upload error handling and coherent limits with file manager --- .../src/controllers/FileController.ts | 2 +- platforms/esigner-api/src/index.ts | 4 +- platforms/esigner/src/lib/stores/files.ts | 33 +- .../routes/(protected)/files/new/+page.svelte | 771 +++++------------- 4 files changed, 231 insertions(+), 579 deletions(-) diff --git a/platforms/esigner-api/src/controllers/FileController.ts b/platforms/esigner-api/src/controllers/FileController.ts index 9483322c6..7315348f6 100644 --- a/platforms/esigner-api/src/controllers/FileController.ts +++ b/platforms/esigner-api/src/controllers/FileController.ts @@ -3,7 +3,7 @@ import { FileService, ReservedFileNameError } from "../services/FileService"; import multer from "multer"; const upload = multer({ - limits: { fileSize: 10 * 1024 * 1024 }, // 10MB limit + limits: { fileSize: 1024 * 1024 * 1024 }, // 1GB limit storage: multer.memoryStorage(), }); diff --git a/platforms/esigner-api/src/index.ts b/platforms/esigner-api/src/index.ts index 9efbce9a1..101592e99 100644 --- a/platforms/esigner-api/src/index.ts +++ b/platforms/esigner-api/src/index.ts @@ -61,8 +61,8 @@ app.use( credentials: true, }), ); -app.use(express.json({ limit: "50mb" })); -app.use(express.urlencoded({ limit: "50mb", extended: true })); +app.use(express.json({ limit: "1gb" })); +app.use(express.urlencoded({ limit: "1gb", extended: true })); // Controllers const authController = new AuthController(); diff --git a/platforms/esigner/src/lib/stores/files.ts b/platforms/esigner/src/lib/stores/files.ts index c594ed940..4de6f4af3 100644 --- a/platforms/esigner/src/lib/stores/files.ts +++ b/platforms/esigner/src/lib/stores/files.ts @@ -65,7 +65,25 @@ export const fetchDocuments = async () => { // Keep fetchFiles alias for backward compatibility export const fetchFiles = fetchDocuments; +const MAX_FILE_SIZE = 1024 * 1024 * 1024; // 1GB limit + +export class FileSizeError extends Error { + constructor(public fileSize: number, public maxSize: number = MAX_FILE_SIZE) { + const fileSizeMB = (fileSize / (1024 * 1024)).toFixed(2); + const maxSizeMB = (maxSize / (1024 * 1024)).toFixed(0); + super(`File size (${fileSizeMB} MB) exceeds the maximum limit of ${maxSizeMB} MB`); + this.name = 'FileSizeError'; + } +} + export const uploadFile = async (file: File, displayName?: string, description?: string) => { + // Client-side file size validation + if (file.size > MAX_FILE_SIZE) { + const err = new FileSizeError(file.size, MAX_FILE_SIZE); + error.set(err.message); + throw err; + } + try { isLoading.set(true); error.set(null); @@ -84,7 +102,20 @@ export const uploadFile = async (file: File, displayName?: string, description?: }); await fetchDocuments(); return response.data; - } catch (err) { + } catch (err: unknown) { + // Handle HTTP 413 Payload Too Large + if (err && typeof err === 'object' && 'response' in err) { + const axiosError = err as { response?: { status?: number; data?: { error?: string; maxSize?: number; fileSize?: number } } }; + if (axiosError.response?.status === 413) { + const data = axiosError.response.data; + const fileSizeErr = new FileSizeError( + data?.fileSize || file.size, + data?.maxSize || MAX_FILE_SIZE + ); + error.set(fileSizeErr.message); + throw fileSizeErr; + } + } error.set(err instanceof Error ? err.message : 'Failed to upload file'); throw err; } finally { diff --git a/platforms/esigner/src/routes/(protected)/files/new/+page.svelte b/platforms/esigner/src/routes/(protected)/files/new/+page.svelte index f5e4ec402..6ab61a5f6 100644 --- a/platforms/esigner/src/routes/(protected)/files/new/+page.svelte +++ b/platforms/esigner/src/routes/(protected)/files/new/+page.svelte @@ -1,575 +1,196 @@ - - -
- -
- - - - - Back to Signature Containers - -

New Signature Container

-
- -
-
-
-
= 1 ? 'bg-blue-600 text-white' : 'bg-gray-200 text-gray-600' - }`}> - 1 -
- = 1 ? 'text-blue-600 font-medium' : 'text-gray-600'}>Upload File -
-
-
= 2 ? 'bg-blue-600' : ''}`} style="width: {currentStep >= 2 ? '100%' : '0%'}">
-
-
-
= 2 ? 'bg-blue-600 text-white' : 'bg-gray-200 text-gray-600' - }`}> - 2 -
- = 2 ? 'text-blue-600 font-medium' : 'text-gray-600'}>Invite Signees -
-
-
= 3 ? 'bg-blue-600' : ''}`} style="width: {currentStep >= 3 ? '100%' : '0%'}">
-
-
-
= 3 ? 'bg-blue-600 text-white' : 'bg-gray-200 text-gray-600' - }`}> - 3 -
- = 3 ? 'text-blue-600 font-medium' : 'text-gray-600'}>Review -
-
-
- - - {#if currentStep === 1} -
-

Upload File

- - -
-

Upload New File

-
- {#if uploadedFile} -
-
{getFileIcon(uploadedFile.type)}
-

{uploadedFile.name}

-

{formatFileSize(uploadedFile.size)}

-
- - {:else} -
๐Ÿ“ค
-

Drag and drop a file here

-

or

- - - {#if isLoading} -

Uploading...

- {/if} - {/if} -
-
- - -
-

Or Select Existing File

- {#if selectableFiles.filter(file => !file.signatures || file.signatures.length === 0).length === 0} -

No unused files available

- {:else} -
- {#each selectableFiles.filter(file => !file.signatures || file.signatures.length === 0) as file} - - {/each} -
- {/if} -
- - - {#if selectedFile || uploadedFile} -
-
- - -

Give this signature container a descriptive name

-
-
- - -
-
- {/if} - -
- -
-
- {/if} - - - {#if currentStep === 2} -
-

Invite Signees

- - -
-
- {getFileIcon(selectedFile?.mimeType || '')} -
-

{selectedFile?.name}

-

{formatFileSize(selectedFile?.size || 0)}

-
-
-
- - -
- - - {#if searchResults.length > 0} -
- {#each searchResults as user} - - {/each} -
- {/if} -
- - -
-

- Selected Signees ({selectedUsers.length}) -

- {#if selectedUsers.length === 0} -

- No additional signees. You can skip this step to create a self-signed document. -

- {:else} -
- {#each selectedUsers as user} -
-
-
- - {user.name?.[0] || user.ename?.[0] || '?'} - -
-
-

{user.name || 'No name'}

-

{user.ename}

-
-
- -
- {/each} -
- {/if} -
- -
- -
- - -
-
-
- {/if} - - - {#if currentStep === 3} -
-

Review & Confirm

- - -
-

File

-
- {getFileIcon(selectedFile?.mimeType || '')} -
-

{selectedFile?.name}

-

{formatFileSize(selectedFile?.size || 0)}

-
-
-
- - -
-

Signees ({selectedUsers.length + 1})

-
- -
-
- You -
-
-

You (Owner)

-

Automatically added as signee

-
-
- - {#each selectedUsers as user} -
-
- - {user.name?.[0] || user.ename?.[0] || '?'} - -
-
-

{user.name || 'No name'}

-

{user.ename}

-
-
- {/each} -
-
- - -
-

- Note: All signees will be notified automatically once the signing container is created. -

-
- -
- - -
-
- {/if} -
- +import { onMount } from "svelte"; +import { goto } from "$app/navigation"; +import { isAuthenticated } from "$lib/stores/auth"; +import { + files, + fetchFiles, + uploadFile, + FileSizeError, +} from "$lib/stores/files"; +import { apiClient } from "$lib/utils/axios"; +import { inviteSignees } from "$lib/stores/invitations"; + +let currentStep = $state(1); +let selectedFile = $state(null); +let uploadedFile = $state(null); +let searchQuery = $state(""); +let searchResults = $state([]); +let selectedUsers = $state([]); +let isLoading = $state(false); +let isSubmitting = $state(false); +let dragOver = $state(false); +let currentUserId = $state(null); +let displayName = $state(""); +let description = $state(""); +// All files available to select for a new container (includes File Managerโ€“only uploads) +let selectableFiles = $state([]); + +onMount(async () => { + isAuthenticated.subscribe((auth) => { + if (!auth) { + goto("/auth"); + } + }); + + // Get current user ID from API + try { + const response = await apiClient.get("/api/users"); + currentUserId = response.data.id; + } catch (err) { + console.error("Failed to get current user:", err); + } + + // Load all files for picker (list=all) so user can select any file, including those not yet used as containers + try { + const res = await apiClient.get("/api/files", { + params: { list: "all" }, + }); + selectableFiles = res.data ?? []; + } catch (err) { + console.error("Failed to load selectable files:", err); + } +}); + +async function handleFileUpload(file: File) { + try { + isLoading = true; + // Set default display name to file name if not set + const nameToUse = displayName.trim() || file.name; + const result = await uploadFile( + file, + nameToUse, + description.trim() || undefined, + ); + uploadedFile = file; + selectedFile = result; + // Update displayName if it was empty + if (!displayName.trim()) { + displayName = file.name; + } + } catch (err) { + console.error("Upload failed:", err); + if (err instanceof FileSizeError) { + alert(err.message); + } else { + alert("Failed to upload file. Please try again."); + } + throw err; + } finally { + isLoading = false; + } +} + +function handleFileSelect(event: Event) { + const target = event.target as HTMLInputElement; + if (target.files && target.files[0]) { + // Don't upload immediately - just store the file + uploadedFile = target.files[0]; + selectedFile = null; + // Reset display name and description for new upload + displayName = target.files[0].name; + description = ""; + } +} + +function handleDragOver(event: DragEvent) { + event.preventDefault(); + dragOver = true; +} + +function handleDragLeave() { + dragOver = false; +} + +function handleDrop(event: DragEvent) { + event.preventDefault(); + dragOver = false; + if (event.dataTransfer?.files && event.dataTransfer.files[0]) { + // Don't upload immediately - just store the file + uploadedFile = event.dataTransfer.files[0]; + selectedFile = null; + // Reset display name and description for new upload + displayName = event.dataTransfer.files[0].name; + description = ""; + } +} + +async function searchUsers() { + if (searchQuery.length < 2) { + searchResults = []; + return; + } + + try { + isLoading = true; + const response = await apiClient.get("/api/users/search", { + params: { query: searchQuery, limit: 10 }, + }); + searchResults = response.data; + } catch (err) { + console.error("Search failed:", err); + searchResults = []; + } finally { + isLoading = false; + } +} + +function addUser(user: any) { + // Don't allow user to invite themselves + if (currentUserId && user.id === currentUserId) { + alert( + "You cannot invite yourself. You are automatically added as a signee.", + ); + return; + } + + if (!selectedUsers.find((u) => u.id === user.id)) { + selectedUsers = [...selectedUsers, user]; + } + searchQuery = ""; + searchResults = []; +} + +function removeUser(userId: string) { + selectedUsers = selectedUsers.filter((u) => u.id !== userId); +} + +async function handleSubmit() { + if (!selectedFile) { + alert("Please select a file"); + return; + } + + try { + isSubmitting = true; + const userIds = selectedUsers.map((u) => u.id); + + // Backend will automatically add owner as signee + await inviteSignees(selectedFile.id, userIds); + + goto(`/files/${selectedFile.id}`); + } catch (err) { + console.error("Failed to create invitations:", err); + alert("Failed to send invitations"); + } finally { + isSubmitting = false; + } +} + +function formatFileSize(bytes: number): string { + if (bytes === 0) return "0 Bytes"; + const k = 1024; + const sizes = ["Bytes", "KB", "MB", "GB"]; + const i = Math.floor(Math.log(bytes) / Math.log(k)); + return ( + Number.parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i] + ); +} + +function getFileIcon(mimeType: string): string { + if (mimeType.startsWith("image/")) return "๐Ÿ–ผ๏ธ"; + if (mimeType === "application/pdf") return "๐Ÿ“„"; + if (mimeType.includes("word") || mimeType.includes("document")) return "๐Ÿ“"; + if (mimeType.includes("spreadsheet") || mimeType.includes("excel")) + return "๐Ÿ“Š"; + return "๐Ÿ“Ž"; +} From 1394dd3d2cb764b29719466c5d8b1d2d6eb0560f Mon Sep 17 00:00:00 2001 From: SoSweetHam Date: Wed, 28 Jan 2026 13:52:39 +0530 Subject: [PATCH 2/5] fix: readd svelte markup --- .../routes/(protected)/files/new/+page.svelte | 775 +++++++++++++----- 1 file changed, 579 insertions(+), 196 deletions(-) diff --git a/platforms/esigner/src/routes/(protected)/files/new/+page.svelte b/platforms/esigner/src/routes/(protected)/files/new/+page.svelte index 6ab61a5f6..26ea6d7e5 100644 --- a/platforms/esigner/src/routes/(protected)/files/new/+page.svelte +++ b/platforms/esigner/src/routes/(protected)/files/new/+page.svelte @@ -1,196 +1,579 @@ -import { onMount } from "svelte"; -import { goto } from "$app/navigation"; -import { isAuthenticated } from "$lib/stores/auth"; -import { - files, - fetchFiles, - uploadFile, - FileSizeError, -} from "$lib/stores/files"; -import { apiClient } from "$lib/utils/axios"; -import { inviteSignees } from "$lib/stores/invitations"; - -let currentStep = $state(1); -let selectedFile = $state(null); -let uploadedFile = $state(null); -let searchQuery = $state(""); -let searchResults = $state([]); -let selectedUsers = $state([]); -let isLoading = $state(false); -let isSubmitting = $state(false); -let dragOver = $state(false); -let currentUserId = $state(null); -let displayName = $state(""); -let description = $state(""); -// All files available to select for a new container (includes File Managerโ€“only uploads) -let selectableFiles = $state([]); - -onMount(async () => { - isAuthenticated.subscribe((auth) => { - if (!auth) { - goto("/auth"); - } - }); - - // Get current user ID from API - try { - const response = await apiClient.get("/api/users"); - currentUserId = response.data.id; - } catch (err) { - console.error("Failed to get current user:", err); - } - - // Load all files for picker (list=all) so user can select any file, including those not yet used as containers - try { - const res = await apiClient.get("/api/files", { - params: { list: "all" }, - }); - selectableFiles = res.data ?? []; - } catch (err) { - console.error("Failed to load selectable files:", err); - } -}); - -async function handleFileUpload(file: File) { - try { - isLoading = true; - // Set default display name to file name if not set - const nameToUse = displayName.trim() || file.name; - const result = await uploadFile( - file, - nameToUse, - description.trim() || undefined, - ); - uploadedFile = file; - selectedFile = result; - // Update displayName if it was empty - if (!displayName.trim()) { - displayName = file.name; - } - } catch (err) { - console.error("Upload failed:", err); - if (err instanceof FileSizeError) { - alert(err.message); - } else { - alert("Failed to upload file. Please try again."); - } - throw err; - } finally { - isLoading = false; - } -} - -function handleFileSelect(event: Event) { - const target = event.target as HTMLInputElement; - if (target.files && target.files[0]) { - // Don't upload immediately - just store the file - uploadedFile = target.files[0]; - selectedFile = null; - // Reset display name and description for new upload - displayName = target.files[0].name; - description = ""; - } -} - -function handleDragOver(event: DragEvent) { - event.preventDefault(); - dragOver = true; -} - -function handleDragLeave() { - dragOver = false; -} - -function handleDrop(event: DragEvent) { - event.preventDefault(); - dragOver = false; - if (event.dataTransfer?.files && event.dataTransfer.files[0]) { - // Don't upload immediately - just store the file - uploadedFile = event.dataTransfer.files[0]; - selectedFile = null; - // Reset display name and description for new upload - displayName = event.dataTransfer.files[0].name; - description = ""; - } -} - -async function searchUsers() { - if (searchQuery.length < 2) { - searchResults = []; - return; - } - - try { - isLoading = true; - const response = await apiClient.get("/api/users/search", { - params: { query: searchQuery, limit: 10 }, - }); - searchResults = response.data; - } catch (err) { - console.error("Search failed:", err); - searchResults = []; - } finally { - isLoading = false; - } -} - -function addUser(user: any) { - // Don't allow user to invite themselves - if (currentUserId && user.id === currentUserId) { - alert( - "You cannot invite yourself. You are automatically added as a signee.", - ); - return; - } - - if (!selectedUsers.find((u) => u.id === user.id)) { - selectedUsers = [...selectedUsers, user]; - } - searchQuery = ""; - searchResults = []; -} - -function removeUser(userId: string) { - selectedUsers = selectedUsers.filter((u) => u.id !== userId); -} - -async function handleSubmit() { - if (!selectedFile) { - alert("Please select a file"); - return; - } - - try { - isSubmitting = true; - const userIds = selectedUsers.map((u) => u.id); - - // Backend will automatically add owner as signee - await inviteSignees(selectedFile.id, userIds); - - goto(`/files/${selectedFile.id}`); - } catch (err) { - console.error("Failed to create invitations:", err); - alert("Failed to send invitations"); - } finally { - isSubmitting = false; - } -} - -function formatFileSize(bytes: number): string { - if (bytes === 0) return "0 Bytes"; - const k = 1024; - const sizes = ["Bytes", "KB", "MB", "GB"]; - const i = Math.floor(Math.log(bytes) / Math.log(k)); - return ( - Number.parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i] - ); -} - -function getFileIcon(mimeType: string): string { - if (mimeType.startsWith("image/")) return "๐Ÿ–ผ๏ธ"; - if (mimeType === "application/pdf") return "๐Ÿ“„"; - if (mimeType.includes("word") || mimeType.includes("document")) return "๐Ÿ“"; - if (mimeType.includes("spreadsheet") || mimeType.includes("excel")) - return "๐Ÿ“Š"; - return "๐Ÿ“Ž"; -} + + +
+ +
+ + + + + Back to Signature Containers + +

New Signature Container

+
+ +
+
+
+
= 1 ? 'bg-blue-600 text-white' : 'bg-gray-200 text-gray-600' + }`}> + 1 +
+ = 1 ? 'text-blue-600 font-medium' : 'text-gray-600'}>Upload File +
+
+
= 2 ? 'bg-blue-600' : ''}`} style="width: {currentStep >= 2 ? '100%' : '0%'}">
+
+
+
= 2 ? 'bg-blue-600 text-white' : 'bg-gray-200 text-gray-600' + }`}> + 2 +
+ = 2 ? 'text-blue-600 font-medium' : 'text-gray-600'}>Invite Signees +
+
+
= 3 ? 'bg-blue-600' : ''}`} style="width: {currentStep >= 3 ? '100%' : '0%'}">
+
+
+
= 3 ? 'bg-blue-600 text-white' : 'bg-gray-200 text-gray-600' + }`}> + 3 +
+ = 3 ? 'text-blue-600 font-medium' : 'text-gray-600'}>Review +
+
+
+ + + {#if currentStep === 1} +
+

Upload File

+ + +
+

Upload New File

+
+ {#if uploadedFile} +
+
{getFileIcon(uploadedFile.type)}
+

{uploadedFile.name}

+

{formatFileSize(uploadedFile.size)}

+
+ + {:else} +
๐Ÿ“ค
+

Drag and drop a file here

+

or

+ + + {#if isLoading} +

Uploading...

+ {/if} + {/if} +
+
+ + +
+

Or Select Existing File

+ {#if selectableFiles.filter(file => !file.signatures || file.signatures.length === 0).length === 0} +

No unused files available

+ {:else} +
+ {#each selectableFiles.filter(file => !file.signatures || file.signatures.length === 0) as file} + + {/each} +
+ {/if} +
+ + + {#if selectedFile || uploadedFile} +
+
+ + +

Give this signature container a descriptive name

+
+
+ + +
+
+ {/if} + +
+ +
+
+ {/if} + + + {#if currentStep === 2} +
+

Invite Signees

+ + +
+
+ {getFileIcon(selectedFile?.mimeType || '')} +
+

{selectedFile?.name}

+

{formatFileSize(selectedFile?.size || 0)}

+
+
+
+ + +
+ + + {#if searchResults.length > 0} +
+ {#each searchResults as user} + + {/each} +
+ {/if} +
+ + +
+

+ Selected Signees ({selectedUsers.length}) +

+ {#if selectedUsers.length === 0} +

+ No additional signees. You can skip this step to create a self-signed document. +

+ {:else} +
+ {#each selectedUsers as user} +
+
+
+ + {user.name?.[0] || user.ename?.[0] || '?'} + +
+
+

{user.name || 'No name'}

+

{user.ename}

+
+
+ +
+ {/each} +
+ {/if} +
+ +
+ +
+ + +
+
+
+ {/if} + + + {#if currentStep === 3} +
+

Review & Confirm

+ + +
+

File

+
+ {getFileIcon(selectedFile?.mimeType || '')} +
+

{selectedFile?.name}

+

{formatFileSize(selectedFile?.size || 0)}

+
+
+
+ + +
+

Signees ({selectedUsers.length + 1})

+
+ +
+
+ You +
+
+

You (Owner)

+

Automatically added as signee

+
+
+ + {#each selectedUsers as user} +
+
+ + {user.name?.[0] || user.ename?.[0] || '?'} + +
+
+

{user.name || 'No name'}

+

{user.ename}

+
+
+ {/each} +
+
+ + +
+

+ Note: All signees will be notified automatically once the signing container is created. +

+
+ +
+ + +
+
+ {/if} +
+ From 67e38e9b0bb9cad1c3e6d7bbd67a603561f6999a Mon Sep 17 00:00:00 2001 From: SoSweetHam Date: Wed, 28 Jan 2026 14:05:59 +0530 Subject: [PATCH 3/5] fix: coderabbit suggestions --- platforms/esigner-api/src/controllers/FileController.ts | 2 +- platforms/esigner-api/src/index.ts | 4 ++-- platforms/esigner/src/lib/stores/files.ts | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/platforms/esigner-api/src/controllers/FileController.ts b/platforms/esigner-api/src/controllers/FileController.ts index 7315348f6..9483322c6 100644 --- a/platforms/esigner-api/src/controllers/FileController.ts +++ b/platforms/esigner-api/src/controllers/FileController.ts @@ -3,7 +3,7 @@ import { FileService, ReservedFileNameError } from "../services/FileService"; import multer from "multer"; const upload = multer({ - limits: { fileSize: 1024 * 1024 * 1024 }, // 1GB limit + limits: { fileSize: 10 * 1024 * 1024 }, // 10MB limit storage: multer.memoryStorage(), }); diff --git a/platforms/esigner-api/src/index.ts b/platforms/esigner-api/src/index.ts index 101592e99..bddf6cc81 100644 --- a/platforms/esigner-api/src/index.ts +++ b/platforms/esigner-api/src/index.ts @@ -61,8 +61,8 @@ app.use( credentials: true, }), ); -app.use(express.json({ limit: "1gb" })); -app.use(express.urlencoded({ limit: "1gb", extended: true })); +app.use(express.json({ limit: "10mb" })); +app.use(express.urlencoded({ limit: "10mb", extended: true })); // Controllers const authController = new AuthController(); diff --git a/platforms/esigner/src/lib/stores/files.ts b/platforms/esigner/src/lib/stores/files.ts index 4de6f4af3..d1c986bae 100644 --- a/platforms/esigner/src/lib/stores/files.ts +++ b/platforms/esigner/src/lib/stores/files.ts @@ -65,7 +65,7 @@ export const fetchDocuments = async () => { // Keep fetchFiles alias for backward compatibility export const fetchFiles = fetchDocuments; -const MAX_FILE_SIZE = 1024 * 1024 * 1024; // 1GB limit +const MAX_FILE_SIZE = 10 * 1024 * 1024; // 10MB limit export class FileSizeError extends Error { constructor(public fileSize: number, public maxSize: number = MAX_FILE_SIZE) { From ae6b7d20876251353d7dc49bb755a9d6fd98e29e Mon Sep 17 00:00:00 2001 From: SoSweetHam Date: Wed, 28 Jan 2026 14:09:01 +0530 Subject: [PATCH 4/5] fix: limit to 20 megs --- platforms/esigner-api/src/controllers/FileController.ts | 4 ++-- platforms/esigner-api/src/index.ts | 8 ++++---- platforms/esigner/src/lib/stores/files.ts | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/platforms/esigner-api/src/controllers/FileController.ts b/platforms/esigner-api/src/controllers/FileController.ts index 9483322c6..b96f028c8 100644 --- a/platforms/esigner-api/src/controllers/FileController.ts +++ b/platforms/esigner-api/src/controllers/FileController.ts @@ -3,7 +3,7 @@ import { FileService, ReservedFileNameError } from "../services/FileService"; import multer from "multer"; const upload = multer({ - limits: { fileSize: 10 * 1024 * 1024 }, // 10MB limit + limits: { fileSize: 20 * 1024 * 1024 }, // 20MB limit storage: multer.memoryStorage(), }); @@ -200,7 +200,7 @@ export class FileController { } const signatures = await this.fileService.getFileSignatures(fileId); - + res.json(signatures.map(sig => ({ id: sig.id, userId: sig.userId, diff --git a/platforms/esigner-api/src/index.ts b/platforms/esigner-api/src/index.ts index bddf6cc81..1f41d4a9f 100644 --- a/platforms/esigner-api/src/index.ts +++ b/platforms/esigner-api/src/index.ts @@ -24,12 +24,12 @@ AppDataSource.initialize() .then(async () => { console.log("Database connection established"); console.log("Web3 adapter initialized"); - + // Initialize platform eVault for eSigner try { const platformService = PlatformEVaultService.getInstance(); const exists = await platformService.checkPlatformEVaultExists(); - + if (!exists) { console.log("๐Ÿ”ง Creating platform eVault for eSigner..."); const result = await platformService.createPlatformEVault(); @@ -61,8 +61,8 @@ app.use( credentials: true, }), ); -app.use(express.json({ limit: "10mb" })); -app.use(express.urlencoded({ limit: "10mb", extended: true })); +app.use(express.json({ limit: "20mb" })); +app.use(express.urlencoded({ limit: "20mb", extended: true })); // Controllers const authController = new AuthController(); diff --git a/platforms/esigner/src/lib/stores/files.ts b/platforms/esigner/src/lib/stores/files.ts index d1c986bae..570eef2d7 100644 --- a/platforms/esigner/src/lib/stores/files.ts +++ b/platforms/esigner/src/lib/stores/files.ts @@ -65,7 +65,7 @@ export const fetchDocuments = async () => { // Keep fetchFiles alias for backward compatibility export const fetchFiles = fetchDocuments; -const MAX_FILE_SIZE = 10 * 1024 * 1024; // 10MB limit +const MAX_FILE_SIZE = 20 * 1024 * 1024; // 20MB limit export class FileSizeError extends Error { constructor(public fileSize: number, public maxSize: number = MAX_FILE_SIZE) { From cb81d95cc04b5b60cb27671f1274fbb5a5552988 Mon Sep 17 00:00:00 2001 From: SoSweetHam Date: Wed, 28 Jan 2026 14:25:12 +0530 Subject: [PATCH 5/5] fix: express req body size 20M --- .../src/controllers/FileController.ts | 4 +++- platforms/esigner-api/src/index.ts | 24 ++++++++++++++++++- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/platforms/esigner-api/src/controllers/FileController.ts b/platforms/esigner-api/src/controllers/FileController.ts index b96f028c8..1fbea406a 100644 --- a/platforms/esigner-api/src/controllers/FileController.ts +++ b/platforms/esigner-api/src/controllers/FileController.ts @@ -2,8 +2,10 @@ import { Request, Response } from "express"; import { FileService, ReservedFileNameError } from "../services/FileService"; import multer from "multer"; +export const MAX_FILE_SIZE = 20 * 1024 * 1024; // 20MB limit + const upload = multer({ - limits: { fileSize: 20 * 1024 * 1024 }, // 20MB limit + limits: { fileSize: MAX_FILE_SIZE }, storage: multer.memoryStorage(), }); diff --git a/platforms/esigner-api/src/index.ts b/platforms/esigner-api/src/index.ts index 1f41d4a9f..746a817a4 100644 --- a/platforms/esigner-api/src/index.ts +++ b/platforms/esigner-api/src/index.ts @@ -5,7 +5,8 @@ import { config } from "dotenv"; import { AppDataSource } from "./database/data-source"; import path from "path"; import { AuthController } from "./controllers/AuthController"; -import { FileController } from "./controllers/FileController"; +import { FileController, MAX_FILE_SIZE } from "./controllers/FileController"; +import multer from "multer"; import { InvitationController } from "./controllers/InvitationController"; import { SignatureController } from "./controllers/SignatureController"; import { UserController } from "./controllers/UserController"; @@ -105,6 +106,27 @@ app.post("/api/signatures/session", authGuard, signatureController.createSigning app.get("/api/signatures/session/:id", signatureController.getSigningSessionStatus); app.post("/api/signatures/callback", signatureController.handleSignedPayload); +// Global error handler for multer file size errors +app.use((err: Error, req: express.Request, res: express.Response, next: express.NextFunction) => { + if (err instanceof multer.MulterError && err.code === "LIMIT_FILE_SIZE") { + const maxSizeMB = Math.round(MAX_FILE_SIZE / (1024 * 1024)); + return res.status(413).json({ + error: `File size exceeds the maximum limit of ${maxSizeMB} MB`, + code: "LIMIT_FILE_SIZE", + maxSize: MAX_FILE_SIZE, + }); + } + // Handle other multer errors + if (err instanceof multer.MulterError) { + return res.status(400).json({ + error: err.message, + code: err.code, + }); + } + // Pass other errors to the default handler + next(err); +}); + // Start server app.listen(port, () => { console.log(`eSigner API server running on port ${port}`);