From 28e99c2a0dc7ce61ad40bc8e373a34e7ee2044b2 Mon Sep 17 00:00:00 2001 From: mhmmdafif Date: Tue, 2 Dec 2025 10:44:07 +0700 Subject: [PATCH] add: import , repair: change save local storage to imagekit --- controllers/sparepart.controller.js | 241 +++++++++++++++++++++------- middleware/upload.js | 5 + routes/sparepart.route.js | 13 +- 3 files changed, 202 insertions(+), 57 deletions(-) create mode 100644 middleware/upload.js diff --git a/controllers/sparepart.controller.js b/controllers/sparepart.controller.js index e0cf690..2f5880c 100644 --- a/controllers/sparepart.controller.js +++ b/controllers/sparepart.controller.js @@ -1,8 +1,15 @@ const ExcelJS = require("exceljs"); -const fs = require("fs"); const path = require("path"); +const ImageKit = require("imagekit"); +const imagekit = new ImageKit({ + publicKey: process.env.IMAGEKIT_PUBLIC_KEY, + privateKey: process.env.IMAGEKIT_PRIVATE_KEY, + urlEndpoint: process.env.IMAGEKIT_URL_ENDPOINT, +}); + const SparepartService = require("../services/sparepart.service"); + const { setResponse, setResponsePaging, @@ -38,25 +45,28 @@ class SparepartController { if (error) { return res.status(400).json(setResponse(error, "Validation failed", 400)); } + try { if (req.file) { - const file = req.file; - const ext = require("path").extname(file.originalname).toLowerCase(); - const typeDoc = ext === ".pdf" ? "PDF" : "IMAGE"; - const folder = typeDoc === "PDF" ? "pdf" : "images"; - const pathDocument = `${folder}/${file.filename}`; - value.sparepart_foto = pathDocument; + const upload = await imagekit.upload({ + file: req.file.buffer, + fileName: req.file.originalname, + folder: "/sparepart", + }); + + value.sparepart_foto = upload.url; } + value.userId = req.user.user_id; + const results = await SparepartService.createSparepart(value); - const response = await setResponse( - results, - "Sparepart created successfully" - ); - return res.status(response.statusCode).json(response); + return res + .status(201) + .json(setResponse(results, "Sparepart created successfully")); } catch (err) { - const response = setResponse([], err.message, err.statusCode || 500); - res.status(response.statusCode).json(response); + return res + .status(err.statusCode || 500) + .json(setResponse([], err.message, err.statusCode || 500)); } } @@ -66,25 +76,28 @@ class SparepartController { if (error) { return res.status(400).json(setResponse(error, "Validation failed", 400)); } + try { if (req.file) { - const file = req.file; - const ext = require("path").extname(file.originalname).toLowerCase(); - const typeDoc = ext === ".pdf" ? "PDF" : "IMAGE"; - const folder = typeDoc === "PDF" ? "pdf" : "images"; - const pathDocument = `${folder}/${file.filename}`; - value.sparepart_foto = pathDocument; + const upload = await imagekit.upload({ + file: req.file.buffer, + fileName: req.file.originalname, + folder: "/sparepart", + }); + + value.sparepart_foto = upload.url; } + value.userId = req.user.user_id; + const results = await SparepartService.updateSparepart(id, value); - const response = await setResponse( - results, - "Sparepart updated successfully" - ); - res.status(response.statusCode).json(response); + return res + .status(200) + .json(setResponse(results, "Sparepart updated successfully")); } catch (err) { - const response = setResponse([], err.message, err.statusCode || 500); - res.status(response.statusCode).json(response); + return res + .status(err.statusCode || 500) + .json(setResponse([], err.message, err.statusCode || 500)); } } @@ -113,16 +126,8 @@ class SparepartController { worksheet.addRows( results.data.map((item) => ({ - sparepart_name: item.sparepart_name, - sparepart_code: item.sparepart_code, - sparepart_qty: item.sparepart_qty, - sparepart_merk: item.sparepart_merk, - sparepart_model: item.sparepart_model, - sparepart_unit: item.sparepart_unit, - sparepart_stok: item.sparepart_stok, + ...item, sparepart_foto: "", - sparepart_item_type: item.sparepart_item_type, - created_at: item.created_at, })) ); @@ -132,27 +137,38 @@ class SparepartController { if (!item.sparepart_foto) continue; - let foto = item.sparepart_foto.trim(); + let imageUrl = item.sparepart_foto; - foto = foto.replace(/^images\//, ""); + if (!imageUrl.startsWith("https")) continue; - const fullPath = path.resolve(process.cwd(), "uploads", "images", foto); + let ext = path.extname(imageUrl).toLowerCase().replace(".", ""); - if (fs.existsSync(fullPath)) { - const ext = fullPath.split(".").pop().toLowerCase(); + if (!ext) ext = "jpg"; - const imageId = workbook.addImage({ - filename: fullPath, - extension: ext, - }); - - worksheet.addImage(imageId, { - tl: { col: 7, row: rowNumber - 1 }, - ext: { width: 80, height: 80 }, - }); - - worksheet.getRow(rowNumber).height = 70; + const supported = ["jpg", "jpeg", "png"]; + if (!supported.includes(ext)) { + ext = "png"; } + + let buffer; + try { + const resp = await fetch(imageUrl); + buffer = Buffer.from(await resp.arrayBuffer()); + } catch (e) { + continue; + } + + const imageId = workbook.addImage({ + buffer, + extension: ext === "jpg" ? "jpeg" : ext, + }); + + worksheet.addImage(imageId, { + tl: { col: 7, row: rowNumber - 1 }, + ext: { width: 80, height: 80 }, + }); + + worksheet.getRow(rowNumber).height = 70; } worksheet.getRow(1).eachCell((cell) => { @@ -173,10 +189,124 @@ class SparepartController { return res.send(buffer); } catch (error) { - console.log("Export Excel Error:", error); - return res.status(500).json({ - message: error.message, + return res.status(500).json({ message: error.message }); + } + } + + static async importExcel(req, res) { + try { + + const workbook = new ExcelJS.Workbook(); + await workbook.xlsx.load(req.file.buffer); + + const worksheet = workbook.getWorksheet(1); + + const images = worksheet.getImages(); + const imageMap = {}; + images.forEach((imgObj) => { + const imageId = imgObj.imageId; + const range = imgObj.range; + const row = range.tl.nativeRow + 1; + const image = workbook.getImage(imageId); + imageMap[row] = image; }); + + const spareparts = []; + + worksheet.eachRow({ includeEmpty: false }, (row, rowNumber) => { + if (rowNumber === 1) return; + + const [ + sparepart_name, + sparepart_code, + sparepart_description, + sparepart_qty_excel, + sparepart_merk, + sparepart_model, + sparepart_unit, + sparepart_stok_excel, + sparepart_foto_excel, + sparepart_item_type, + ] = row.values.slice(1); + + if (!sparepart_name) return; + + if (!sparepart_code) { + return; + } + + spareparts.push({ + sparepart_name: sparepart_name || "", + sparepart_code: sparepart_code || "", + sparepart_description: sparepart_description || "", + sparepart_qty: Number(sparepart_qty_excel) || 0, + sparepart_merk: sparepart_merk || "", + sparepart_model: sparepart_model || "", + sparepart_unit: sparepart_unit || "", + sparepart_stok: sparepart_stok_excel || "", + sparepart_foto: sparepart_foto_excel || "", + sparepart_item_type: sparepart_item_type || "", + rowNumber, + }); + }); + + if (spareparts.length === 0) { + return res + .status(400) + .json(setResponse([], "Tidak ada data valid untuk diimport", 400)); + } + + const results = []; + + for (const data of spareparts) { + let uploadedUrl = ""; + + try { + const image = imageMap[data.rowNumber]; + if (image) { + const fileName = `sparepart_${Date.now()}_${ + data.sparepart_code + }.jpg`; + const uploadResult = await imagekit.upload({ + file: image.buffer, + fileName: fileName, + folder: "/sparepart", + }); + + uploadedUrl = uploadResult.url; + } + } catch (err) { + err; + } + + data.sparepart_foto = uploadedUrl || ""; + + const { rowNumber, ...dbData } = data; + + const created = await SparepartService.createSparepart(dbData); + + if (created && created[0]) { + results.push({ + sparepart_id: created[0].sparepart_id, + sparepart_name: created[0].sparepart_name, + sparepart_code: created[0].sparepart_code, + sparepart_description: created[0].sparepart_description, + sparepart_qty: created[0].sparepart_qty, + sparepart_merk: created[0].sparepart_merk, + sparepart_model: created[0].sparepart_model, + sparepart_unit: created[0].sparepart_unit, + sparepart_stok: created[0].sparepart_stok, + sparepart_foto: created[0].sparepart_foto, + sparepart_item_type: created[0].sparepart_item_type, + }); + } + } + + return res.json( + setResponse(results, `${results.length} Sparepart berhasil diimport`) + ); + } catch (error) { + return res.status(500).json({ message: error.message }); } } @@ -193,4 +323,5 @@ class SparepartController { res.status(response.statusCode).json(response); } } + module.exports = SparepartController; diff --git a/middleware/upload.js b/middleware/upload.js new file mode 100644 index 0000000..1b21e11 --- /dev/null +++ b/middleware/upload.js @@ -0,0 +1,5 @@ +const multer = require("multer"); + +const storage = multer.memoryStorage(); + +module.exports = multer({ storage }); diff --git a/routes/sparepart.route.js b/routes/sparepart.route.js index c151538..9781a01 100644 --- a/routes/sparepart.route.js +++ b/routes/sparepart.route.js @@ -2,7 +2,8 @@ const express = require("express"); const SparepartController = require("../controllers/sparepart.controller"); const verifyToken = require("../middleware/verifyToken"); const verifyAccess = require("../middleware/verifyAccess"); -const upload = require("../middleware/uploads"); +const upload = require("../middleware/upload"); + const router = express.Router(); router.get( @@ -11,6 +12,14 @@ router.get( SparepartController.exportExcel ); +router.post( + "/import", + verifyToken.verifyAccessToken, + verifyAccess(), + upload.single("file"), + SparepartController.importExcel +); + router .route("/") .get(verifyToken.verifyAccessToken, SparepartController.getAll) @@ -36,4 +45,4 @@ router SparepartController.delete ); -module.exports = router; +module.exports = router; \ No newline at end of file