import { Body, Controller, Delete, Get, Path, Post, Put, Route, Security, SuccessResponse, Tags, Request, Response, Example, } from "tsoa"; import minioClient from "../minio"; import esClient from "../elasticsearch"; import { copyCond, listFolder, listItem, pathExist, replaceIllegalChars } from "../utils/minio"; import HttpStatusCode from "../interfaces/http-status"; import { StorageFile, StorageFolder } from "../interfaces/storage-fs"; import HttpError from "../interfaces/http-error"; const DEFAULT_BUCKET = process.env.MINIO_BUCKET; const DEFAULT_INDEX = process.env.ELASTICSEARCH_INDEX; if (!DEFAULT_BUCKET) throw Error("Default MinIO bucket must be specified."); if (!DEFAULT_INDEX) throw Error("Default ElasticSearch index must be specified."); @Route("/cabinet/{cabinetName}/drawer") export class DrawerController extends Controller { /** * @example cabinetName "ตู้เอกสาร 1" */ @Get("/") @Tags("ลิ้นชัก") @Security("bearerAuth") @Response( HttpStatusCode.INTERNAL_SERVER_ERROR, "เกิดข้อผิดพลาด ไม่สามารถแสดงรายการลิ้นชักได้ กรุณาลองใหม่ในภายหลัง", ) @SuccessResponse(HttpStatusCode.OK, "สำเร็จ") @Example([ { path: "ตู้เอกสาร 1/ลิ้นชัก 1/", name: "ลิ้นชัก 1", createdAt: "2021-07-20T12:33:13.018Z", createdBy: "admin", }, { path: "ตู้เอกสาร 1/ลิ้นชัก 2/", name: "ลิ้นชัก 2", createdAt: "2022-01-23T16:05:02.114Z", createdBy: "admin", }, ]) public async listDrawer(@Path() cabinetName: string): Promise { const list = await listFolder(DEFAULT_BUCKET!, `${cabinetName}/`).catch((e) => console.error(`Error List Folder: ${e}`), ); if (!list) throw new Error("เกิดข้อผิดพลาด ไม่สามารถแสดงรายการลิ้นชักได้ กรุณาลองใหม่ในภายหลัง"); return list; } /** * @example cabinetName "ตู้เอกสาร 1" */ @Post("/") @Tags("ลิ้นชัก") @Security("bearerAuth", ["admin", "management-role"]) @Response(HttpStatusCode.NOT_FOUND, "ไม่พบลิ้นชัก") @Response(HttpStatusCode.INTERNAL_SERVER_ERROR, "เกิดข้อผิดพลาดกับระบบจัดการไฟล์") @SuccessResponse(HttpStatusCode.CREATED, "สำเร็จ") public async createDrawer( @Request() request: { user: { preferred_username: string } }, @Path() cabinetName: string, @Body() body: { /** * @example "ลิ้นชัก 1" */ name: string; }, ) { const basePath = `${cabinetName}/`; if (!(await pathExist(DEFAULT_BUCKET!, basePath))) { throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบตำแหน่งที่ต้องการสร้างลิ้นชัก"); } const created = await minioClient .putObject(DEFAULT_BUCKET!, `${basePath}${replaceIllegalChars(body.name)}/.keep`, "", 0, { createdAt: new Date().toISOString(), createdBy: request.user.preferred_username, }) .catch((e) => console.error(e)); if (!created) throw new Error("เกิดข้อผิดพลาดกับระบบจัดการไฟล์"); return this.setStatus(HttpStatusCode.CREATED); } /** * @example cabinetName "ตู้เอกสาร 1" * @example drawerName "ลิ้นชัก 1" */ @Put("/{drawerName}") @Tags("ลิ้นชัก") @Security("bearerAuth", ["admin", "management-role"]) @Response(HttpStatusCode.INTERNAL_SERVER_ERROR, "เกิดข้อผิดพลาดไม่สามารถย้ายไฟล์ได้") @SuccessResponse(HttpStatusCode.NO_CONTENT, "สำเร็จ") public async editDrawer( @Path() cabinetName: string, @Path() drawerName: string, @Body() body: { /** * @example "ลิ้นชักใหม่" */ name: string; }, ): Promise { const path = `${cabinetName}/${drawerName}/`; const list = await listItem(DEFAULT_BUCKET!, path, true); await Promise.all( list.map(async (current) => { if (!current.name) return; const base = `${cabinetName}/${replaceIllegalChars(body.name)}/`; const destination = `${base}${current.name.slice(path.length)}`; const source = `/${DEFAULT_BUCKET}/${current.name}`; return await minioClient .copyObject(DEFAULT_BUCKET!, destination, source, copyCond) .then(async () => { if (current.name.includes(".keep")) { return await minioClient.removeObject(DEFAULT_BUCKET!, current.name); } const search = await esClient.search< StorageFile & { attachment: Record } >({ index: DEFAULT_INDEX!, query: { match: { pathname: current.name } }, }); if (search && search.hits.hits.length === 0) throw new Error("ไม่พบข้อมูลในฐานข้อมูล"); const data = search.hits.hits[0]; await esClient.update({ index: DEFAULT_INDEX!, id: data._id, doc: { pathname: destination, path: destination.split("/").slice(0, -1).join("/") + "/", }, refresh: "wait_for", }); await minioClient.removeObject(DEFAULT_BUCKET!, current.name); }) .catch((e) => { console.error(e); throw new Error("เกิดข้อผิดพลาด ไม่สามารถย้ายไฟล์ได้"); }); }), ); return this.setStatus(HttpStatusCode.NO_CONTENT); } /** * @example cabinetName "ตู้เอกสาร 1" * @example drawerName "ลิ้นชัก 1" */ @Delete("/{drawerName}") @Tags("ลิ้นชัก") @Security("bearerAuth", ["admin", "management-role"]) @SuccessResponse(HttpStatusCode.NO_CONTENT, "สำเร็จ") public async deleteDrawer(@Path() cabinetName: string, @Path() drawerName: string) { await new Promise((resolve, reject) => { const objects: string[] = []; const stream = minioClient.listObjectsV2( DEFAULT_BUCKET!, `${cabinetName}/${drawerName}/`, true, ); stream.on("data", (v) => { if (v && v.name) objects.push(v.name); }); stream.on("close", async () => resolve(await minioClient.removeObjects(DEFAULT_BUCKET!, objects)), ); stream.on("error", () => reject(new Error("เกิดข้อผิดพลาด ไม่สามารถลบไฟล์ได้"))); }); return this.setStatus(HttpStatusCode.NO_CONTENT); } }