diff --git a/Services/server/src/controllers/storageController.ts b/Services/server/src/controllers/storageController.ts new file mode 100644 index 0000000..4a1c32d --- /dev/null +++ b/Services/server/src/controllers/storageController.ts @@ -0,0 +1,104 @@ +import { Body, Controller, Get, Path, Post, Put, Query, Route, SuccessResponse } from "tsoa"; +import { replaceIllegalChars } from "../utils/minio"; +import minioClient from "../minio"; +import HttpStatusCode from "../interfaces/http-status"; +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("/storage/d") +export class StorageController extends Controller { + @Get() + @SuccessResponse(HttpStatusCode.OK, "Success") + public async getFolder(@Query() path: string, @Query() bucket?: string) { + const list = await new Promise<{ pathname: string; name: string }[]>((resolve, reject) => { + const item: { pathname: string; name: string }[] = []; + + const stream = minioClient.listObjectsV2(bucket ?? DEFAULT_BUCKET!, path); + stream.on("data", (v) => { + if (v && v.prefix) { + item.push({ + pathname: v.prefix, + name: v.prefix.slice(path?.length).split("/")[0], + }); + } + }); + stream.on("end", () => resolve(item)); + stream.on("error", () => reject(new Error("เกิดข้อผิดพลาดกับระบบจัดการไฟล์"))); + }); + + return list; + } + + @Post() + @SuccessResponse(HttpStatusCode.CREATED, "Success") + public async createFolder(@Query() path: string, @Query() bucket?: string) { + const fragments = path.split("/").filter(Boolean); + + await Promise.all( + fragments.map(async (_, i, a) => { + const path = [...a.slice(0, i + 1)].map((x) => replaceIllegalChars(x)).join("/"); + const created = await minioClient + .putObject(bucket ?? DEFAULT_BUCKET!, `${path}/.keep`, "", 0) + .catch((e) => console.error(e)); + if (!created) throw new Error("เกิดข้อผิดพลาดกับระบบจัดการไฟล์"); + }), + ); + + return this.setStatus(HttpStatusCode.CREATED); + } + + @Put() + @SuccessResponse(HttpStatusCode.NO_CONTENT, "Success") + public async updateFolder( + @Body() + body: { + from: { + bucket: string; + path: string; + }; + to: { + bucket: string; + path: string; + }; + }, + ) { + const src = body.from.path.split("/").filter(Boolean).join("/"); + const dst = body.to.path.split("/").filter(Boolean).join("/"); + + if ( + !Boolean( + await minioClient + .statObject(DEFAULT_BUCKET!, `${dst.replace(/^\/|\/$/g, "")}/.keep`) + .catch((e) => { + if (e.code === "NotFound") return false; + throw new Error(`Minio Error: ${e}`); + }), + ) + ) + throw new HttpError(HttpStatusCode.NOT_FOUND, "Destination Not Found"); + + const list = await new Promise<{ pathname: string; name: string }[]>((resolve, reject) => { + const item: { pathname: string; name: string }[] = []; + + const stream = minioClient.listObjectsV2(body.from.bucket, src); + + stream.on("data", (v) => { + if (v && v.prefix) { + item.push({ + pathname: v.prefix, + name: v.prefix.slice(src.length).split("/")[0], + }); + } + }); + stream.on("error", (e) => reject(new Error(`Minio Error: ${e}`))); + stream.on("end", () => resolve(item)); + }); + + return this.setStatus(HttpStatusCode.NO_CONTENT); + } +}