From dc7bc15dc4cfc2bc2d40f8a7e5a9f7d7b43a54f8 Mon Sep 17 00:00:00 2001 From: patrickhag Date: Mon, 13 May 2024 18:48:14 +0200 Subject: [PATCH] [finishes #187354260] Created feature that will allow sellers to update their products in stock modified: src/controllers/productsController.ts modified: src/helpers/claudinary.ts new file: src/helpers/destroyImage.ts new file: src/helpers/extractImageId.ts new file: src/middlewares/handleFileUploads.ts modified: src/routes/productRoutes.ts --- src/controllers/productsController.ts | 70 +++++++++++++++++++++++++++ src/helpers/claudinary.ts | 22 +++++---- src/helpers/destroyImage.ts | 11 +++++ src/helpers/extractImageId.ts | 10 ++++ src/middlewares/handleFileUploads.ts | 10 ++++ src/routes/productRoutes.ts | 9 ++++ 6 files changed, 123 insertions(+), 9 deletions(-) create mode 100644 src/helpers/destroyImage.ts create mode 100644 src/helpers/extractImageId.ts create mode 100644 src/middlewares/handleFileUploads.ts diff --git a/src/controllers/productsController.ts b/src/controllers/productsController.ts index a1cb4805..3292113b 100644 --- a/src/controllers/productsController.ts +++ b/src/controllers/productsController.ts @@ -11,6 +11,8 @@ import sequelize from '../database/models'; import Notification from '../database/models/notification'; import User from '../database/models/user'; import { sendEmail } from '../helpers/send-email'; +import { destroyImage } from '../helpers/destroyImage'; +import { extractImageId } from '../helpers/extractImageId'; export const createProduct = async (req: Request, res: Response) => { try { @@ -70,6 +72,74 @@ export const createSize = async (req: Request, res: Response) => { } }; +export const updateProduct = async (req: Request, res: Response) => { + try { + const { productId } = req.params; + + const data = { + name: req.body.name, + description: req.body.description, + colors: req.body.colors, + }; + + // update images + if (req.files) { + const product = await Product.findByPk(productId); + const foundImages = product?.images; + + // delete already existing images + if (foundImages instanceof Array) { + for (let i = 0; i < foundImages.length; i++) { + const strImg = foundImages[i].toString(); + const imageId = extractImageId(strImg) as string; + await destroyImage(imageId); + product!.images = []; + } + } + + // update new images + const images: unknown = req.files; + const productImages = []; + if (images instanceof Array && images.length > 3) { + for (const image of images) { + const imageBuffer: Buffer = image.buffer; + const url = await uploadImage(imageBuffer); + productImages.push(url); + Product.update({ images: productImages }, { where: { id: productId } }); + } + } else { + return res.status(400).json({ + message: 'Product should have at least 4 images', + }); + } + } + // update product + Product.update(data, { where: { id: productId } }).then(() => { + res.status(200).json({ + ok: true, + message: 'Product updated successfully', + }); + }); + } catch (error) { + sendInternalErrorResponse(res, error); + } +}; + +// update size +export const updateSize = async (req: Request, res: Response) => { + try { + const { sizeId } = req.params; + const { size, price, discount, expiryDate } = req.body as SizeAttributes; + await Size.update({ size, price, discount, expiryDate }, { where: { id: sizeId } }); + res.status(200).json({ + ok: true, + message: 'Product size updated successfully', + }); + } catch (error) { + sendInternalErrorResponse(res, error); + } +}; + const checkSizeExistance = async (sizeId: string, res: Response) => { const isSizeExist = await Size.findByPk(sizeId); diff --git a/src/helpers/claudinary.ts b/src/helpers/claudinary.ts index c00482e5..f37d7af1 100644 --- a/src/helpers/claudinary.ts +++ b/src/helpers/claudinary.ts @@ -1,5 +1,5 @@ +import { File } from 'buffer'; import { v2 as cloudinary } from 'cloudinary'; -// const config = require("config"); cloudinary.config({ cloud_name: process.env.CLOUDINARY_NAME, @@ -10,15 +10,19 @@ cloudinary.config({ const uploadImage = async (imageData: Buffer): Promise => { const base64Image = imageData.toString('base64'); return new Promise((resolve, reject) => { - cloudinary.uploader.upload(`data:image/png;base64,${base64Image}`, { public_id: 'user_image' }, (error, result) => { - if (error) { - reject(error); - } else { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const url: any = result?.secure_url; - resolve(url); + cloudinary.uploader.upload( + `data:image/png;base64,${base64Image}`, + { public_id: String(Date.now()) }, + (error, result) => { + if (error) { + reject(error); + } else { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const url: any = result?.secure_url; + resolve(url); + } } - }); + ); }); }; export default uploadImage; diff --git a/src/helpers/destroyImage.ts b/src/helpers/destroyImage.ts new file mode 100644 index 00000000..2a5ce280 --- /dev/null +++ b/src/helpers/destroyImage.ts @@ -0,0 +1,11 @@ +import { v2 as cloudinary } from 'cloudinary'; + +cloudinary.config({ + cloud_name: process.env.CLOUDINARY_NAME, + api_key: process.env.CLOUDINARY_KEY, + api_secret: process.env.CLOUDINARY_SECRET, +}); + +export const destroyImage = async (public_id: string) => { + return await cloudinary.uploader.destroy(public_id); +}; diff --git a/src/helpers/extractImageId.ts b/src/helpers/extractImageId.ts new file mode 100644 index 00000000..9b94f5f2 --- /dev/null +++ b/src/helpers/extractImageId.ts @@ -0,0 +1,10 @@ +export const extractImageId = (url: string) => { + const regex = /\/([a-zA-Z0-9]+)\.[a-z]+$/; + const match = url.match(regex); + + if (match && match[1]) { + return match[1]; + } else { + return null; + } +}; diff --git a/src/middlewares/handleFileUploads.ts b/src/middlewares/handleFileUploads.ts new file mode 100644 index 00000000..e97ccc4b --- /dev/null +++ b/src/middlewares/handleFileUploads.ts @@ -0,0 +1,10 @@ +import { NextFunction, Request, Response } from 'express'; +import multerUpload from '../helpers/multer'; + +export const handleFileUploads = (req: Request, res: Response, next: NextFunction) => { + if (req.files) { + multerUpload.array('images', 8)(req, res, next); + } else { + multerUpload.single('image')(req, res, next); + } +}; diff --git a/src/routes/productRoutes.ts b/src/routes/productRoutes.ts index 64172a71..6b91450e 100644 --- a/src/routes/productRoutes.ts +++ b/src/routes/productRoutes.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-misused-promises */ import { Router } from 'express'; import { createProduct, @@ -7,6 +8,7 @@ import { getProductById, markProductAsAvailable, markProductAsUnavailable, + updateProduct, } from '../controllers/productsController'; import multerUpload from '../helpers/multer'; import { checkUserRoles, isAuthenticated } from '../middlewares/authMiddlewares'; @@ -21,6 +23,13 @@ router.post( createProduct ); +router.put( + '/:productId/update-product', + multerUpload.array('images', 8), + isAuthenticated, + checkUserRoles('seller'), + updateProduct +); router.post('/:productId/add-size', isAuthenticated, checkUserRoles('seller'), createSize); router.get('/', getAllProduct);