Skip to content

Commit 52822e5

Browse files
sosweethamcoodos
authored andcommitted
fix(e-signer): file upload error handling (#737)
* fix(e-signer): file upload error handling and coherent limits with file manager * fix: readd svelte markup * fix: coderabbit suggestions * fix: limit to 20 megs * fix: express req body size 20M
1 parent eded910 commit 52822e5

4 files changed

Lines changed: 72 additions & 13 deletions

File tree

platforms/esigner-api/src/controllers/FileController.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@ import { Request, Response } from "express";
22
import { FileService, ReservedFileNameError } from "../services/FileService";
33
import multer from "multer";
44

5+
export const MAX_FILE_SIZE = 20 * 1024 * 1024; // 20MB limit
6+
57
const upload = multer({
6-
limits: { fileSize: 10 * 1024 * 1024 }, // 10MB limit
8+
limits: { fileSize: MAX_FILE_SIZE },
79
storage: multer.memoryStorage(),
810
});
911

@@ -200,7 +202,7 @@ export class FileController {
200202
}
201203

202204
const signatures = await this.fileService.getFileSignatures(fileId);
203-
205+
204206
res.json(signatures.map(sig => ({
205207
id: sig.id,
206208
userId: sig.userId,

platforms/esigner-api/src/index.ts

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ import { config } from "dotenv";
55
import { AppDataSource } from "./database/data-source";
66
import path from "path";
77
import { AuthController } from "./controllers/AuthController";
8-
import { FileController } from "./controllers/FileController";
8+
import { FileController, MAX_FILE_SIZE } from "./controllers/FileController";
9+
import multer from "multer";
910
import { InvitationController } from "./controllers/InvitationController";
1011
import { SignatureController } from "./controllers/SignatureController";
1112
import { UserController } from "./controllers/UserController";
@@ -24,12 +25,12 @@ AppDataSource.initialize()
2425
.then(async () => {
2526
console.log("Database connection established");
2627
console.log("Web3 adapter initialized");
27-
28+
2829
// Initialize platform eVault for eSigner
2930
try {
3031
const platformService = PlatformEVaultService.getInstance();
3132
const exists = await platformService.checkPlatformEVaultExists();
32-
33+
3334
if (!exists) {
3435
console.log("🔧 Creating platform eVault for eSigner...");
3536
const result = await platformService.createPlatformEVault();
@@ -61,8 +62,8 @@ app.use(
6162
credentials: true,
6263
}),
6364
);
64-
app.use(express.json({ limit: "50mb" }));
65-
app.use(express.urlencoded({ limit: "50mb", extended: true }));
65+
app.use(express.json({ limit: "20mb" }));
66+
app.use(express.urlencoded({ limit: "20mb", extended: true }));
6667

6768
// Controllers
6869
const authController = new AuthController();
@@ -105,6 +106,27 @@ app.post("/api/signatures/session", authGuard, signatureController.createSigning
105106
app.get("/api/signatures/session/:id", signatureController.getSigningSessionStatus);
106107
app.post("/api/signatures/callback", signatureController.handleSignedPayload);
107108

109+
// Global error handler for multer file size errors
110+
app.use((err: Error, req: express.Request, res: express.Response, next: express.NextFunction) => {
111+
if (err instanceof multer.MulterError && err.code === "LIMIT_FILE_SIZE") {
112+
const maxSizeMB = Math.round(MAX_FILE_SIZE / (1024 * 1024));
113+
return res.status(413).json({
114+
error: `File size exceeds the maximum limit of ${maxSizeMB} MB`,
115+
code: "LIMIT_FILE_SIZE",
116+
maxSize: MAX_FILE_SIZE,
117+
});
118+
}
119+
// Handle other multer errors
120+
if (err instanceof multer.MulterError) {
121+
return res.status(400).json({
122+
error: err.message,
123+
code: err.code,
124+
});
125+
}
126+
// Pass other errors to the default handler
127+
next(err);
128+
});
129+
108130
// Start server
109131
app.listen(port, () => {
110132
console.log(`eSigner API server running on port ${port}`);

platforms/esigner/src/lib/stores/files.ts

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,25 @@ export const fetchDocuments = async () => {
6565
// Keep fetchFiles alias for backward compatibility
6666
export const fetchFiles = fetchDocuments;
6767

68+
const MAX_FILE_SIZE = 20 * 1024 * 1024; // 20MB limit
69+
70+
export class FileSizeError extends Error {
71+
constructor(public fileSize: number, public maxSize: number = MAX_FILE_SIZE) {
72+
const fileSizeMB = (fileSize / (1024 * 1024)).toFixed(2);
73+
const maxSizeMB = (maxSize / (1024 * 1024)).toFixed(0);
74+
super(`File size (${fileSizeMB} MB) exceeds the maximum limit of ${maxSizeMB} MB`);
75+
this.name = 'FileSizeError';
76+
}
77+
}
78+
6879
export const uploadFile = async (file: File, displayName?: string, description?: string) => {
80+
// Client-side file size validation
81+
if (file.size > MAX_FILE_SIZE) {
82+
const err = new FileSizeError(file.size, MAX_FILE_SIZE);
83+
error.set(err.message);
84+
throw err;
85+
}
86+
6987
try {
7088
isLoading.set(true);
7189
error.set(null);
@@ -84,7 +102,20 @@ export const uploadFile = async (file: File, displayName?: string, description?:
84102
});
85103
await fetchDocuments();
86104
return response.data;
87-
} catch (err) {
105+
} catch (err: unknown) {
106+
// Handle HTTP 413 Payload Too Large
107+
if (err && typeof err === 'object' && 'response' in err) {
108+
const axiosError = err as { response?: { status?: number; data?: { error?: string; maxSize?: number; fileSize?: number } } };
109+
if (axiosError.response?.status === 413) {
110+
const data = axiosError.response.data;
111+
const fileSizeErr = new FileSizeError(
112+
data?.fileSize || file.size,
113+
data?.maxSize || MAX_FILE_SIZE
114+
);
115+
error.set(fileSizeErr.message);
116+
throw fileSizeErr;
117+
}
118+
}
88119
error.set(err instanceof Error ? err.message : 'Failed to upload file');
89120
throw err;
90121
} finally {

platforms/esigner/src/routes/(protected)/files/new/+page.svelte

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import { onMount } from 'svelte';
33
import { goto } from '$app/navigation';
44
import { isAuthenticated } from '$lib/stores/auth';
5-
import { uploadFile } from '$lib/stores/files';
5+
import { files, fetchFiles, uploadFile, FileSizeError } from '$lib/stores/files';
66
import { apiClient } from '$lib/utils/axios';
77
import { inviteSignees } from '$lib/stores/invitations';
88
@@ -59,7 +59,11 @@
5959
}
6060
} catch (err) {
6161
console.error('Upload failed:', err);
62-
alert('Failed to upload file');
62+
if (err instanceof FileSizeError) {
63+
alert(err.message);
64+
} else {
65+
alert('Failed to upload file. Please try again.');
66+
}
6367
throw err;
6468
} finally {
6569
isLoading = false;
@@ -126,7 +130,7 @@
126130
alert('You cannot invite yourself. You are automatically added as a signee.');
127131
return;
128132
}
129-
133+
130134
if (!selectedUsers.find(u => u.id === user.id)) {
131135
selectedUsers = [...selectedUsers, user];
132136
}
@@ -147,10 +151,10 @@
147151
try {
148152
isSubmitting = true;
149153
const userIds = selectedUsers.map(u => u.id);
150-
154+
151155
// Backend will automatically add owner as signee
152156
await inviteSignees(selectedFile.id, userIds);
153-
157+
154158
goto(`/files/${selectedFile.id}`);
155159
} catch (err) {
156160
console.error('Failed to create invitations:', err);

0 commit comments

Comments
 (0)