import { Body, Controller, Delete, Example, Get, Patch, Path, Post, Route, Security, SuccessResponse, Tags, } from "tsoa"; import HttpStatus from "../interfaces/http-status"; import { createFile, createFolder, deleteFile, deleteFolder, downloadFile, listFile, updateFile, } from "../services/edm"; @Route("api/v1/salary/file") @Security("bearerAuth") @Tags("Document") export class DocumentController extends Controller { /** * @example id "00000000-0000-0000-0000-000000000000" * * @summary SLR_015 - รายการเอกสารอ้างอิงผังเงินเดือน */ @Get("{id}") @SuccessResponse(200, "สำเร็จ") @Example([ { pathname: "ระบบเงินเดือน/00000000-0000-0000-0000-000000000000/เอกสาร 1.docx", path: "ระบบเงินเดือน/00000000-0000-0000-0000-000000000000", title: "เอกสาร 1", description: "", author: "นายก", metadata: { tag: 1 }, keyword: [], category: [], fileName: "เอกสาร 1.docx", fileType: "application/vnd.openxmlformats-officedocument.wordprocessingml.document", fileSize: 1024, upload: true, createdAt: "2021-07-20T12:33:13.018Z", createdBy: "service-account-ext-api", updatedAt: "2021-07-20T12:33:13.018Z", updatedBy: "service-account-ext-api", }, ]) public async getFile(@Path() id: string) { const list = await listFile(["ระบบเงินเดือน", "เอกสารอ้างอิงผังเงินเดือน", id]); if (!list) throw new Error("เกิดข้อผิดพลาดกับระบบจัดการไฟล์"); return list; } /** * ข้อควรระวัง: หากลิงก์ยาวเกินไปอาจทำให้ไม่สามารถอัปโหลดได้ * * @example id "00000000-0000-0000-0000-000000000000" * @example file "เอกสาร 1.docx" * * @summary ข้อมูลเอกสารพร้อมลิงก์ดาวน์โหลด */ @Get("/{id}/{file}") @SuccessResponse(200, "สำเร็จ") @Example({ pathname: "ระบบเงินเดือน/00000000-0000-0000-0000-000000000000/เอกสาร 1.docx", path: "ระบบเงินเดือน/00000000-0000-0000-0000-000000000000", title: "เอกสาร 1", description: "", author: "นายก", keyword: [], category: [], metadata: { tag: 1 }, fileName: "เอกสาร 1.docx", fileType: "application/vnd.openxmlformats-officedocument.wordprocessingml.document", fileSize: 1024, hidden: false, upload: true, createdAt: "2021-07-20T12:33:13.018Z", createdBy: "service-account-ext-api", updatedAt: "2021-07-20T12:33:13.018Z", updatedBy: "service-account-ext-api", downloadUrl: "https://.../...", // Presigned Download URL 7 Days Exp }) public async getFileDownload(@Path() id: string, @Path() file: string) { const data = await downloadFile(["ระบบเงินเดือน", "เอกสารอ้างอิงผังเงินเดือน", id], file); if (!data) throw new Error("เกิดข้อผิดพลาดกับระบบจัดการไฟล์"); return data; } /** * ข้อควรระวัง: หากลิงก์ภาษาไทยยาวเกินไปอาจทำให้ไม่สามารถอัปโหลดได้ (น่าจะเป็นปัญหาทางด้านเทคนิคของ DNS) * * เมื่ออัปโหลดไฟล์โดย PUT Method จำเป็นต้องแนบ Content-Type ที่ถูกต้องของไฟล์ไปด้วยเพื่อให้ระบบรู้จักไฟล์นั้นๆ * โดย Content-Type จะเป็น mime-type เช่น docx เป็น application/vnd.openxmlformats-officedocument.wordprocessingml.document * ทั้งนี้ Content-Type อาจจะต่างกันแม้นามสกุลจะเหมือนกันได้ * * โดยปกติเมื่อเลือกไฟล์แล้ว Browser จะเก็บประเภทของไฟล์ไว้ด้วย ซึ่งเป็น Object ของ File มี attribute ชื่อ type ซึ่งเก็บ mime-type ไว้ * * หากไม่มีการป้อนชื่อ title ระบบ EDM จะใช้ title เป็นชื่อเดียวกับ ชื่อไฟล์โดยอัตโนมัติ * * @example id "00000000-0000-0000-0000-000000000000" * * @summary SLR_013 - เพิ่มเอกสารอ้างอิงผังเงินเดือน */ @Post("{id}") @Example([ { pathname: "ระบบเงินเดือน/00000000-0000-0000-0000-000000000000/เอกสาร 1.docx", path: "ระบบเงินเดือน/00000000-0000-0000-0000-000000000000", title: "เอกสาร 1", description: "", author: "นายก", keyword: [], category: [], metadata: { tag: 1, }, fileName: "เอกสาร 1.docx", fileSize: 0, fileType: "", hidden: false, upload: false, createdAt: "2021-07-20T12:33:13.018Z", createdBy: "service-account-ext-api", updatedAt: "2021-07-20T12:33:13.018Z", updatedBy: "service-account-ext-api", uploadUrl: "https://.../...", // Presigned Upload URL 7 Days Exp }, ]) @SuccessResponse(200, "Success") public async uploadFile( @Path() id: string, @Body() body: { /** * @example [ * { "fileName": "เอกสาร 1.docx" }, * { "fileName": "เอกสาร 2.docx", "title": "เอกสาร 2" } * ] */ fileList: { fileName: string; title?: string; description?: string; keyword?: string[]; category?: string[]; author?: string; metadata?: { [key: string]: unknown; }; }[]; /** * @example false */ replace?: boolean; }, ) { const path = ["ระบบเงินเดือน", "เอกสารอ้างอิงผังเงินเดือน", id]; if (!(await createFolder(path, id, true))) { throw new Error("เกิดข้อผิดพลาดกับระบบจัดการไฟล์ ไม่สามารถสร้างแฟ้มได้"); } const list = await listFile(path); if (!list || !Array.isArray(list)) { throw new Error("เกิดข้อผิดพลาดกับระบบจัดการไฟล์ ไม่สามารถสร้างแฟ้มได้"); } let used: string[] = []; let fileList = !body.replace ? body.fileList.map(({ fileName, ...props }) => { const dotIndex = fileName.lastIndexOf("."); const originalName = dotIndex !== -1 && !fileName.startsWith(".") ? fileName.slice(0, dotIndex) : fileName; const extension = dotIndex !== -1 && !fileName.startsWith(".") ? fileName.slice(dotIndex) : ""; let i = 1; while (list.findIndex((v) => v.fileName === fileName) !== -1 || used.includes(fileName)) { fileName = `${originalName} (${i++})`; if (dotIndex !== -1) fileName += extension; } props.author = "ไม่พบข้อมูล"; used.push(fileName); return { fileName: fileName, ...props }; }) : body.fileList; const map = fileList.map(async ({ fileName, ...props }) => [ fileName, await createFile(path, fileName, props), ]); const result = await Promise.all(map).catch((e) => console.error(`Storage Service Error: ${e}`), ); if (!result || result.some((v) => !v[1])) { throw new Error("เกิดข้อผิดพลาดกับระบบจัดการไฟล์ ไม่สามารถอัปโหลดไฟล์ได้"); } return Object.fromEntries(result); } /** * @example id "00000000-0000-0000-0000-000000000000" * @example file "เอกสาร 1.docx" * * @summary แก้ไขข้อมูลไฟล์ของ id นั้นๆ */ @Patch("{id}/{file}") public async updateFile( @Path() id: string, @Path() file: string, @Body() body: { title?: string; description?: string; keyword?: string[]; category?: string[]; author?: string; metadata?: { [key: string]: unknown }; }, ) { const props = body; const result = await updateFile( ["ระบบเงินเดือน", "เอกสารอ้างอิงผังเงินเดือน", id], file, props, ); if (!result) { throw new Error("เกิดข้อผิดพลาดกับระบบจัดการไฟล์ ไม่สามารถแก้ไขไฟล์ได้"); } return this.setStatus(HttpStatus.NO_CONTENT); } /** * @example id "00000000-0000-0000-0000-000000000000" * * @summary ลบไฟล์ทั้งหมดของ id นั้นๆ */ @Delete("{id}") public async deleteFolder(@Path() id: string) { const result = await deleteFolder(["ระบบเงินเดือน", "เอกสารอ้างอิงผังเงินเดือน"], id); if (!result) { throw new Error("เกิดข้อผิดพลาดกับระบบจัดการไฟล์ ไม่สามารถลบแฟ้มได้"); } return this.setStatus(HttpStatus.NO_CONTENT); } /** * @example id "00000000-0000-0000-0000-000000000000" * @example file "เอกสาร 1.docx" * * @summary SLR_014 - ลบเอกสารอ้างอิงผังเงินเดือน */ @Delete("{id}/{file}") public async deleteFile(@Path() id: string, @Path() file: string) { const result = await deleteFile(["ระบบเงินเดือน", "เอกสารอ้างอิงผังเงินเดือน", id], file); if (!result) { throw new Error("เกิดข้อผิดพลาดกับระบบจัดการไฟล์ ไม่สามารถลบไฟล์ ได้"); } return this.setStatus(HttpStatus.NO_CONTENT); } }