diff --git a/src/controllers/03-employee-controller.ts b/src/controllers/03-employee-controller.ts index 3c2d81e..bc2c6be 100644 --- a/src/controllers/03-employee-controller.ts +++ b/src/controllers/03-employee-controller.ts @@ -17,7 +17,6 @@ import { RequestWithUser } from "../interfaces/user"; import prisma from "../db"; import HttpStatus from "../interfaces/http-status"; import HttpError from "../interfaces/http-error"; -import minio, { presignedGetObjectIfExist } from "../services/minio"; import { isSystem } from "../utils/keycloak"; import { filterStatus } from "../services/prisma"; import { @@ -26,13 +25,13 @@ import { createPermCondition, } from "../services/permission"; import { connectOrDisconnect, connectOrNot } from "../utils/relation"; -import { throwRelationError } from "../utils/error"; +import { throwNotFound, throwRelationError } from "../utils/error"; +import { deleteFile, fileLocation, getFile, listFile, setFile } from "../utils/minio"; if (!process.env.MINIO_BUCKET) { throw Error("Require MinIO bucket."); } -const MINIO_BUCKET = process.env.MINIO_BUCKET; const MANAGE_ROLES = [ "system", "head_of_admin", @@ -50,14 +49,6 @@ function globalAllow(user: RequestWithUser["user"]) { const permissionCond = createPermCondition(globalAllow); const permissionCheck = createPermCheck(globalAllow); -function imageLocation(id: string) { - return `employee/${id}/profile-image`; -} - -function attachmentLocation(id: string, filename?: string) { - return `employee/${id}/attachment/${filename || ""}`; -} - type EmployeeCreate = { customerBranchId: string; @@ -364,17 +355,9 @@ export class EmployeeController extends Controller { where: { id: employeeId }, }); - if (!record) { - throw new HttpError(HttpStatus.NOT_FOUND, "Employee cannot be found.", "employeeNotFound"); - } + if (!record) return throwNotFound("Employee"); - return Object.assign(record, { - profileImageUrl: await presignedGetObjectIfExist( - MINIO_BUCKET, - imageLocation(employeeId), - 12 * 60 * 60, - ), - }); + return record; } @Post() @@ -750,9 +733,7 @@ export class EmployeeController extends Controller { }, }); - if (!record) { - throw new HttpError(HttpStatus.NOT_FOUND, "Employee cannot be found.", "employeeNotFound"); - } + if (!record) return throwNotFound("Employee"); await permissionCheck(req.user, record.customerBranch.customer.registeredBranch); @@ -778,114 +759,105 @@ export class EmployeeController extends Controller { where: { masterId: employeeId }, }); } - - @Get("{employeeId}/image") - async getImageByEmployeeId(@Request() req: RequestWithUser, @Path() employeeId: string) { - const url = await presignedGetObjectIfExist(MINIO_BUCKET, imageLocation(employeeId), 60 * 60); - - if (!url) { - throw new HttpError(HttpStatus.NOT_FOUND, "Image cannot be found", "imageNotFound"); - } - - return req.res?.redirect(url); - } - - @Put("{employeeId}/image") - @Security("keycloak", MANAGE_ROLES) - async setImageByEmployeeId(@Request() req: RequestWithUser, @Path() employeeId: string) { - const record = await prisma.employee.findFirst({ - where: { id: employeeId }, - }); - - if (!record) { - throw new HttpError(HttpStatus.NOT_FOUND, "Employee cannot be found.", "employeeNotFound"); - } - - return req.res?.redirect( - await minio.presignedPutObject(MINIO_BUCKET, imageLocation(employeeId), 12 * 60 * 60), - ); - } } -@Route("api/v1/employee/{employeeId}/attachment") -@Tags("Employee") -@Security("keycloak") -export class EmployeeAttachmentController extends Controller { - @Get() - async listAttachment(@Path() employeeId: string) { - const record = await prisma.employee.findFirst({ - where: { id: employeeId }, +@Route("api/v1/employee/{employeeId}") +@Tags("Branch") +export class EmployeeFileController extends Controller { + private async checkPermission(user: RequestWithUser["user"], id: string) { + const data = await prisma.employee.findFirst({ + where: { id }, + include: { + customerBranch: { + include: { + customer: { + include: { + registeredBranch: { + include: branchRelationPermInclude(user), + }, + }, + }, + }, + }, + }, }); - - if (!record) { - throw new HttpError(HttpStatus.NOT_FOUND, "Employee cannot be found.", "employeeNotFound"); - } - - const list = await new Promise((resolve, reject) => { - const item: string[] = []; - - const stream = minio.listObjectsV2(MINIO_BUCKET, attachmentLocation(employeeId)); - - stream.on("data", (v) => v && v.name && item.push(v.name)); - stream.on("end", () => resolve(item)); - stream.on("error", () => reject(new Error("MinIO error."))); - }); - - return list.map((v) => v.split("/").at(-1) as string); + if (!data) return throwNotFound("Employee"); + await permissionCheck(user, data.customerBranch.customer.registeredBranch); } - @Get("{filename}") - async getAttachment(@Path() employeeId: string, @Path() filename: string) { - const record = await prisma.employee.findFirst({ - where: { id: employeeId }, - }); - - if (!record) { - throw new HttpError(HttpStatus.NOT_FOUND, "Employee cannot be found.", "employeeNotFound"); - } - - return await minio.presignedGetObject( - MINIO_BUCKET, - attachmentLocation(record.id, filename), - 12 * 60 * 60, - ); + @Get("image") + @Security("keycloak") + async listImage(@Request() req: RequestWithUser, @Path() employeeId: string) { + await this.checkPermission(req.user, employeeId); + return await listFile(fileLocation.employee.img(employeeId)); } - @Put("{filename}") - async addAttachment( + @Get("image/{name}") + async getImage( @Request() req: RequestWithUser, @Path() employeeId: string, - @Path() filename: string, + @Path() name: string, ) { - const record = await prisma.employee.findFirst({ - where: { id: employeeId }, - }); - - if (!record) { - throw new HttpError(HttpStatus.NOT_FOUND, "Employee cannot be found.", "employeeNotFound"); - } - - return req.res?.redirect( - await minio.presignedPutObject( - MINIO_BUCKET, - attachmentLocation(record.id, filename), - 12 * 60 * 60, - ), - ); + return req.res?.redirect(await getFile(fileLocation.employee.img(employeeId, name))); } - @Delete("{filename}") - async deleteAttachment(@Path() employeeId: string, @Path() filename: string) { - const record = await prisma.employee.findFirst({ - where: { id: employeeId }, - }); - - if (!record) { - throw new HttpError(HttpStatus.NOT_FOUND, "Employee cannot be found.", "employeeNotFound"); + @Put("image/{name}") + @Security("keycloak") + async putImage( + @Request() req: RequestWithUser, + @Path() employeeId: 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, employeeId); + return req.res?.redirect(await setFile(fileLocation.employee.img(employeeId, name))); + } - await minio.removeObject(MINIO_BUCKET, attachmentLocation(record.id, filename), { - forceDelete: true, - }); + @Delete("image/{name}") + @Security("keycloak") + async delImage( + @Request() req: RequestWithUser, + @Path() employeeId: string, + @Path() name: string, + ) { + await this.checkPermission(req.user, employeeId); + return await deleteFile(fileLocation.employee.img(employeeId, name)); + } + + @Get("attachment") + @Security("keycloak") + async listAttachment(@Request() req: RequestWithUser, @Path() employeeId: string) { + await this.checkPermission(req.user, employeeId); + return await listFile(fileLocation.employee.attachment(employeeId)); + } + + @Get("attachment/{name}") + @Security("keycloak") + async getAttachment(@Path() employeeId: string, @Path() name: string) { + return await getFile(fileLocation.employee.attachment(employeeId, name)); + } + + @Put("attachment/{name}") + @Security("keycloak") + async putAttachment( + @Request() req: RequestWithUser, + @Path() employeeId: string, + @Path() name: string, + ) { + await this.checkPermission(req.user, employeeId); + return req.res?.redirect(await setFile(fileLocation.employee.attachment(employeeId, name))); + } + + @Delete("attachment/{name}") + @Security("keycloak") + async delAttachment( + @Request() req: RequestWithUser, + @Path() employeeId: string, + @Path() name: string, + ) { + await this.checkPermission(req.user, employeeId); + return await deleteFile(fileLocation.employee.attachment(employeeId, name)); } }