From 8e1a32dcf3d58bd0a84d2473b38983db9cdf0462 Mon Sep 17 00:00:00 2001 From: Methapon Metanipat Date: Tue, 10 Sep 2024 15:19:31 +0700 Subject: [PATCH] feat: image upload product service --- src/controllers/04-product-controller.ts | 53 ++++++++++++++++++++ src/controllers/04-service-controller.ts | 62 +++++++++++++++++++++--- src/utils/minio.ts | 4 ++ 3 files changed, 112 insertions(+), 7 deletions(-) diff --git a/src/controllers/04-product-controller.ts b/src/controllers/04-product-controller.ts index ebe445d..384b9c4 100644 --- a/src/controllers/04-product-controller.ts +++ b/src/controllers/04-product-controller.ts @@ -25,6 +25,7 @@ import { } from "../services/permission"; import { isSystem } from "../utils/keycloak"; import { filterStatus } from "../services/prisma"; +import { deleteFile, fileLocation, getFile, listFile, setFile } from "../utils/minio"; const MANAGE_ROLES = [ "system", @@ -340,3 +341,55 @@ export class ProductController extends Controller { }); } } + +@Route("api/v1/product/{productId}") +@Tags("Product") +export class ProductFileController extends Controller { + async checkPermission(user: RequestWithUser["user"], id: string) { + const data = await prisma.product.findUnique({ + include: { + productGroup: { + include: { + registeredBranch: { + include: branchRelationPermInclude(user), + }, + }, + }, + }, + where: { id }, + }); + if (!data) { + throw new HttpError(HttpStatus.NOT_FOUND, "Product cannot be found.", "productNotFound"); + } + await permissionCheck(user, data.productGroup.registeredBranch); + } + + @Get("image") + @Security("keycloak") + async listImage(@Request() req: RequestWithUser, @Path() productId: string) { + await this.checkPermission(req.user, productId); + return await listFile(fileLocation.product.img(productId)); + } + + @Get("image/{name}") + async getImage(@Request() req: RequestWithUser, @Path() productId: string, @Path() name: string) { + return req.res?.redirect(await getFile(fileLocation.product.img(productId, name))); + } + + @Put("image/{name}") + @Security("keycloak") + async putImage(@Request() req: RequestWithUser, @Path() productId: string, @Path() name: string) { + if (!req.headers["content-type"]?.startsWith("image/")) { + throw new HttpError(HttpStatus.BAD_REQUEST, "Not a valid image.", "notValidImage"); + } + await this.checkPermission(req.user, productId); + return req.res?.redirect(await setFile(fileLocation.product.img(productId, name))); + } + + @Delete("image/{name}") + @Security("keycloak") + async delImage(@Request() req: RequestWithUser, @Path() productId: string, @Path() name: string) { + await this.checkPermission(req.user, productId); + return await deleteFile(fileLocation.product.img(productId, name)); + } +} diff --git a/src/controllers/04-service-controller.ts b/src/controllers/04-service-controller.ts index af41446..025ab77 100644 --- a/src/controllers/04-service-controller.ts +++ b/src/controllers/04-service-controller.ts @@ -25,6 +25,8 @@ import { createPermCondition, } from "../services/permission"; import { filterStatus } from "../services/prisma"; +import { throwRelationError } from "../utils/error"; +import { deleteFile, fileLocation, getFile, listFile, setFile } from "../utils/minio"; const MANAGE_ROLES = [ "system", @@ -230,13 +232,7 @@ export class ServiceController extends Controller { }), ]); - if (!productGroup) { - throw new HttpError( - HttpStatus.BAD_REQUEST, - "Product Type cannot be found.", - "relationproductGroupNotFound", - ); - } + if (!productGroup) return throwRelationError("Product Type"); await permissionCheck(req.user, productGroup.registeredBranch); @@ -419,3 +415,55 @@ export class ServiceController extends Controller { }); } } + +@Route("api/v1/service/{serviceId}") +@Tags("Service") +export class ServiceFileController extends Controller { + async checkPermission(user: RequestWithUser["user"], id: string) { + const data = await prisma.service.findUnique({ + include: { + productGroup: { + include: { + registeredBranch: { + include: branchRelationPermInclude(user), + }, + }, + }, + }, + where: { id }, + }); + if (!data) { + throw new HttpError(HttpStatus.NOT_FOUND, "Service cannot be found.", "serviceNotFound"); + } + await permissionCheck(user, data.productGroup.registeredBranch); + } + + @Get("image") + @Security("keycloak") + async listImage(@Request() req: RequestWithUser, @Path() serviceId: string) { + await this.checkPermission(req.user, serviceId); + return await listFile(fileLocation.service.img(serviceId)); + } + + @Get("image/{name}") + async getImage(@Request() req: RequestWithUser, @Path() serviceId: string, @Path() name: string) { + return req.res?.redirect(await getFile(fileLocation.service.img(serviceId, name))); + } + + @Put("image/{name}") + @Security("keycloak") + async putImage(@Request() req: RequestWithUser, @Path() serviceId: string, @Path() name: string) { + if (!req.headers["content-type"]?.startsWith("image/")) { + throw new HttpError(HttpStatus.BAD_REQUEST, "Not a valid image.", "notValidImage"); + } + await this.checkPermission(req.user, serviceId); + return req.res?.redirect(await setFile(fileLocation.service.img(serviceId, name))); + } + + @Delete("image/{name}") + @Security("keycloak") + async delImage(@Request() req: RequestWithUser, @Path() serviceId: string, @Path() name: string) { + await this.checkPermission(req.user, serviceId); + return await deleteFile(fileLocation.service.img(serviceId, name)); + } +} diff --git a/src/utils/minio.ts b/src/utils/minio.ts index d89b0d2..66148c1 100644 --- a/src/utils/minio.ts +++ b/src/utils/minio.ts @@ -22,6 +22,10 @@ export async function getFile(path: string, exp = 60 * 60) { return await minio.presignedGetObject(MINIO_BUCKET, path, exp); } +export async function setFile(path: string, exp = 6 * 60 * 60) { + return await minio.presignedPutObject(MINIO_BUCKET, path, exp); +} + export async function deleteFile(path: string) { await minio.removeObject(MINIO_BUCKET, path, { forceDelete: true }); }