import { Body, Controller, Delete, Get, Path, Post, Put, Route, Security, SuccessResponse, Tags, Request, Response, } from "tsoa"; import minioClient from "../minio"; import esClient from "../elasticsearch"; import { copyCond, listFolder, listItem, 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/{drawerName}/folder") export class FolderController extends Controller { @Get("/") @Tags("แฟ้ม") @Security("bearerAuth") @Response( HttpStatusCode.INTERNAL_SERVER_ERROR, "เกิดข้อผิดพลาด ไม่สามารถแสดงรายการแฟ้มได้ กรุณาลองใหม่ในภายหลัง", ) @SuccessResponse(HttpStatusCode.OK, "สำเร็จ") public async listFolder( @Path() cabinetName: string, @Path() drawerName: string, ): Promise { const list = await listFolder(DEFAULT_BUCKET!, `${cabinetName}/${drawerName}`).catch((e) => console.error(`Error List Folder: ${e}`), ); if (!list) throw new Error("เกิดข้อผิดพลาด ไม่สามารถแสดงรายการแฟ้มได้ กรุณาลองใหม่ในภายหลัง"); return list; } @Post("/") @Tags("แฟ้ม") @Security("bearerAuth", ["admin"]) @Response(HttpStatusCode.NOT_FOUND, "ไม่พบตำแหน่งที่ต้องการสร้างแฟ้ม") @Response(HttpStatusCode.INTERNAL_SERVER_ERROR, "เกิดข้อผิดพลาดกับระบบจัดการไฟล์") @SuccessResponse(HttpStatusCode.CREATED, "สำเร็จ") public async createFolder( @Request() request: { user: { preferred_username: string } }, @Body() body: { name: string }, @Path() cabinetName: string, @Path() drawerName: string, ) { const basePath = `${cabinetName}/${drawerName}/`; if ( !Boolean( await minioClient.statObject(DEFAULT_BUCKET!, `${basePath}.keep`).catch((e) => { if (e.code === "NotFound") return false; throw new Error("เกิดข้อผิดพลาดกับระบบจัดการไฟล์"); }), ) ) { 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); } @Put("/{folderName}") @Tags("แฟ้ม") @Security("bearerAuth", ["admin"]) @Response(HttpStatusCode.INTERNAL_SERVER_ERROR, "เกิดข้อผิดพลาดไม่สามารถย้ายไฟล์ได้") @SuccessResponse(HttpStatusCode.NO_CONTENT, "สำเร็จ") public async editFolder( @Body() body: { name: string }, @Path() cabinetName: string, @Path() drawerName: string, @Path() folderName: string, ) { const path = `${cabinetName}/${drawerName}/${folderName}`; const list = await listItem(DEFAULT_BUCKET!, path, true); await Promise.all( list.map(async (current) => { if (!current.name) return; const destination = `${cabinetName}/${drawerName}/${replaceIllegalChars( body.name, )}/${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 }>({ 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 }, }); await minioClient.removeObject(DEFAULT_BUCKET!, current.name); }) .catch((e) => { console.error(e); throw new Error("เกิดข้อผิดพลาด ไม่สามารถย้ายไฟล์ได้"); }); }), ); return this.setStatus(HttpStatusCode.NO_CONTENT); } @Delete("/{folderName}") @Tags("แฟ้ม") @Security("bearerAuth", ["admin"]) @SuccessResponse(HttpStatusCode.NO_CONTENT, "สำเร็จ") public async deleteFolder( @Path() cabinetName: string, @Path() drawerName: string, @Path() folderName: string, ) { await new Promise((resolve, reject) => { const objects: string[] = []; const stream = minioClient.listObjectsV2( DEFAULT_BUCKET!, `${cabinetName}/${drawerName}/${folderName}`, 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); } }