From 2c4d3846f1855631746d6cc7a426d2d9ea503ccd Mon Sep 17 00:00:00 2001 From: Methapon2001 <61303214+Methapon2001@users.noreply.github.com> Date: Mon, 27 Nov 2023 13:41:46 +0700 Subject: [PATCH] refactor: rabbitmq file upload process --- .../server/src/controllers/fileController.ts | 162 ++++---- .../controllers/subFolderFileController.ts | 360 ++++++++---------- Services/server/src/routes.ts | 25 +- Services/server/src/swagger.json | 278 ++++++++------ 4 files changed, 398 insertions(+), 427 deletions(-) diff --git a/Services/server/src/controllers/fileController.ts b/Services/server/src/controllers/fileController.ts index 310a008..ec8b6a2 100644 --- a/Services/server/src/controllers/fileController.ts +++ b/Services/server/src/controllers/fileController.ts @@ -7,6 +7,7 @@ import { Path, Post, Request, + Response, Route, Security, SuccessResponse, @@ -32,8 +33,12 @@ if (!DEFAULT_INDEX) throw Error("Default ElasticSearch index must be specified." export class FileController extends Controller { @Post("/") @Tags("File") - @Security("bearerAuth") - @SuccessResponse(HttpStatusCode.CREATED) + @Security("bearerAuth", ["admin"]) + @Response( + HttpStatusCode.NOT_FOUND, + "ตำแหน่งที่ระบุไม่พบ กรุณาเตรียมตำแหน่งที่ต้องการก่อนดำเนินการ", + ) + @SuccessResponse(HttpStatusCode.CREATED, "สำเร็จ") public async uploadFile( @Request() request: { user: { preferred_username: string } }, @Body() @@ -56,7 +61,25 @@ export class FileController extends Controller { "ตำแหน่งที่ระบุไม่พบ กรุณาเตรียมตำแหน่งที่ต้องการก่อนดำเนินการ", ); } - const rec = await popInfo(pathname); + + const result = await esClient + .search }>({ + index: DEFAULT_INDEX!, + query: { match: { pathname } }, + }) + .catch((e) => console.error(e)); + + // pathname is unique and should not have multiple record with same path + if (result && result.hits.hits.length > 0 && result.hits.hits[0]._source) { + await esClient + .delete({ + index: DEFAULT_INDEX!, + id: result.hits.hits[0]._id, + }) + .catch((e) => console.error(e)); + } + + const rec = result ? result.hits.hits[0]._source : false; const metadata: Partial = { pathname, @@ -75,7 +98,7 @@ export class FileController extends Controller { }; await esClient.index({ - index: "dev-index", + index: DEFAULT_INDEX!, document: metadata, }); @@ -85,24 +108,21 @@ export class FileController extends Controller { createdBy: metadata.createdBy, updatedAt: metadata.updatedAt, updatedBy: metadata.updatedBy, - upload: await minioClient.presignedPutObject("ehr", pathname), + upload: await minioClient.presignedPutObject(DEFAULT_BUCKET!, pathname), }; } @Get("/") @Tags("File") - @SuccessResponse(HttpStatusCode.OK) + @Security("bearerAuth") + @SuccessResponse(HttpStatusCode.OK, "สำเร็จ") public async getFile( @Path() cabinetName: string, @Path() drawerName: string, @Path() folderName: string, ): Promise { - const search = await esClient.search< - EhrFile & { - attachment: Record; - } - >({ - index: process.env.ELASTICSEARCH_INDEX ?? "ehr-index", + const search = await esClient.search }>({ + index: DEFAULT_INDEX!, query: { prefix: { pathname: `${cabinetName}/${drawerName}/${folderName}/`, @@ -124,8 +144,9 @@ export class FileController extends Controller { @Patch("/{fileName}") @Tags("File") - @Security("bearerAuth") - @SuccessResponse(HttpStatusCode.OK) + @Security("bearerAuth", ["admin"]) + @Response(HttpStatusCode.NOT_FOUND, "ไม่พบตำแหน่งที่ต้องการสร้างแฟ้ม") + @SuccessResponse(HttpStatusCode.OK, "สำเร็จ") public async updateFile( @Request() request: { user: { preferred_username: string } }, @Path() cabinetName: string, @@ -141,26 +162,30 @@ export class FileController extends Controller { keyword?: string; }, ): Promise { - const pathname = `${cabinetName}/${drawerName}/${folderName}/${fileName}`; + const basePath = `${cabinetName}/${drawerName}/${folderName}/`; + const pathname = `${basePath}${fileName}`; - if (!(await pathExist(`${cabinetName}/${drawerName}/${folderName}/`))) { - throw new HttpError( - HttpStatusCode.PRECONDITION_FAILED, - "Cabinet, drawer or folder cannot be found.", - ); + if ( + !Boolean( + await minioClient.statObject(DEFAULT_BUCKET!, `${pathname}`).catch((e) => { + if (e.code === "NotFound") return false; + throw new Error("เกิดข้อผิดพลาดกับระบบจัดการไฟล์"); + }), + ) + ) { + throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบไฟล์"); } - // assume user will replace file by re-upload + // assume user will probably replace file by re-upload but maybe just rename if (body.file) { - const destination = `${cabinetName}/${drawerName}/${folderName}/${body.file}`; - const source = `ehr/${cabinetName}/${drawerName}/${folderName}/${fileName}`; - - const copy = await minioClient.copyObject("ehr", destination, source, copyCond); + const destination = `${basePath}${body.file}`; + const source = `/${DEFAULT_BUCKET}/${basePath}${fileName}`; + const copy = await minioClient.copyObject(DEFAULT_BUCKET!, destination, source, copyCond); if (copy) { const search = await esClient .search }>({ - index: "my-test-index", + index: DEFAULT_INDEX!, query: { match: { pathname } }, }) .catch((e) => console.error(e)); @@ -177,15 +202,15 @@ export class FileController extends Controller { updatedBy: request.user.preferred_username ?? "n/a", }, }) - .then(() => minioClient.removeObject("ehr", pathname)); + .then(() => minioClient.removeObject(DEFAULT_BUCKET!, pathname)); } else { - await minioClient.removeObject("ehr", pathname); + await minioClient.removeObject(DEFAULT_BUCKET!, pathname); } } } else { const search = await esClient .search }>({ - index: "my-test-index", + index: DEFAULT_INDEX!, query: { match: { pathname } }, }) .catch((e) => console.error(e)); @@ -207,52 +232,36 @@ export class FileController extends Controller { } return body.file - ? this.setStatus(HttpStatusCode.NO_CONTENT) - : { + ? { upload: await minioClient.presignedPutObject( - "ehr", - `${cabinetName}/${drawerName}/${folderName}/${body.file ?? fileName}`, + DEFAULT_BUCKET!, + `${basePath}${body.file ?? fileName}`, ), - }; + } + : this.setStatus(HttpStatusCode.NO_CONTENT); } @Delete("/{fileName}") @Tags("File") - @Security("bearerAuth") - @SuccessResponse(HttpStatusCode.OK) + @Security("bearerAuth", ["admin"]) + @SuccessResponse(HttpStatusCode.OK, "สำเร็จ") public async deleteFile( @Path() cabinetName: string, @Path() drawerName: string, @Path() folderName: string, @Path() fileName: string, ) { - const result = await esClient - .deleteByQuery({ - index: process.env.ELASTICSEARCH_INDEX ?? "ehr-index", - query: { - match: { - pathname: `${cabinetName}/${drawerName}/${folderName}/${fileName}`, - }, - }, - }) - .catch((e) => console.error(e)); - - if (result && result.total === 0) { - throw new HttpError(HttpStatusCode.NOT_FOUND, "Data not found"); - } - - if (!result) { - throw new Error("An error occured, cannot perform this action."); - } - - await minioClient.removeObject("ehr", `${cabinetName}/${drawerName}/${folderName}/${fileName}`); - + await minioClient.removeObject( + DEFAULT_BUCKET!, + `${cabinetName}/${drawerName}/${folderName}/${fileName}`, + ); return this.setStatus(HttpStatusCode.NO_CONTENT); } @Get("/{fileName}") - @Tags("File") - @SuccessResponse(HttpStatusCode.OK) + @Tags("Download") + @Security("bearerAuth") + @SuccessResponse(HttpStatusCode.OK, "สำเร็จ") public async downloadFile( @Path() cabinetName: string, @Path() drawerName: string, @@ -260,7 +269,7 @@ export class FileController extends Controller { @Path() fileName: string, ) { const search = await esClient.search }>({ - index: process.env.ELASTICSEARCH_INDEX ?? "ehr-index", + index: DEFAULT_INDEX!, query: { match: { pathname: `${cabinetName}/${drawerName}/${folderName}/${fileName}` }, }, @@ -270,43 +279,14 @@ export class FileController extends Controller { throw new HttpError(HttpStatusCode.NOT_FOUND, "Not found"); } - const data = search.hits.hits[0]._source; - - if (!data) { - throw new HttpError(HttpStatusCode.INTERNAL_SERVER_ERROR, "Found data but no info."); - } - - const { attachment, ...rest } = data; + const { attachment, ...rest } = search.hits.hits[0]._source!; return { ...rest, download: await minioClient.presignedGetObject( - "ehr", + DEFAULT_BUCKET!, `${cabinetName}/${drawerName}/${folderName}/${fileName}`, ), }; } } - -async function popInfo(pathname: string) { - const result = await esClient - .search }>({ - index: DEFAULT_INDEX!, - query: { match: { pathname } }, - }) - .catch((e) => console.error(e)); - - // pathname is unique and should not have multiple record with same path - if (result && result.hits.hits.length > 0 && result.hits.hits[0]._source) { - await esClient - .delete({ - index: DEFAULT_INDEX!, - id: result.hits.hits[0]._id, - }) - .catch((e) => console.error(e)); - - return result.hits.hits[0]._source; - } - - return false; -} diff --git a/Services/server/src/controllers/subFolderFileController.ts b/Services/server/src/controllers/subFolderFileController.ts index af513d9..5fd993e 100644 --- a/Services/server/src/controllers/subFolderFileController.ts +++ b/Services/server/src/controllers/subFolderFileController.ts @@ -1,24 +1,33 @@ import { + Body, Controller, Delete, - FormField, Get, Patch, Path, Post, Request, + Response, Route, Security, SuccessResponse, Tags, - UploadedFile, } from "tsoa"; + import esClient from "../elasticsearch"; -import minioClient from "../storage"; +import minioClient from "../minio"; + import HttpStatusCode from "../interfaces/http-status"; -import { pathExist } from "../utils/minio"; -import HttpError from "../interfaces/http-error"; import { EhrFile } from "../interfaces/ehr-fs"; +import HttpError from "../interfaces/http-error"; + +import { copyCond, pathExist } from "../utils/minio"; + +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/{folderName}/subfolder/{subFolderName}/file", @@ -26,109 +35,98 @@ import { EhrFile } from "../interfaces/ehr-fs"; export class SubFolderFileController extends Controller { @Post("/") @Tags("SubFolder File") - @Security("bearerAuth") - @SuccessResponse(HttpStatusCode.CREATED) + @Security("bearerAuth", ["admin"]) + @Response( + HttpStatusCode.NOT_FOUND, + "ตำแหน่งที่ระบุไม่พบ กรุณาเตรียมตำแหน่งที่ต้องการก่อนดำเนินการ", + ) + @SuccessResponse(HttpStatusCode.CREATED, "สำเร็จ") public async uploadFile( @Request() request: { user: { preferred_username: string } }, - @UploadedFile() file: Express.Multer.File, - @FormField() title: string, - @FormField() description: string, - @FormField() keyword: string, - @FormField() category: string, + @Body() + body: { + file: string; + title: string; + description: string; + category: string; + keyword: string; + }, @Path() cabinetName: string, @Path() drawerName: string, @Path() folderName: string, @Path() subFolderName: string, ) { - const filename = Buffer.from(file.originalname, "latin1").toString("utf-8"); - const pathname = `${cabinetName}/${drawerName}/${folderName}/${subFolderName}/${filename}`; + const pathname = `${cabinetName}/${drawerName}/${folderName}/${subFolderName}/${body.file}`; if (!(await pathExist(`${cabinetName}/${drawerName}/${folderName}/${subFolderName}`))) { throw new HttpError( - HttpStatusCode.PRECONDITION_FAILED, - "Cabinet, drawer, folder or subfolder cannot be found.", + HttpStatusCode.NOT_FOUND, + "ตำแหน่งที่ระบุไม่พบ กรุณาเตรียมตำแหน่งที่ต้องการก่อนดำเนินการ", ); } - const info = await minioClient - .putObject("ehr", pathname, file.buffer, file.size, { - "Content-Type": file.mimetype, - createdAt: new Date().toISOString(), - createdBy: request.user.preferred_username, + const result = await esClient + .search }>({ + index: DEFAULT_INDEX!, + query: { match: { pathname } }, }) .catch((e) => console.error(e)); - if (!info) throw new Error("Object storage error occured."); + // pathname is unique and should not have multiple record with same path + if (result && result.hits.hits.length > 0 && result.hits.hits[0]._source) { + await esClient + .delete({ + index: DEFAULT_INDEX!, + id: result.hits.hits[0]._id, + }) + .catch((e) => console.error(e)); + } - const search = await esClient.search }>({ - index: process.env.ELASTICSEARCH_INDEX ?? 'ehr-index', - query: { - match: { - pathname: pathname, - }, - }, - }); - - const exist = search.hits.hits.find((v) => v._source?.pathname === pathname); + const rec = result ? result.hits.hits[0]._source : false; const metadata: Partial = { pathname, - fileName: filename, - fileSize: file.size, - fileType: file.mimetype, - title: title, - description: description, - category: category.split(","), - keyword: keyword.split(","), + fileName: body.file, + fileSize: 0, + fileType: "", + title: body.title, + description: body.description, + category: body.category.split(","), + keyword: body.keyword.split(","), + upload: false, + createdAt: new Date().toISOString(), + createdBy: rec ? rec.createdBy : "n/a", + updatedAt: new Date().toISOString(), + updatedBy: request.user.preferred_username ?? "n/a", }; - if (!exist) { - await esClient.index({ - pipeline: "attachment", - index: process.env.ELASTICSEARCH_INDEX ?? 'ehr-index', - document: { - data: Buffer.from(file.buffer).toString("base64"), - createdAt: new Date().toISOString(), - createdBy: request.user.preferred_username, - updatedAt: new Date().toISOString(), - updatedBy: request.user.preferred_username, - ...metadata, - }, - }); - } else { - await esClient.delete({ index: exist._index, id: exist._id }); - await esClient.index({ - pipeline: "attachment", - index: process.env.ELASTICSEARCH_INDEX ?? 'ehr-index', - document: { - data: Buffer.from(file.buffer).toString("base64"), - createdAt: exist._source?.createdAt, - createdBy: exist._source?.createdBy, - updatedAt: new Date().toISOString(), - updatedBy: request.user.preferred_username, - ...metadata, - }, - }); - } + await esClient.index({ + index: DEFAULT_INDEX!, + document: metadata, + }); - return this.setStatus(HttpStatusCode.CREATED); + return { + ...body, + createdAt: metadata.createdAt, + createdBy: metadata.createdBy, + updatedAt: metadata.updatedAt, + updatedBy: metadata.updatedBy, + upload: await minioClient.presignedPutObject(DEFAULT_BUCKET!, pathname), + }; } @Get("/") @Tags("SubFolder File") - @SuccessResponse(HttpStatusCode.OK) + @Security("bearerAuth") + @SuccessResponse(HttpStatusCode.OK, "สำเร็จ") public async getFile( @Path() cabinetName: string, @Path() drawerName: string, @Path() folderName: string, @Path() subFolderName: string, - ) { - const search = await esClient.search< - EhrFile & { - attachment: Record; - } - >({ - index: process.env.ELASTICSEARCH_INDEX ?? 'ehr-index', + ): Promise { + const search = await esClient.search }>({ + index: DEFAULT_INDEX!, query: { prefix: { pathname: `${cabinetName}/${drawerName}/${folderName}/${subFolderName}`, @@ -136,24 +134,23 @@ export class SubFolderFileController extends Controller { }, }); - // Use flatMap for return type only. Filter does not change type after filter out undefined or null const records = search.hits.hits .map((v) => { - if (!v._source) return; - - const { attachment, ...rest } = v._source; - - return rest; + if (v._source) { + const { attachment, ...rest } = v._source; + return rest satisfies EhrFile; + } }) - .flatMap((v) => (v ? [v] : [])); + .filter((v: EhrFile | undefined): v is EhrFile => !!v); return records; } @Patch("/{fileName}") @Tags("SubFolder File") - @Security("bearerAuth") - @SuccessResponse(HttpStatusCode.OK) + @Security("bearerAuth", ["admin"]) + @Response(HttpStatusCode.NOT_FOUND, "ไม่พบตำแหน่งที่ต้องการสร้างแฟ้ม") + @SuccessResponse(HttpStatusCode.OK, "สำเร็จ") public async updateFile( @Request() request: { user: { preferred_username: string } }, @Path() cabinetName: string, @@ -161,92 +158,98 @@ export class SubFolderFileController extends Controller { @Path() folderName: string, @Path() subFolderName: string, @Path() fileName: string, - @UploadedFile() file?: Express.Multer.File, - @FormField() title?: string, - @FormField() description?: string, - @FormField() keyword?: string, - @FormField() category?: string, + @Body() + body: { + file?: string; + title?: string; + description?: string; + category?: string; + keyword?: string; + }, ) { - const search = await esClient.search }>({ - index: process.env.ELASTICSEARCH_INDEX ?? 'ehr-index', - query: { - match: { - pathname: `${cabinetName}/${drawerName}/${folderName}/${subFolderName}/${fileName}`, - }, - }, - }); + const basePath = `${cabinetName}/${drawerName}/${folderName}/${subFolderName}/`; + const pathname = `${basePath}${fileName}`; - if (search && search.hits.hits.length === 0) { - throw new HttpError(HttpStatusCode.NOT_FOUND, "Not found"); + if ( + !Boolean( + await minioClient.statObject(DEFAULT_BUCKET!, `${pathname}`).catch((e) => { + if (e.code === "NotFound") return false; + throw new Error("เกิดข้อผิดพลาดกับระบบจัดการไฟล์"); + }), + ) + ) { + throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบไฟล์"); } - const data = search.hits.hits[0]; + // assume user will probably replace file by re-upload but maybe just rename + if (body.file) { + const destination = `${basePath}${body.file}`; + const source = `/${DEFAULT_BUCKET}/${basePath}${fileName}`; + const copy = await minioClient.copyObject(DEFAULT_BUCKET!, destination, source, copyCond); - if (!file) { - const esResult = await esClient - .update({ - index: process.env.ELASTICSEARCH_INDEX ?? 'ehr-index', - id: data._id, - doc: { - title, - description, - keyword: keyword?.split(","), - category: category?.split(","), - updatedAt: new Date().toISOString(), - updatedBy: request.user.preferred_username, - }, - }) - .catch((e) => console.error(e)); + if (copy) { + const search = await esClient + .search }>({ + index: DEFAULT_INDEX!, + query: { match: { pathname } }, + }) + .catch((e) => console.error(e)); - if (!esResult) throw new Error("An error occured, cannot perform this action."); + if (search && search.hits.hits.length > 0 && search.hits.hits[0]._source) { + const { _index: index, _id: id } = search.hits.hits[0]; + await esClient + .update({ + index, + id, + doc: { + pathname: destination, + updatedAt: new Date().toISOString(), + updatedBy: request.user.preferred_username ?? "n/a", + }, + }) + .then(() => minioClient.removeObject(DEFAULT_BUCKET!, pathname)); + } else { + await minioClient.removeObject(DEFAULT_BUCKET!, pathname); + } + } } else { - const filename = Buffer.from(file.originalname, "latin1").toString("utf-8"); - const pathname = `${cabinetName}/${drawerName}/${folderName}/${subFolderName}/${filename}`; - - await minioClient.removeObject( - "ehr", - `${cabinetName}/${drawerName}/${folderName}/${subFolderName}/${fileName}`, - ); - - const info = await minioClient - .putObject("ehr", pathname, file.buffer, file.size, { - "Content-Type": file.mimetype, - createdAt: new Date().toISOString(), - createdBy: request.user.preferred_username, + const search = await esClient + .search }>({ + index: DEFAULT_INDEX!, + query: { match: { pathname } }, }) .catch((e) => console.error(e)); - if (!info) throw new Error("Object storage error occured."); - - await esClient.delete({ index: data._index, id: data._id }); - await esClient.index({ - pipeline: "attachment", - index: process.env.ELASTICSEARCH_INDEX ?? 'ehr-index', - document: { - data: Buffer.from(file.buffer).toString("base64"), - pathname, - fileName: filename, - fileSize: file.size, - fileType: file.mimetype, - title: title, - description: description, - category: category?.split(","), - keyword: keyword?.split(","), - createdAt: data._source?.createdAt, - createdBy: data._source?.createdBy, - updatedAt: new Date().toISOString(), - updatedBy: request.user.preferred_username, - }, - }); + if (search && search.hits.hits.length > 0 && search.hits.hits[0]._source) { + const { _index: index, _id: id } = search.hits.hits[0]; + await esClient.update({ + index, + id, + doc: { + ...body, + keyword: body.keyword?.split(","), + category: body.category?.split(","), + updatedAt: new Date().toISOString(), + updatedBy: request.user.preferred_username ?? "n/a", + }, + }); + } } - return this.setStatus(HttpStatusCode.NO_CONTENT); + return body.file + ? { + upload: await minioClient.presignedPutObject( + DEFAULT_BUCKET!, + `${basePath}${body.file ?? fileName}`, + ), + } + : this.setStatus(HttpStatusCode.NO_CONTENT); } @Delete("/{fileName}") @Tags("SubFolder File") - @Security("bearerAuth") - @SuccessResponse(HttpStatusCode.OK) + @Security("bearerAuth", ["admin"]) + @SuccessResponse(HttpStatusCode.OK, "สำเร็จ") public async deleteFile( @Path() cabinetName: string, @Path() drawerName: string, @@ -254,42 +257,15 @@ export class SubFolderFileController extends Controller { @Path() subFolderName: string, @Path() fileName: string, ) { - const search = await esClient.search< - EhrFile & { - attachment: Record; - } - >({ - index: process.env.ELASTICSEARCH_INDEX ?? 'ehr-index', - query: { - match: { - pathname: `${cabinetName}/${drawerName}/${folderName}/${subFolderName}/${fileName}`, - }, - }, - }); - - if (search && search.hits.hits.length === 0) { - throw new HttpError(HttpStatusCode.NOT_FOUND, "Not found"); - } - - const esResult = await esClient - .delete({ - index: process.env.ELASTICSEARCH_INDEX ?? 'ehr-index', - id: search.hits.hits[0]._id, - }) - .catch((e) => console.error(e)); - - if (!esResult) throw new Error("An error occured, cannot perform this action."); - await minioClient.removeObject( - "ehr", - `${cabinetName}/${drawerName}/${folderName}/${subFolderName}/${fileName}`, + DEFAULT_BUCKET!, + `${cabinetName}/${drawerName}/${folderName}/${fileName}/${subFolderName}/`, ); - return this.setStatus(HttpStatusCode.NO_CONTENT); } @Get("/{fileName}") - @Tags("File") + @Tags("Download") @SuccessResponse(HttpStatusCode.OK) public async downloadFile( @Path() cabinetName: string, @@ -299,7 +275,7 @@ export class SubFolderFileController extends Controller { @Path() fileName: string, ) { const search = await esClient.search }>({ - index: process.env.ELASTICSEARCH_INDEX ?? 'ehr-index', + index: DEFAULT_INDEX!, query: { match: { pathname: `${cabinetName}/${drawerName}/${folderName}/${subFolderName}/${fileName}`, @@ -311,19 +287,13 @@ export class SubFolderFileController extends Controller { throw new HttpError(HttpStatusCode.NOT_FOUND, "Not found"); } - const data = search.hits.hits[0]._source; - - if (!data) { - throw new HttpError(HttpStatusCode.INTERNAL_SERVER_ERROR, "Found data but no info."); - } - - const { attachment, ...rest } = data; + const { attachment, ...rest } = search.hits.hits[0]._source!; return { ...rest, download: await minioClient.presignedGetObject( - "ehr", - `${cabinetName}/${drawerName}/${folderName}/${subFolderName}/${fileName}`, + DEFAULT_BUCKET!, + `${cabinetName}/${drawerName}/${folderName}/${fileName}`, ), }; } diff --git a/Services/server/src/routes.ts b/Services/server/src/routes.ts index 35ed08e..8f68008 100644 --- a/Services/server/src/routes.ts +++ b/Services/server/src/routes.ts @@ -20,8 +20,6 @@ import { expressAuthentication } from './utils/auth'; // @ts-ignore - no great way to install types from subpackage const promiseAny = require('promise.any'); import type { RequestHandler, Router } from 'express'; -const multer = require('multer'); -const upload = multer(); // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa @@ -321,6 +319,7 @@ export function RegisterRoutes(app: Router) { }); // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa app.get('/cabinet/:cabinetName/drawer/:drawerName/folder/:folderName/file', + authenticateMiddleware([{"bearerAuth":[]}]), ...(fetchMiddlewares(FileController)), ...(fetchMiddlewares(FileController.prototype.getFile)), @@ -408,6 +407,7 @@ export function RegisterRoutes(app: Router) { }); // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa app.get('/cabinet/:cabinetName/drawer/:drawerName/folder/:folderName/file/:fileName', + authenticateMiddleware([{"bearerAuth":[]}]), ...(fetchMiddlewares(FileController)), ...(fetchMiddlewares(FileController.prototype.downloadFile)), @@ -691,19 +691,14 @@ export function RegisterRoutes(app: Router) { }); // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa app.post('/cabinet/:cabinetName/drawer/:drawerName/folder/:folderName/subfolder/:subFolderName/file', - authenticateMiddleware([{"bearerAuth":[]}]), - upload.single('file'), + authenticateMiddleware([{"bearerAuth":["admin"]}]), ...(fetchMiddlewares(SubFolderFileController)), ...(fetchMiddlewares(SubFolderFileController.prototype.uploadFile)), function SubFolderFileController_uploadFile(request: any, response: any, next: any) { const args = { request: {"in":"request","name":"request","required":true,"dataType":"object"}, - file: {"in":"formData","name":"file","required":true,"dataType":"file"}, - title: {"in":"formData","name":"title","required":true,"dataType":"string"}, - description: {"in":"formData","name":"description","required":true,"dataType":"string"}, - keyword: {"in":"formData","name":"keyword","required":true,"dataType":"string"}, - category: {"in":"formData","name":"category","required":true,"dataType":"string"}, + body: {"in":"body","name":"body","required":true,"dataType":"nestedObjectLiteral","nestedProperties":{"keyword":{"dataType":"string","required":true},"category":{"dataType":"string","required":true},"description":{"dataType":"string","required":true},"title":{"dataType":"string","required":true},"file":{"dataType":"string","required":true}}}, cabinetName: {"in":"path","name":"cabinetName","required":true,"dataType":"string"}, drawerName: {"in":"path","name":"drawerName","required":true,"dataType":"string"}, folderName: {"in":"path","name":"folderName","required":true,"dataType":"string"}, @@ -727,6 +722,7 @@ export function RegisterRoutes(app: Router) { }); // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa app.get('/cabinet/:cabinetName/drawer/:drawerName/folder/:folderName/subfolder/:subFolderName/file', + authenticateMiddleware([{"bearerAuth":[]}]), ...(fetchMiddlewares(SubFolderFileController)), ...(fetchMiddlewares(SubFolderFileController.prototype.getFile)), @@ -755,8 +751,7 @@ export function RegisterRoutes(app: Router) { }); // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa app.patch('/cabinet/:cabinetName/drawer/:drawerName/folder/:folderName/subfolder/:subFolderName/file/:fileName', - authenticateMiddleware([{"bearerAuth":[]}]), - upload.single('file'), + authenticateMiddleware([{"bearerAuth":["admin"]}]), ...(fetchMiddlewares(SubFolderFileController)), ...(fetchMiddlewares(SubFolderFileController.prototype.updateFile)), @@ -768,11 +763,7 @@ export function RegisterRoutes(app: Router) { folderName: {"in":"path","name":"folderName","required":true,"dataType":"string"}, subFolderName: {"in":"path","name":"subFolderName","required":true,"dataType":"string"}, fileName: {"in":"path","name":"fileName","required":true,"dataType":"string"}, - file: {"in":"formData","name":"file","dataType":"file"}, - title: {"in":"formData","name":"title","dataType":"string"}, - description: {"in":"formData","name":"description","dataType":"string"}, - keyword: {"in":"formData","name":"keyword","dataType":"string"}, - category: {"in":"formData","name":"category","dataType":"string"}, + body: {"in":"body","name":"body","required":true,"dataType":"nestedObjectLiteral","nestedProperties":{"keyword":{"dataType":"string"},"category":{"dataType":"string"},"description":{"dataType":"string"},"title":{"dataType":"string"},"file":{"dataType":"string"}}}, }; // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa @@ -792,7 +783,7 @@ export function RegisterRoutes(app: Router) { }); // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa app.delete('/cabinet/:cabinetName/drawer/:drawerName/folder/:folderName/subfolder/:subFolderName/file/:fileName', - authenticateMiddleware([{"bearerAuth":[]}]), + authenticateMiddleware([{"bearerAuth":["admin"]}]), ...(fetchMiddlewares(SubFolderFileController)), ...(fetchMiddlewares(SubFolderFileController.prototype.deleteFile)), diff --git a/Services/server/src/swagger.json b/Services/server/src/swagger.json index a997829..d8bcdc9 100644 --- a/Services/server/src/swagger.json +++ b/Services/server/src/swagger.json @@ -609,7 +609,7 @@ } }, "404": { - "description": "ไม่พบลิ้นชัก" + "description": "ตำแหน่งที่ระบุไม่พบ กรุณาเตรียมตำแหน่งที่ต้องการก่อนดำเนินการ" } }, "tags": [ @@ -703,7 +703,11 @@ "tags": [ "File" ], - "security": [], + "security": [ + { + "bearerAuth": [] + } + ], "parameters": [ { "in": "path", @@ -891,7 +895,7 @@ "operationId": "DownloadFile", "responses": { "200": { - "description": "", + "description": "สำเร็จ", "content": { "application/json": { "schema": { @@ -987,7 +991,11 @@ "tags": [ "Download" ], - "security": [], + "security": [ + { + "bearerAuth": [] + } + ], "parameters": [ { "in": "path", @@ -1536,7 +1544,77 @@ "operationId": "UploadFile", "responses": { "201": { - "description": "" + "description": "สำเร็จ", + "content": { + "application/json": { + "schema": { + "properties": { + "keyword": { + "type": "string" + }, + "category": { + "type": "string" + }, + "description": { + "type": "string" + }, + "title": { + "type": "string" + }, + "file": { + "type": "string" + }, + "upload": { + "type": "string" + }, + "updatedBy": { + "type": "string" + }, + "updatedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "format": "date-time" + } + ] + }, + "createdBy": { + "type": "string" + }, + "createdAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "format": "date-time" + } + ] + } + }, + "required": [ + "keyword", + "category", + "description", + "title", + "file", + "upload", + "updatedBy", + "updatedAt", + "createdBy", + "createdAt" + ], + "type": "object" + } + } + } + }, + "404": { + "description": "ตำแหน่งที่ระบุไม่พบ กรุณาเตรียมตำแหน่งที่ต้องการก่อนดำเนินการ" } }, "tags": [ @@ -1544,7 +1622,9 @@ ], "security": [ { - "bearerAuth": [] + "bearerAuth": [ + "admin" + ] } ], "parameters": [ @@ -1584,34 +1664,33 @@ "requestBody": { "required": true, "content": { - "multipart/form-data": { + "application/json": { "schema": { - "type": "object", "properties": { - "file": { - "type": "string", - "format": "binary" - }, - "title": { - "type": "string" - }, - "description": { - "type": "string" - }, "keyword": { "type": "string" }, "category": { "type": "string" + }, + "description": { + "type": "string" + }, + "title": { + "type": "string" + }, + "file": { + "type": "string" } }, "required": [ - "file", - "title", - "description", "keyword", - "category" - ] + "category", + "description", + "title", + "file" + ], + "type": "object" } } } @@ -1621,91 +1700,12 @@ "operationId": "GetFile", "responses": { "200": { - "description": "", + "description": "สำเร็จ", "content": { "application/json": { "schema": { "items": { - "properties": { - "createdBy": { - "type": "string" - }, - "createdAt": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "format": "date-time" - } - ] - }, - "updatedBy": { - "type": "string" - }, - "updatedAt": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "string", - "format": "date-time" - } - ] - }, - "upload": { - "type": "boolean" - }, - "keyword": { - "items": { - "type": "string" - }, - "type": "array" - }, - "category": { - "items": { - "type": "string" - }, - "type": "array" - }, - "description": { - "type": "string" - }, - "title": { - "type": "string" - }, - "fileType": { - "type": "string" - }, - "fileSize": { - "type": "number", - "format": "double" - }, - "fileName": { - "type": "string" - }, - "pathname": { - "type": "string" - } - }, - "required": [ - "createdBy", - "createdAt", - "updatedBy", - "updatedAt", - "upload", - "keyword", - "category", - "description", - "title", - "fileType", - "fileSize", - "fileName", - "pathname" - ], - "type": "object" + "$ref": "#/components/schemas/EhrFile" }, "type": "array" } @@ -1716,7 +1716,11 @@ "tags": [ "SubFolder File" ], - "security": [], + "security": [ + { + "bearerAuth": [] + } + ], "parameters": [ { "in": "path", @@ -1758,7 +1762,30 @@ "operationId": "UpdateFile", "responses": { "200": { - "description": "" + "description": "สำเร็จ", + "content": { + "application/json": { + "schema": { + "anyOf": [ + {}, + { + "properties": { + "upload": { + "type": "string" + } + }, + "required": [ + "upload" + ], + "type": "object" + } + ] + } + } + } + }, + "404": { + "description": "ไม่พบตำแหน่งที่ต้องการสร้างแฟ้ม" } }, "tags": [ @@ -1766,7 +1793,9 @@ ], "security": [ { - "bearerAuth": [] + "bearerAuth": [ + "admin" + ] } ], "parameters": [ @@ -1812,29 +1841,28 @@ } ], "requestBody": { - "required": false, + "required": true, "content": { - "multipart/form-data": { + "application/json": { "schema": { - "type": "object", "properties": { - "file": { - "type": "string", - "format": "binary" - }, - "title": { - "type": "string" - }, - "description": { - "type": "string" - }, "keyword": { "type": "string" }, "category": { "type": "string" + }, + "description": { + "type": "string" + }, + "title": { + "type": "string" + }, + "file": { + "type": "string" } - } + }, + "type": "object" } } } @@ -1844,7 +1872,7 @@ "operationId": "DeleteFile", "responses": { "200": { - "description": "" + "description": "สำเร็จ" } }, "tags": [ @@ -1852,7 +1880,9 @@ ], "security": [ { - "bearerAuth": [] + "bearerAuth": [ + "admin" + ] } ], "parameters": [ @@ -1996,7 +2026,7 @@ } }, "tags": [ - "File" + "Download" ], "security": [], "parameters": [