Skip to content

Commit a57f65a

Browse files
committed
feat(upload.ts): add file validations
Add validations on file type and size
1 parent de00bf5 commit a57f65a

File tree

4 files changed

+227
-0
lines changed

4 files changed

+227
-0
lines changed

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
"cors": "^2.8.5",
5555
"ethers": "^6.12.2",
5656
"express": "^4.19.2",
57+
"file-type": "^19.6.0",
5758
"gql.tada": "^1.2.1",
5859
"graphql": "^16.8.1",
5960
"graphql-filter": "^1.1.5",
@@ -63,6 +64,7 @@
6364
"kysely": "^0.27.4",
6465
"lodash": "^4.17.21",
6566
"lru-cache": "^11.0.0",
67+
"mime-types": "^2.1.35",
6668
"multer": "1.4.5-lts.1",
6769
"node-cron": "^3.0.3",
6870
"pg": "^8.12.0",
@@ -96,6 +98,7 @@
9698
"@swc/cli": "^0.3.12",
9799
"@swc/core": "^1.4.15",
98100
"@types/body-parser": "^1.19.5",
101+
"@types/mime-types": "^2.1.4",
99102
"@types/multer": "^1.4.12",
100103
"@types/node-cron": "^3.0.11",
101104
"@types/pg": "^8.11.6",

pnpm-lock.yaml

Lines changed: 75 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/middleware/upload.ts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,62 @@
1+
import { Request } from "express";
2+
import { fileTypeFromBuffer } from "file-type";
3+
import { lookup } from "mime-types";
14
import multer from "multer";
25

6+
// Allowed file types
7+
const ALLOWED_MIMES = new Set([
8+
"image/jpeg",
9+
"image/png",
10+
"image/gif",
11+
"application/pdf",
12+
"text/plain",
13+
"application/json",
14+
]);
15+
16+
// File validation error types
17+
export enum FileValidationError {
18+
INVALID_TYPE = "INVALID_TYPE",
19+
CONTENT_MISMATCH = "CONTENT_MISMATCH",
20+
SIZE_EXCEEDED = "SIZE_EXCEEDED",
21+
}
22+
23+
// File validation middleware
24+
export const validateFile = async (
25+
req: Request,
26+
file: Express.Multer.File,
27+
): Promise<void> => {
28+
// 1. Check file size
29+
if (file.size > 10 * 1024 * 1024) {
30+
throw new Error(FileValidationError.SIZE_EXCEEDED);
31+
}
32+
33+
// 2. Verify mime type
34+
const detectedType = await fileTypeFromBuffer(file.buffer);
35+
const declaredMime = lookup(file.originalname) || file.mimetype;
36+
37+
if (!ALLOWED_MIMES.has(declaredMime)) {
38+
throw new Error(FileValidationError.INVALID_TYPE);
39+
}
40+
41+
// 3. Check if declared type matches actual content
42+
if (detectedType && detectedType.mime !== declaredMime) {
43+
throw new Error(FileValidationError.CONTENT_MISMATCH);
44+
}
45+
};
46+
47+
// Configure multer with validation
348
export const upload = multer({
449
limits: {
550
fileSize: 10 * 1024 * 1024, // 10MB
651
files: 5,
752
},
853
storage: multer.memoryStorage(),
54+
fileFilter: async (req, file, cb) => {
55+
try {
56+
await validateFile(req, file);
57+
cb(null, true);
58+
} catch (error) {
59+
cb(error as Error);
60+
}
61+
},
962
});

0 commit comments

Comments
 (0)