diff --git a/middlewares/upload.js b/middlewares/upload.js deleted file mode 100644 index 0b7cf25..0000000 --- a/middlewares/upload.js +++ /dev/null @@ -1,74 +0,0 @@ -import multer from "multer"; -import crypto from "crypto"; -import path from "path"; -import fs from "fs"; -import response from "../response.js"; - -const memoryStorage = multer.memoryStorage(); - -const fileFilter = (req, file, cb) => { - const ext = path.extname(file.originalname).toLowerCase(); - if (ext === ".png" || ext === ".jpg" || ext === ".jpeg") { - cb(null, true); - } else { - cb( - new Error( - "Invalid file type, only .png, .jpg, and .jpeg files are allowed!" - ), - false - ); - } -}; - -const upload = multer({ - storage: memoryStorage, - fileFilter, - limits: { fileSize: 5 * 1024 * 1024 }, // Limit file size to 5MB -}).fields([ - { name: "icon", maxCount: 1 }, - { name: "thumbnail", maxCount: 1 }, -]); - -const saveFileToDisk = (file) => { - const md5sum = crypto - .createHash("md5") - .update(Date.now().toString()) - .digest("hex"); - const ext = path.extname(file.originalname); - const filename = `${md5sum}${ext}`; - const filepath = path.join("public/uploads", filename); - fs.writeFileSync(filepath, file.buffer); - return filename; -}; - -const handleUpload = (req, res, next) => { - upload(req, res, (err) => { - if (err) { - return response(400, null, err.message, res); - } - - const files = req.files; - const icon = files?.icon ? files.icon[0] : null; - const thumbnail = files?.thumbnail ? files.thumbnail[0] : null; - - try { - // Validate icon and thumbnail before saving - if (icon && thumbnail) { - const iconFilename = saveFileToDisk(icon); - const thumbnailFilename = saveFileToDisk(thumbnail); - - // Update the filenames in the request object for further processing - req.body.icon = iconFilename; - req.body.thumbnail = thumbnailFilename; - - next(); - } else { - return response(400, null, "Both icon and thumbnail are required", res); - } - } catch (error) { - return response(500, null, "Internal Server Error", res); - } - }); -}; - -export default handleUpload; diff --git a/middlewares/uploadSubject.js b/middlewares/uploadSubject.js new file mode 100644 index 0000000..f58fc45 --- /dev/null +++ b/middlewares/uploadSubject.js @@ -0,0 +1,101 @@ +import multer from "multer"; +import crypto from "crypto"; +import path from "path"; +import fs from "fs"; +import response from "../response.js"; + +const memoryStorage = multer.memoryStorage(); + +const fileFilter = (req, file, cb) => { + const ext = path.extname(file.originalname).toLowerCase(); + if (ext === ".png" || ext === ".jpg" || ext === ".jpeg") { + cb(null, true); + } else { + cb( + new Error( + "Invalid file type, only .png, .jpg, and .jpeg files are allowed!" + ), + false + ); + } +}; + +const upload = multer({ + storage: memoryStorage, + fileFilter, + limits: { fileSize: 10 * 1024 * 1024 }, // Limit file size to 5MB +}).fields([ + { name: "icon", maxCount: 1 }, + { name: "thumbnail", maxCount: 1 }, +]); + +const handleUpload = (req, res, next) => { + upload(req, res, (err) => { + if (err) { + return response(400, null, err.message, res); + } + + const files = req.files; + const icon = files?.icon ? files.icon[0] : null; + const thumbnail = files?.thumbnail ? files.thumbnail[0] : null; + + try { + let validFiles = true; + let errorMessages = []; + + // Validate file sizes + if (icon && icon.size > 5 * 1024 * 1024) { + validFiles = false; + icon.buffer = null; + errorMessages.push("Icon file exceeds the size limit of 5MB"); + } + + if (thumbnail && thumbnail.size > 5 * 1024 * 1024) { + validFiles = false; + thumbnail.buffer = null; + errorMessages.push("Thumbnail file exceeds the size limit of 5MB"); + } + + if (validFiles) { + req.filesToSave = { icon, thumbnail }; + next(); + } else { + clearFileBuffers({ icon, thumbnail }); + return response(400, null, errorMessages.join(", "), res); + } + } catch (error) { + console.log(error); + clearFileBuffers({ icon, thumbnail }); + return response(500, null, "Internal Server Error", res); + } + }); +}; + +export const clearFileBuffers = (files) => { + for (const file of Object.values(files)) { + if (file && file.buffer) { + file.buffer = null; + } + } +}; + +export const saveFileToDisk = (file, fieldName, subjectName) => { + const ext = path.extname(file.originalname); + const hash = crypto + .createHash("md5") + .update(subjectName + file.originalname + file.buffer.length.toString()) + .digest("hex") + .slice(0, 8); + const filename = `${fieldName}-${hash}${ext}`; + const folderPath = path.join("public/uploads/subject"); + + if (!fs.existsSync(folderPath)) { + fs.mkdirSync(folderPath, { recursive: true }); + } + + const filepath = path.join(folderPath, filename); + fs.writeFileSync(filepath, file.buffer); + return filename; +}; + +export default handleUpload; diff --git a/public/uploads/d65fdab37a0cf11d22c7a8b08f727812.jpg b/public/uploads/d65fdab37a0cf11d22c7a8b08f727812.jpg deleted file mode 100644 index 72049ec..0000000 Binary files a/public/uploads/d65fdab37a0cf11d22c7a8b08f727812.jpg and /dev/null differ diff --git a/public/uploads/0025932b3fb82d7e19648e1ff9bbf7bc.png b/public/uploads/subject/Menulis-icon-2802b5de.png similarity index 100% rename from public/uploads/0025932b3fb82d7e19648e1ff9bbf7bc.png rename to public/uploads/subject/Menulis-icon-2802b5de.png diff --git a/public/uploads/subject/Menulis-thumbnail-448b4bd5.jpeg b/public/uploads/subject/Menulis-thumbnail-448b4bd5.jpeg new file mode 100644 index 0000000..953f28d Binary files /dev/null and b/public/uploads/subject/Menulis-thumbnail-448b4bd5.jpeg differ diff --git a/routes/subject.js b/routes/subject.js index b4f1174..9cff2de 100644 --- a/routes/subject.js +++ b/routes/subject.js @@ -1,5 +1,5 @@ import express from "express"; -import handleUpload from '../middlewares/upload.js'; +import handleUpload from '../middlewares/uploadSubject.js'; import { getSubjects, getSubjectById, createSubject, updateSubjectById, deleteSubjectById } from "../controllers/subject.js"; import { verifyLoginUser, adminOnly, teacherOnly } from "../middlewares/authUser.js";