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); } }