tinfo-web-server / middleware /uploadMiddleware.js
Faridaqurr
initial backend
e8c33fa
Raw
History Blame Contribute Delete
2.78 kB
const path = require("path");
const multer = require("multer");
const IMAGE_EXTENSIONS = new Set(["jpg", "jpeg", "png", "webp"]);
const IMAGE_MIME_TYPES = new Set([
"image/jpeg",
"image/jpg",
"image/png",
"image/x-png",
"image/webp",
]);
const DOCUMENT_EXTENSIONS = new Set([
"pdf",
"doc",
"docx",
"xls",
"xlsx",
"ppt",
"pptx",
"txt",
"csv",
]);
const DOCUMENT_MIME_TYPES = new Set([
"application/pdf",
"application/msword",
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
"application/vnd.ms-excel",
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
"application/vnd.ms-powerpoint",
"application/vnd.openxmlformats-officedocument.presentationml.presentation",
"text/plain",
"text/csv",
]);
const getFileKindChecker = (kind = "any") => {
return (file) => {
const ext = path.extname(file.originalname || "").toLowerCase().replace(".", "");
const mime = String(file.mimetype || "").toLowerCase();
// Both extension AND MIME must match; no fallback to block SVG and other bypass vectors.
const isImage = IMAGE_EXTENSIONS.has(ext) && IMAGE_MIME_TYPES.has(mime);
const isDocument = DOCUMENT_EXTENSIONS.has(ext) && DOCUMENT_MIME_TYPES.has(mime);
if (kind === "image") return isImage;
if (kind === "document") return isDocument;
return isImage || isDocument;
};
};
const buildFileFilter = (kind) => {
const isAllowed = getFileKindChecker(kind);
return (req, file, cb) => {
if (isAllowed(file)) {
cb(null, true);
return;
}
if (kind === "image") {
const err = new Error("Only JPG, JPEG, PNG, and WEBP image files are allowed");
err.statusCode = 400;
cb(err);
return;
}
if (kind === "document") {
const err = new Error("Only supported document files are allowed");
err.statusCode = 400;
cb(err);
return;
}
const err = new Error("Unsupported file type");
err.statusCode = 400;
cb(err);
};
};
const createUpload = (options = {}) => {
const {
fileSize = 50 * 1024 * 1024,
kind = "any",
} = options;
return multer({
storage: multer.memoryStorage(),
limits: {
fileSize,
fields: 20,
fieldNameSize: 100,
fieldSize: 1 * 1024 * 1024,
},
fileFilter: buildFileFilter(kind),
});
};
const sharp = require("sharp");
const createImageValidator = () => async (req, res, next) => {
if (!req.file) return next();
try {
await sharp(req.file.buffer).metadata();
next();
} catch {
return res.status(400).json({ message: "File is not a valid image" });
}
};
module.exports = createUpload;
module.exports.createUpload = createUpload;
module.exports.createImageValidator = createImageValidator;