refactor: rabbitmq implement
This commit is contained in:
parent
24350a11a4
commit
3fc70daed0
12 changed files with 676 additions and 545 deletions
|
|
@ -5,8 +5,10 @@ import cors from "cors";
|
|||
|
||||
import { RegisterRoutes } from "./routes";
|
||||
import errorHandler from "./middlewares/exception";
|
||||
import rabbitmq from "./rabbitmq";
|
||||
|
||||
import swaggerSpecs from "./swagger.json";
|
||||
import { handler as amqHandler } from "./rabbitmq/handler";
|
||||
|
||||
const PORT = +(process.env.PORT || 80);
|
||||
|
||||
|
|
@ -28,3 +30,5 @@ app.use(errorHandler);
|
|||
app.listen(PORT, "0.0.0.0", () =>
|
||||
console.log(`Application is running on http://localhost:${PORT}`),
|
||||
);
|
||||
|
||||
rabbitmq.init(amqHandler).catch((e) => console.error(e));
|
||||
|
|
|
|||
|
|
@ -11,104 +11,109 @@ import {
|
|||
SuccessResponse,
|
||||
Tags,
|
||||
Request,
|
||||
Response,
|
||||
} from "tsoa";
|
||||
import * as Minio from "minio";
|
||||
import minioClient from "../storage";
|
||||
|
||||
import { EhrFile, EhrFolder } from "../interfaces/ehr-fs";
|
||||
import HttpStatusCode from "../interfaces/http-status";
|
||||
import { listFolder, listItem, replaceIllegalChars } from "../utils/minio";
|
||||
import minioClient from "../minio";
|
||||
import esClient from "../elasticsearch";
|
||||
|
||||
import { copyCond, listFolder, listItem, replaceIllegalChars } from "../utils/minio";
|
||||
|
||||
import HttpStatusCode from "../interfaces/http-status";
|
||||
import { EhrFile, EhrFolder } from "../interfaces/ehr-fs";
|
||||
|
||||
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")
|
||||
export class CabinetController extends Controller {
|
||||
@Get("/")
|
||||
@Tags("Cabinet")
|
||||
@SuccessResponse(HttpStatusCode.OK)
|
||||
@Security("bearerAuth")
|
||||
@Response(
|
||||
HttpStatusCode.INTERNAL_SERVER_ERROR,
|
||||
"เกิดข้อผิดพลาด ไม่สามารถแสดงรายการตู้เอกสารได้ กรุณาลองใหม่ในภายหลัง",
|
||||
)
|
||||
@SuccessResponse(HttpStatusCode.OK, "สำเร็จ")
|
||||
public async listCabinet(): Promise<EhrFolder[]> {
|
||||
const list = await listFolder().catch((e) => console.error(`Error List Folder: ${e}`));
|
||||
|
||||
if (!list) {
|
||||
throw new Error("Error listing folder");
|
||||
}
|
||||
|
||||
const list = await listFolder(DEFAULT_BUCKET!).catch((e) =>
|
||||
console.error(`Error List Folder: ${e}`),
|
||||
);
|
||||
if (!list)
|
||||
throw new Error("เกิดข้อผิดพลาด ไม่สามารถแสดงรายการตู้เอกสารได้ กรุณาลองใหม่ในภายหลัง");
|
||||
return list;
|
||||
}
|
||||
|
||||
@Post("/")
|
||||
@Tags("Cabinet")
|
||||
@Security("bearerAuth")
|
||||
@SuccessResponse(HttpStatusCode.CREATED)
|
||||
@Security("bearerAuth", ["admin"])
|
||||
@Response(HttpStatusCode.INTERNAL_SERVER_ERROR, "เกิดข้อผิดพลาดกับระบบจัดการไฟล์")
|
||||
@SuccessResponse(HttpStatusCode.CREATED, "สำเร็จ")
|
||||
public async createCabinet(
|
||||
@Request() request: { user: { preferred_username: string } },
|
||||
@Body() body: { name: string },
|
||||
) {
|
||||
const uploaded = await minioClient
|
||||
.putObject("ehr", `${replaceIllegalChars(body.name)}/.keep`, "", 0, {
|
||||
const created = await minioClient
|
||||
.putObject(DEFAULT_BUCKET!, `${replaceIllegalChars(body.name)}/.keep`, "", 0, {
|
||||
createdAt: new Date().toISOString(),
|
||||
createdBy: request.user.preferred_username,
|
||||
})
|
||||
.catch((e) => console.error(e));
|
||||
|
||||
if (!uploaded) throw new Error("Object storage error occured.");
|
||||
if (!created) throw new Error("เกิดข้อผิดพลาดกับระบบจัดการไฟล์");
|
||||
|
||||
return this.setStatus(HttpStatusCode.CREATED);
|
||||
}
|
||||
|
||||
@Put("/{cabinetName}")
|
||||
@Tags("Cabinet")
|
||||
@Security("bearerAuth")
|
||||
@SuccessResponse(HttpStatusCode.NO_CONTENT, "Success")
|
||||
@Security("bearerAuth", ["admin"])
|
||||
@Response(HttpStatusCode.INTERNAL_SERVER_ERROR, "เกิดข้อผิดพลาดไม่สามารถย้ายไฟล์ได้")
|
||||
@SuccessResponse(HttpStatusCode.NO_CONTENT, "สำเร็จ")
|
||||
public async editCabinet(
|
||||
@Path() cabinetName: string,
|
||||
@Body() body: { name: string },
|
||||
): Promise<void> {
|
||||
const list = await listItem(`${cabinetName}/`, true);
|
||||
|
||||
const cond = new Minio.CopyConditions();
|
||||
const path = `${cabinetName}/`;
|
||||
const list = await listItem(DEFAULT_BUCKET!, path, true);
|
||||
|
||||
await Promise.all(
|
||||
list.map(async (current) => {
|
||||
if (!current.name) return;
|
||||
|
||||
const destination = `${replaceIllegalChars(body.name)}/${current.name.slice(
|
||||
cabinetName.length + 1,
|
||||
)}`;
|
||||
const source = `/ehr/${current.name}`;
|
||||
const destination = `${replaceIllegalChars(body.name)}/${current.name.slice(path.length)}`;
|
||||
const source = `/${DEFAULT_BUCKET}/${current.name}`;
|
||||
|
||||
return await minioClient
|
||||
.copyObject("ehr", destination, source, cond)
|
||||
.copyObject(DEFAULT_BUCKET!, destination, source, copyCond)
|
||||
.then(async () => {
|
||||
if (!current.name) return;
|
||||
|
||||
await minioClient.removeObject("ehr", current.name);
|
||||
|
||||
if (current.name.includes(".keep")) return;
|
||||
if (current.name.includes(".keep")) {
|
||||
return await minioClient.removeObject(DEFAULT_BUCKET!, current.name);
|
||||
}
|
||||
|
||||
const search = await esClient.search<EhrFile & { attachment: Record<string, string> }>({
|
||||
index: process.env.ELASTICSEARCH_INDEX ?? "ehr-index",
|
||||
query: {
|
||||
match: {
|
||||
pathname: current.name,
|
||||
},
|
||||
},
|
||||
index: DEFAULT_INDEX!,
|
||||
query: { match: { pathname: current.name } },
|
||||
});
|
||||
|
||||
if (search && search.hits.hits.length === 0) {
|
||||
throw new Error("Data cannot be found in database.");
|
||||
}
|
||||
if (search && search.hits.hits.length === 0) throw new Error("ไม่พบข้อมูลในฐานข้อมูล");
|
||||
|
||||
const data = search.hits.hits[0];
|
||||
|
||||
await esClient.update({
|
||||
index: process.env.ELASTICSEARCH_INDEX ?? "ehr-index",
|
||||
index: DEFAULT_INDEX!,
|
||||
id: data._id,
|
||||
doc: { pathname: destination },
|
||||
});
|
||||
|
||||
await minioClient.removeObject(DEFAULT_BUCKET!, current.name);
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error(e);
|
||||
throw new Error("Failed to move.");
|
||||
throw new Error("เกิดข้อผิดพลาด ไม่สามารถย้ายไฟล์ได้");
|
||||
});
|
||||
}),
|
||||
);
|
||||
|
|
@ -118,44 +123,23 @@ export class CabinetController extends Controller {
|
|||
|
||||
@Delete("/{cabinetName}")
|
||||
@Tags("Cabinet")
|
||||
@Security("bearerAuth")
|
||||
@SuccessResponse(HttpStatusCode.NO_CONTENT)
|
||||
@Security("bearerAuth", ["admin"])
|
||||
@Response(HttpStatusCode.INTERNAL_SERVER_ERROR, "เกิดข้อผิดพลาด ไม่สามารถลบไฟล์ได้")
|
||||
@SuccessResponse(HttpStatusCode.NO_CONTENT, "สำเร็จ")
|
||||
public async deleteCabinet(@Path() cabinetName: string) {
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
const objects: string[] = [];
|
||||
const stream = minioClient.listObjectsV2("ehr", `${cabinetName}/`, true);
|
||||
const stream = minioClient.listObjectsV2(DEFAULT_BUCKET!, `${cabinetName}/`, true);
|
||||
|
||||
stream.on("data", (v) => {
|
||||
if (!(v && v.name)) return;
|
||||
|
||||
objects.push(v.name);
|
||||
if (v && v.name) objects.push(v.name);
|
||||
});
|
||||
|
||||
stream.on("close", async () => {
|
||||
minioClient.removeObjects("ehr", objects);
|
||||
resolve();
|
||||
});
|
||||
stream.on("error", () => reject(new Error("Object storage error occured.")));
|
||||
stream.on("close", async () =>
|
||||
resolve(await minioClient.removeObjects(DEFAULT_BUCKET!, objects)),
|
||||
);
|
||||
stream.on("error", () => reject(new Error("เกิดข้อผิดพลาด ไม่สามารถลบไฟล์ได้")));
|
||||
});
|
||||
|
||||
const searchResult = await esClient.search({
|
||||
index: process.env.ELASTICSEARCH_INDEX ?? "ehr-index",
|
||||
query: {
|
||||
prefix: { pathname: `${cabinetName}/` },
|
||||
},
|
||||
});
|
||||
|
||||
await Promise.all(
|
||||
searchResult.hits.hits.map(async (v) => {
|
||||
return esClient
|
||||
.delete({
|
||||
index: process.env.ELASTICSEARCH_INDEX ?? "ehr-index",
|
||||
id: v._id,
|
||||
})
|
||||
.catch((e) => console.error(`ElasticSearch Error: ${e}`));
|
||||
}),
|
||||
);
|
||||
|
||||
return this.setStatus(HttpStatusCode.NO_CONTENT);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,120 +6,133 @@ import {
|
|||
Path,
|
||||
Post,
|
||||
Put,
|
||||
Request,
|
||||
Route,
|
||||
Security,
|
||||
SuccessResponse,
|
||||
Tags,
|
||||
Request,
|
||||
Response,
|
||||
} from "tsoa";
|
||||
import * as Minio from "minio";
|
||||
import minioClient from "../storage";
|
||||
|
||||
import minioClient from "../minio";
|
||||
import esClient from "../elasticsearch";
|
||||
|
||||
import { copyCond, listFolder, listItem, replaceIllegalChars } from "../utils/minio";
|
||||
|
||||
import HttpStatusCode from "../interfaces/http-status";
|
||||
import HttpError from "../interfaces/http-error";
|
||||
import { listFolder, listItem, pathExist, replaceIllegalChars } from "../utils/minio";
|
||||
import esClient from "../elasticsearch";
|
||||
import { EhrFile, EhrFolder } from "../interfaces/ehr-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")
|
||||
export class DrawerController extends Controller {
|
||||
@Get("/")
|
||||
@Tags("Drawer")
|
||||
@SuccessResponse(HttpStatusCode.OK)
|
||||
@Security("bearerAuth")
|
||||
@Response(
|
||||
HttpStatusCode.INTERNAL_SERVER_ERROR,
|
||||
"เกิดข้อผิดพลาด ไม่สามารถแสดงรายการลิ้นชักได้ กรุณาลองใหม่ในภายหลัง",
|
||||
)
|
||||
@SuccessResponse(HttpStatusCode.OK, "สำเร็จ")
|
||||
public async listDrawer(@Path() cabinetName: string): Promise<EhrFolder[]> {
|
||||
const fullpath = [cabinetName, ""].join("/");
|
||||
|
||||
if (!(await pathExist(fullpath))) {
|
||||
throw new HttpError(HttpStatusCode.NOT_FOUND, "Provided path does not exist.");
|
||||
}
|
||||
|
||||
return listFolder(fullpath);
|
||||
const list = await listFolder(DEFAULT_BUCKET!, `${cabinetName}/`).catch((e) =>
|
||||
console.error(`Error List Folder: ${e}`),
|
||||
);
|
||||
if (!list)
|
||||
throw new Error("เกิดข้อผิดพลาด ไม่สามารถแสดงรายการลิ้นชักได้ กรุณาลองใหม่ในภายหลัง");
|
||||
return list;
|
||||
}
|
||||
|
||||
@Post("/")
|
||||
@Tags("Drawer")
|
||||
@Security("bearerAuth")
|
||||
@SuccessResponse(HttpStatusCode.CREATED)
|
||||
@Security("bearerAuth", ["admin"])
|
||||
@Response(HttpStatusCode.NOT_FOUND, "ไม่พบลิ้นชัก")
|
||||
@Response(HttpStatusCode.INTERNAL_SERVER_ERROR, "เกิดข้อผิดพลาดกับระบบจัดการไฟล์")
|
||||
@SuccessResponse(HttpStatusCode.CREATED, "สำเร็จ")
|
||||
public async createDrawer(
|
||||
@Request() request: { user: { preferred_username: string } },
|
||||
@Path() cabinetName: string,
|
||||
@Body() body: { name: string },
|
||||
) {
|
||||
if (!(await pathExist(`${cabinetName}/`))) {
|
||||
throw new HttpError(HttpStatusCode.PRECONDITION_FAILED, "Cabinet cannot be found.");
|
||||
const basePath = `${cabinetName}/`;
|
||||
|
||||
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 uploaded = await minioClient
|
||||
.putObject("ehr", `${cabinetName}/${replaceIllegalChars(body.name)}/.keep`, "", 0, {
|
||||
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 (!uploaded) {
|
||||
throw new Error("Object storage error occured.");
|
||||
}
|
||||
if (!created) throw new Error("เกิดข้อผิดพลาดกับระบบจัดการไฟล์");
|
||||
|
||||
return this.setStatus(HttpStatusCode.CREATED);
|
||||
}
|
||||
|
||||
@Put("/{drawerName}")
|
||||
@Tags("Drawer")
|
||||
@Security("bearerAuth")
|
||||
@SuccessResponse(HttpStatusCode.NO_CONTENT)
|
||||
@Security("bearerAuth", ["admin"])
|
||||
@Response(HttpStatusCode.INTERNAL_SERVER_ERROR, "เกิดข้อผิดพลาดไม่สามารถย้ายไฟล์ได้")
|
||||
@SuccessResponse(HttpStatusCode.NO_CONTENT, "สำเร็จ")
|
||||
public async editDrawer(
|
||||
@Path() cabinetName: string,
|
||||
@Path() drawerName: string,
|
||||
@Body() body: { name: string },
|
||||
): Promise<void> {
|
||||
const fullpath = `${cabinetName}/${drawerName}/`;
|
||||
|
||||
const list = await listItem(fullpath, true);
|
||||
|
||||
const cond = new Minio.CopyConditions();
|
||||
const path = `${cabinetName}/${drawerName}/`;
|
||||
const list = await listItem(DEFAULT_BUCKET!, path, true);
|
||||
|
||||
await Promise.all(
|
||||
list.map(async (current) => {
|
||||
if (!current.name) return;
|
||||
|
||||
const destination = `${cabinetName}/${replaceIllegalChars(body.name)}/${current.name.slice(
|
||||
fullpath.length,
|
||||
path.length,
|
||||
)}`;
|
||||
const source = `/ehr/${current.name}`;
|
||||
const source = `/${DEFAULT_BUCKET}/${current.name}`;
|
||||
|
||||
return await minioClient
|
||||
.copyObject("ehr", destination, source, cond)
|
||||
.copyObject(DEFAULT_BUCKET!, destination, source, copyCond)
|
||||
.then(async () => {
|
||||
if (!current.name) return;
|
||||
|
||||
await minioClient.removeObject("ehr", current.name);
|
||||
|
||||
if (current.name.includes(".keep")) return;
|
||||
if (current.name.includes(".keep")) {
|
||||
return await minioClient.removeObject(DEFAULT_BUCKET!, current.name);
|
||||
}
|
||||
|
||||
const search = await esClient.search<EhrFile & { attachment: Record<string, string> }>({
|
||||
index: process.env.ELASTICSEARCH_INDEX ?? "ehr-index",
|
||||
query: {
|
||||
match: {
|
||||
pathname: current.name,
|
||||
},
|
||||
},
|
||||
index: DEFAULT_INDEX!,
|
||||
query: { match: { pathname: current.name } },
|
||||
});
|
||||
|
||||
if (search && search.hits.hits.length === 0) {
|
||||
throw new Error("Data cannot be found in database.");
|
||||
}
|
||||
if (search && search.hits.hits.length === 0) throw new Error("ไม่พบข้อมูลในฐานข้อมูล");
|
||||
|
||||
const data = search.hits.hits[0];
|
||||
|
||||
await esClient.update({
|
||||
index: process.env.ELASTICSEARCH_INDEX ?? "ehr-index",
|
||||
index: DEFAULT_INDEX!,
|
||||
id: data._id,
|
||||
doc: { pathname: destination },
|
||||
});
|
||||
|
||||
await minioClient.removeObject(DEFAULT_BUCKET!, current.name);
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error(e);
|
||||
throw new Error("Failed to move.");
|
||||
throw new Error("เกิดข้อผิดพลาด ไม่สามารถย้ายไฟล์ได้");
|
||||
});
|
||||
}),
|
||||
);
|
||||
|
|
@ -129,44 +142,26 @@ export class DrawerController extends Controller {
|
|||
|
||||
@Delete("/{drawerName}")
|
||||
@Tags("Drawer")
|
||||
@Security("bearerAuth")
|
||||
@SuccessResponse(HttpStatusCode.NO_CONTENT)
|
||||
@Security("bearerAuth", ["admin"])
|
||||
@SuccessResponse(HttpStatusCode.NO_CONTENT, "สำเร็จ")
|
||||
public async deleteDrawer(@Path() cabinetName: string, @Path() drawerName: string) {
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
const objects: string[] = [];
|
||||
const stream = minioClient.listObjectsV2("ehr", `${cabinetName}/${drawerName}/`, true);
|
||||
const stream = minioClient.listObjectsV2(
|
||||
DEFAULT_BUCKET!,
|
||||
`${cabinetName}/${drawerName}/`,
|
||||
true,
|
||||
);
|
||||
|
||||
stream.on("data", (v) => {
|
||||
if (!(v && v.name)) return;
|
||||
|
||||
objects.push(v.name);
|
||||
if (v && v.name) objects.push(v.name);
|
||||
});
|
||||
|
||||
stream.on("close", async () => {
|
||||
minioClient.removeObjects("ehr", objects);
|
||||
resolve();
|
||||
});
|
||||
stream.on("error", () => reject(new Error("Object storage error occured.")));
|
||||
stream.on("close", async () =>
|
||||
resolve(await minioClient.removeObjects(DEFAULT_BUCKET!, objects)),
|
||||
);
|
||||
stream.on("error", () => reject(new Error("เกิดข้อผิดพลาด ไม่สามารถลบไฟล์ได้")));
|
||||
});
|
||||
|
||||
const searchResult = await esClient.search({
|
||||
index: process.env.ELASTICSEARCH_INDEX ?? "ehr-index",
|
||||
query: {
|
||||
prefix: { pathname: `${cabinetName}/${drawerName}/` },
|
||||
},
|
||||
});
|
||||
|
||||
await Promise.all(
|
||||
searchResult.hits.hits.map(async (v) => {
|
||||
return esClient
|
||||
.delete({
|
||||
index: process.env.ELASTICSEARCH_INDEX ?? "ehr-index",
|
||||
id: v._id,
|
||||
})
|
||||
.catch((e) => console.error(`ElasticSearch Error: ${e}`));
|
||||
}),
|
||||
);
|
||||
|
||||
return this.setStatus(HttpStatusCode.NO_CONTENT);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,88 +6,100 @@ import {
|
|||
Path,
|
||||
Post,
|
||||
Put,
|
||||
Request,
|
||||
Route,
|
||||
Security,
|
||||
SuccessResponse,
|
||||
Tags,
|
||||
Request,
|
||||
Response,
|
||||
} from "tsoa";
|
||||
import * as Minio from "minio";
|
||||
|
||||
import HttpError from "../interfaces/http-error";
|
||||
import HttpStatusCode from "../interfaces/http-status";
|
||||
import { listFolder, listItem, pathExist, replaceIllegalChars } from "../utils/minio";
|
||||
import { EhrFile, EhrFolder } from "../interfaces/ehr-fs";
|
||||
import minioClient from "../storage";
|
||||
import minioClient from "../minio";
|
||||
import esClient from "../elasticsearch";
|
||||
|
||||
import { copyCond, listFolder, listItem, replaceIllegalChars } from "../utils/minio";
|
||||
|
||||
import HttpStatusCode from "../interfaces/http-status";
|
||||
import { EhrFile, EhrFolder } from "../interfaces/ehr-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("Folder")
|
||||
@SuccessResponse(HttpStatusCode.OK)
|
||||
@Security("bearerAuth")
|
||||
@Response(
|
||||
HttpStatusCode.INTERNAL_SERVER_ERROR,
|
||||
"เกิดข้อผิดพลาด ไม่สามารถแสดงรายการแฟ้มได้ กรุณาลองใหม่ในภายหลัง",
|
||||
)
|
||||
@SuccessResponse(HttpStatusCode.OK, "สำเร็จ")
|
||||
public async listFolder(
|
||||
@Path() cabinetName: string,
|
||||
@Path() drawerName: string,
|
||||
): Promise<EhrFolder[]> {
|
||||
const fullpath = [cabinetName, drawerName, ""].join("/");
|
||||
|
||||
if (!(await pathExist(fullpath))) {
|
||||
throw new HttpError(HttpStatusCode.NOT_FOUND, "Provided path does not exist.");
|
||||
}
|
||||
|
||||
return listFolder(fullpath);
|
||||
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("Folder")
|
||||
@Security("bearerAuth")
|
||||
@SuccessResponse(HttpStatusCode.CREATED)
|
||||
@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,
|
||||
) {
|
||||
if (!(await pathExist(`${cabinetName}/${drawerName}/`))) {
|
||||
throw new HttpError(HttpStatusCode.PRECONDITION_FAILED, "Cabinet or drawer cannot be found.");
|
||||
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 uploaded = await minioClient
|
||||
.putObject(
|
||||
"ehr",
|
||||
`${cabinetName}/${drawerName}/${replaceIllegalChars(body.name)}/.keep`,
|
||||
"",
|
||||
0,
|
||||
{
|
||||
createdAt: new Date().toISOString(),
|
||||
createdBy: request.user.preferred_username,
|
||||
},
|
||||
)
|
||||
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 (!uploaded) {
|
||||
throw new Error("Object storage error occured.");
|
||||
}
|
||||
if (!created) throw new Error("เกิดข้อผิดพลาดกับระบบจัดการไฟล์");
|
||||
|
||||
return this.setStatus(HttpStatusCode.CREATED);
|
||||
}
|
||||
|
||||
@Put("/{folderName}")
|
||||
@Tags("Folder")
|
||||
@Security("bearerAuth")
|
||||
@SuccessResponse(HttpStatusCode.NO_CONTENT)
|
||||
@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 fullpath = `${cabinetName}/${drawerName}/${folderName}/`;
|
||||
|
||||
const list = await listItem(fullpath, true);
|
||||
|
||||
const cond = new Minio.CopyConditions();
|
||||
const path = `${cabinetName}/${drawerName}/${folderName}`;
|
||||
const list = await listItem(DEFAULT_BUCKET!, path, true);
|
||||
|
||||
await Promise.all(
|
||||
list.map(async (current) => {
|
||||
|
|
@ -95,42 +107,36 @@ export class FolderController extends Controller {
|
|||
|
||||
const destination = `${cabinetName}/${drawerName}/${replaceIllegalChars(
|
||||
body.name,
|
||||
)}/${current.name.slice(fullpath.length)}`;
|
||||
const source = `/ehr/${current.name}`;
|
||||
)}/${current.name.slice(path.length)}`;
|
||||
const source = `/${DEFAULT_BUCKET}/${current.name}`;
|
||||
|
||||
return await minioClient
|
||||
.copyObject("ehr", destination, source, cond)
|
||||
.copyObject(DEFAULT_BUCKET!, destination, source, copyCond)
|
||||
.then(async () => {
|
||||
if (!current.name) return;
|
||||
|
||||
await minioClient.removeObject("ehr", current.name);
|
||||
|
||||
if (current.name.includes(".keep")) return;
|
||||
if (current.name.includes(".keep")) {
|
||||
return await minioClient.removeObject(DEFAULT_BUCKET!, current.name);
|
||||
}
|
||||
|
||||
const search = await esClient.search<EhrFile & { attachment: Record<string, string> }>({
|
||||
index: process.env.ELASTICSEARCH_INDEX ?? "ehr-index",
|
||||
query: {
|
||||
match: {
|
||||
pathname: current.name,
|
||||
},
|
||||
},
|
||||
index: DEFAULT_INDEX!,
|
||||
query: { match: { pathname: current.name } },
|
||||
});
|
||||
|
||||
if (search && search.hits.hits.length === 0) {
|
||||
throw new Error("Data cannot be found in database.");
|
||||
}
|
||||
if (search && search.hits.hits.length === 0) throw new Error("ไม่พบข้อมูลในฐานข้อมูล");
|
||||
|
||||
const data = search.hits.hits[0];
|
||||
|
||||
await esClient.update({
|
||||
index: process.env.ELASTICSEARCH_INDEX ?? "ehr-index",
|
||||
index: DEFAULT_INDEX!,
|
||||
id: data._id,
|
||||
doc: { pathname: destination },
|
||||
});
|
||||
|
||||
await minioClient.removeObject(DEFAULT_BUCKET!, current.name);
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error(e);
|
||||
throw new Error("Failed to move.");
|
||||
throw new Error("เกิดข้อผิดพลาด ไม่สามารถย้ายไฟล์ได้");
|
||||
});
|
||||
}),
|
||||
);
|
||||
|
|
@ -140,8 +146,8 @@ export class FolderController extends Controller {
|
|||
|
||||
@Delete("/{folderName}")
|
||||
@Tags("Folder")
|
||||
@Security("bearerAuth")
|
||||
@SuccessResponse(HttpStatusCode.NO_CONTENT)
|
||||
@Security("bearerAuth", ["admin"])
|
||||
@SuccessResponse(HttpStatusCode.NO_CONTENT, "สำเร็จ")
|
||||
public async deleteFolder(
|
||||
@Path() cabinetName: string,
|
||||
@Path() drawerName: string,
|
||||
|
|
@ -150,42 +156,20 @@ export class FolderController extends Controller {
|
|||
await new Promise<void>((resolve, reject) => {
|
||||
const objects: string[] = [];
|
||||
const stream = minioClient.listObjectsV2(
|
||||
"ehr",
|
||||
`${cabinetName}/${drawerName}/${folderName}/`,
|
||||
DEFAULT_BUCKET!,
|
||||
`${cabinetName}/${drawerName}/${folderName}`,
|
||||
true,
|
||||
);
|
||||
|
||||
stream.on("data", (v) => {
|
||||
if (!(v && v.name)) return;
|
||||
|
||||
objects.push(v.name);
|
||||
if (v && v.name) objects.push(v.name);
|
||||
});
|
||||
|
||||
stream.on("close", async () => {
|
||||
minioClient.removeObjects("ehr", objects);
|
||||
resolve();
|
||||
});
|
||||
stream.on("error", () => reject(new Error("Object storage error occured.")));
|
||||
stream.on("close", async () =>
|
||||
resolve(await minioClient.removeObjects(DEFAULT_BUCKET!, objects)),
|
||||
);
|
||||
stream.on("error", () => reject(new Error("เกิดข้อผิดพลาด ไม่สามารถลบไฟล์ได้")));
|
||||
});
|
||||
|
||||
const searchResult = await esClient.search({
|
||||
index: process.env.ELASTICSEARCH_INDEX ?? "ehr-index",
|
||||
query: {
|
||||
prefix: { pathname: `${cabinetName}/${drawerName}/${folderName}/` },
|
||||
},
|
||||
});
|
||||
|
||||
await Promise.all(
|
||||
searchResult.hits.hits.map(async (v) => {
|
||||
return esClient
|
||||
.delete({
|
||||
index: process.env.ELASTICSEARCH_INDEX ?? "ehr-index",
|
||||
id: v._id,
|
||||
})
|
||||
.catch((e) => console.error(`ElasticSearch Error: ${e}`));
|
||||
}),
|
||||
);
|
||||
|
||||
return this.setStatus(HttpStatusCode.NO_CONTENT);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,44 +6,58 @@ import {
|
|||
Path,
|
||||
Post,
|
||||
Put,
|
||||
Request,
|
||||
Route,
|
||||
Security,
|
||||
SuccessResponse,
|
||||
Tags,
|
||||
Request,
|
||||
Response,
|
||||
} from "tsoa";
|
||||
import * as Minio from "minio";
|
||||
|
||||
import HttpError from "../interfaces/http-error";
|
||||
import HttpStatusCode from "../interfaces/http-status";
|
||||
import { listFolder, listItem, pathExist, replaceIllegalChars } from "../utils/minio";
|
||||
import { EhrFile, EhrFolder } from "../interfaces/ehr-fs";
|
||||
import minioClient from "../storage";
|
||||
import minioClient from "../minio";
|
||||
import esClient from "../elasticsearch";
|
||||
|
||||
import { copyCond, listFolder, listItem, replaceIllegalChars } from "../utils/minio";
|
||||
|
||||
import HttpStatusCode from "../interfaces/http-status";
|
||||
import { EhrFile, EhrFolder } from "../interfaces/ehr-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/{folderName}/subfolder")
|
||||
export class SubFolderController extends Controller {
|
||||
@Get("/")
|
||||
@Tags("SubFolder")
|
||||
@SuccessResponse(HttpStatusCode.OK)
|
||||
@Security("bearerAuth")
|
||||
@Response(
|
||||
HttpStatusCode.INTERNAL_SERVER_ERROR,
|
||||
"เกิดข้อผิดพลาด ไม่สามารถแสดงรายการแฟ้มได้ กรุณาลองใหม่ในภายหลัง",
|
||||
)
|
||||
@SuccessResponse(HttpStatusCode.OK, "สำเร็จ")
|
||||
public async listFolder(
|
||||
@Path() cabinetName: string,
|
||||
@Path() drawerName: string,
|
||||
@Path() folderName: string,
|
||||
): Promise<EhrFolder[]> {
|
||||
const fullpath = [cabinetName, drawerName, folderName, ""].join("/");
|
||||
|
||||
if (!(await pathExist(fullpath))) {
|
||||
throw new HttpError(HttpStatusCode.NOT_FOUND, "Provided path does not exist.");
|
||||
}
|
||||
|
||||
return listFolder(fullpath);
|
||||
const list = await listFolder(
|
||||
DEFAULT_BUCKET!,
|
||||
`${cabinetName}/${drawerName}/${folderName}`,
|
||||
).catch((e) => console.error(`Error List Folder: ${e}`));
|
||||
if (!list) throw new Error("เกิดข้อผิดพลาด ไม่สามารถแสดงรายการแฟ้มได้ กรุณาลองใหม่ในภายหลัง");
|
||||
return list;
|
||||
}
|
||||
|
||||
@Post("/")
|
||||
@Tags("SubFolder")
|
||||
@Security("bearerAuth")
|
||||
@SuccessResponse(HttpStatusCode.CREATED)
|
||||
@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 },
|
||||
|
|
@ -51,37 +65,36 @@ export class SubFolderController extends Controller {
|
|||
@Path() drawerName: string,
|
||||
@Path() folderName: string,
|
||||
) {
|
||||
if (!(await pathExist(`${cabinetName}/${drawerName}/${folderName}`))) {
|
||||
throw new HttpError(
|
||||
HttpStatusCode.PRECONDITION_FAILED,
|
||||
"Cabinet, drawer or folder cannot be found.",
|
||||
);
|
||||
const basePath = `${cabinetName}/${drawerName}/${folderName}/`;
|
||||
|
||||
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 uploaded = await minioClient
|
||||
.putObject(
|
||||
"ehr",
|
||||
`${cabinetName}/${drawerName}/${folderName}/${replaceIllegalChars(body.name)}/.keep`,
|
||||
"",
|
||||
0,
|
||||
{
|
||||
createdAt: new Date().toISOString(),
|
||||
createdBy: request.user.preferred_username,
|
||||
},
|
||||
)
|
||||
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 (!uploaded) {
|
||||
throw new Error("Object storage error occured.");
|
||||
}
|
||||
if (!created) throw new Error("เกิดข้อผิดพลาดกับระบบจัดการไฟล์");
|
||||
|
||||
return this.setStatus(HttpStatusCode.CREATED);
|
||||
}
|
||||
|
||||
@Put("/{subFolderName}")
|
||||
@Tags("SubFolder")
|
||||
@Security("bearerAuth")
|
||||
@SuccessResponse(HttpStatusCode.NO_CONTENT)
|
||||
@Security("bearerAuth", ["admin"])
|
||||
@Response(HttpStatusCode.INTERNAL_SERVER_ERROR, "เกิดข้อผิดพลาดไม่สามารถย้ายไฟล์ได้")
|
||||
@SuccessResponse(HttpStatusCode.NO_CONTENT, "สำเร็จ")
|
||||
public async editFolder(
|
||||
@Body() body: { name: string },
|
||||
@Path() cabinetName: string,
|
||||
|
|
@ -89,11 +102,8 @@ export class SubFolderController extends Controller {
|
|||
@Path() folderName: string,
|
||||
@Path() subFolderName: string,
|
||||
) {
|
||||
const fullpath = `${cabinetName}/${drawerName}/${folderName}/${subFolderName}`;
|
||||
|
||||
const list = await listItem(fullpath, true);
|
||||
|
||||
const cond = new Minio.CopyConditions();
|
||||
const path = `${cabinetName}/${drawerName}/${folderName}/${subFolderName}/`;
|
||||
const list = await listItem(DEFAULT_BUCKET!, path, true);
|
||||
|
||||
await Promise.all(
|
||||
list.map(async (current) => {
|
||||
|
|
@ -101,42 +111,36 @@ export class SubFolderController extends Controller {
|
|||
|
||||
const destination = `${cabinetName}/${drawerName}/${folderName}/${replaceIllegalChars(
|
||||
body.name,
|
||||
)}/${current.name.slice(fullpath.length)}`;
|
||||
const source = `/ehr/${current.name}`;
|
||||
)}/${current.name.slice(path.length)}`;
|
||||
const source = `/${DEFAULT_BUCKET}/${current.name}`;
|
||||
|
||||
return await minioClient
|
||||
.copyObject("ehr", destination, source, cond)
|
||||
.copyObject(DEFAULT_BUCKET!, destination, source, copyCond)
|
||||
.then(async () => {
|
||||
if (!current.name) return;
|
||||
|
||||
await minioClient.removeObject("ehr", current.name);
|
||||
|
||||
if (current.name.includes(".keep")) return;
|
||||
if (current.name.includes(".keep")) {
|
||||
return await minioClient.removeObject(DEFAULT_BUCKET!, current.name);
|
||||
}
|
||||
|
||||
const search = await esClient.search<EhrFile & { attachment: Record<string, string> }>({
|
||||
index: process.env.ELASTICSEARCH_INDEX ?? "ehr-index",
|
||||
query: {
|
||||
match: {
|
||||
pathname: current.name,
|
||||
},
|
||||
},
|
||||
index: DEFAULT_INDEX!,
|
||||
query: { match: { pathname: current.name } },
|
||||
});
|
||||
|
||||
if (search && search.hits.hits.length === 0) {
|
||||
throw new Error("Data cannot be found in database.");
|
||||
}
|
||||
if (search && search.hits.hits.length === 0) throw new Error("ไม่พบข้อมูลในฐานข้อมูล");
|
||||
|
||||
const data = search.hits.hits[0];
|
||||
|
||||
await esClient.update({
|
||||
index: process.env.ELASTICSEARCH_INDEX ?? "ehr-index",
|
||||
index: DEFAULT_INDEX!,
|
||||
id: data._id,
|
||||
doc: { pathname: destination },
|
||||
});
|
||||
|
||||
await minioClient.removeObject(DEFAULT_BUCKET!, current.name);
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error(e);
|
||||
throw new Error("Failed to move.");
|
||||
throw new Error("เกิดข้อผิดพลาด ไม่สามารถย้ายไฟล์ได้");
|
||||
});
|
||||
}),
|
||||
);
|
||||
|
|
@ -146,8 +150,8 @@ export class SubFolderController extends Controller {
|
|||
|
||||
@Delete("/{subFolderName}")
|
||||
@Tags("SubFolder")
|
||||
@Security("bearerAuth")
|
||||
@SuccessResponse(HttpStatusCode.NO_CONTENT)
|
||||
@Security("bearerAuth", ["admin"])
|
||||
@SuccessResponse(HttpStatusCode.NO_CONTENT, "สำเร็จ")
|
||||
public async deleteFolder(
|
||||
@Path() cabinetName: string,
|
||||
@Path() drawerName: string,
|
||||
|
|
@ -157,42 +161,20 @@ export class SubFolderController extends Controller {
|
|||
await new Promise<void>((resolve, reject) => {
|
||||
const objects: string[] = [];
|
||||
const stream = minioClient.listObjectsV2(
|
||||
"ehr",
|
||||
`${cabinetName}/${drawerName}/${folderName}/${subFolderName}/`,
|
||||
DEFAULT_BUCKET!,
|
||||
`${cabinetName}/${drawerName}/${folderName}/${subFolderName}`,
|
||||
true,
|
||||
);
|
||||
|
||||
stream.on("data", (v) => {
|
||||
if (!(v && v.name)) return;
|
||||
|
||||
objects.push(v.name);
|
||||
if (v && v.name) objects.push(v.name);
|
||||
});
|
||||
|
||||
stream.on("close", async () => {
|
||||
minioClient.removeObjects("ehr", objects);
|
||||
resolve();
|
||||
});
|
||||
stream.on("error", () => reject(new Error("Object storage error occured.")));
|
||||
stream.on("close", async () =>
|
||||
resolve(await minioClient.removeObjects(DEFAULT_BUCKET!, objects)),
|
||||
);
|
||||
stream.on("error", () => reject(new Error("เกิดข้อผิดพลาด ไม่สามารถลบไฟล์ได้")));
|
||||
});
|
||||
|
||||
const searchResult = await esClient.search({
|
||||
index: process.env.ELASTICSEARCH_INDEX ?? "ehr-index",
|
||||
query: {
|
||||
prefix: { pathname: `${cabinetName}/${drawerName}/${folderName}/${subFolderName}` },
|
||||
},
|
||||
});
|
||||
|
||||
await Promise.all(
|
||||
searchResult.hits.hits.map(async (v) => {
|
||||
return esClient
|
||||
.delete({
|
||||
index: process.env.ELASTICSEARCH_INDEX ?? "ehr-index",
|
||||
id: v._id,
|
||||
})
|
||||
.catch((e) => console.error(`ElasticSearch Error: ${e}`));
|
||||
}),
|
||||
);
|
||||
|
||||
return this.setStatus(HttpStatusCode.NO_CONTENT);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,17 +1,25 @@
|
|||
import { EhrFile } from "../interfaces/ehr-fs";
|
||||
import esClient from "../elasticsearch";
|
||||
import minioClient from "../storage";
|
||||
import minioClient from "../minio";
|
||||
|
||||
const DEFAULT_INDEX = process.env.ELASTICSEARCH_INDEX;
|
||||
|
||||
if (!DEFAULT_INDEX) throw Error("Default ElasticSearch index must be specified.");
|
||||
|
||||
// for failed queue that will come later
|
||||
const cachedBuffer: Record<string, Buffer> = {};
|
||||
const cachedMetadata: Record<string, { size: number; type: string }> = {};
|
||||
|
||||
export async function handler(key: string): Promise<boolean> {
|
||||
console.info(`[AMQ] Messages received - key: ${key}`);
|
||||
export async function handler(key: string, event: string): Promise<boolean> {
|
||||
console.info(`[AMQ] Messages received - key: ${key}, event: ${event}`);
|
||||
|
||||
const [bucket, ...fragment] = key.split("/");
|
||||
const pathname = fragment.join("/");
|
||||
|
||||
if (event === "s3:ObjectRemoved:Delete") {
|
||||
return await ensureDelete(pathname);
|
||||
}
|
||||
|
||||
if (!cachedBuffer[key]) {
|
||||
const stream = await minioClient.getObject(bucket, pathname);
|
||||
const buffer = Buffer.concat(await stream.toArray());
|
||||
|
|
@ -41,7 +49,7 @@ export async function handler(key: string): Promise<boolean> {
|
|||
async function popInfo(pathname: string) {
|
||||
const result = await esClient
|
||||
.search<EhrFile & { attachment?: Record<string, unknown> }>({
|
||||
index: "my-test-index",
|
||||
index: DEFAULT_INDEX!,
|
||||
query: { match: { pathname } },
|
||||
})
|
||||
.catch((e) => console.error(e));
|
||||
|
|
@ -50,7 +58,7 @@ async function popInfo(pathname: string) {
|
|||
if (result && result.hits.hits.length > 0 && result.hits.hits[0]._source) {
|
||||
await esClient
|
||||
.delete({
|
||||
index: "my-test-index",
|
||||
index: DEFAULT_INDEX!,
|
||||
id: result.hits.hits[0]._id,
|
||||
})
|
||||
.catch((e) => console.error(e));
|
||||
|
|
@ -63,6 +71,20 @@ async function popInfo(pathname: string) {
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* If there is data in database then delete it
|
||||
*/
|
||||
async function ensureDelete(pathname: string) {
|
||||
await esClient
|
||||
.deleteByQuery({
|
||||
index: DEFAULT_INDEX!,
|
||||
query: { match: { pathname } },
|
||||
conflicts: "proceed",
|
||||
})
|
||||
.catch((e) => console.error(e));
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle when record in elasticsearch cannot be found.
|
||||
* This will insert empty metadata.
|
||||
|
|
@ -94,7 +116,7 @@ async function handleNotFoundRecord(
|
|||
const result = await esClient
|
||||
.index({
|
||||
pipeline: "attachment",
|
||||
index: "my-test-index",
|
||||
index: DEFAULT_INDEX!,
|
||||
document: { data: base64, ...metadata },
|
||||
})
|
||||
.catch((e) => console.error(e));
|
||||
|
|
@ -116,7 +138,7 @@ async function handleFoundRecord(
|
|||
const result = await esClient
|
||||
.index({
|
||||
pipeline: "attachment",
|
||||
index: "my-test-index",
|
||||
index: DEFAULT_INDEX!,
|
||||
document: { data: Buffer.from(buffer).toString("base64"), ...metadata },
|
||||
})
|
||||
.catch((e) => console.error(e));
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import amqp from "amqplib";
|
||||
|
||||
export async function init(cb: (key: string) => boolean | Promise<boolean>) {
|
||||
export async function init(cb: (key: string, event: string) => boolean | Promise<boolean>) {
|
||||
if (!process.env.AMQ_URL || !process.env.AMQ_QUEUE) return;
|
||||
|
||||
const { AMQ_URL: url, AMQ_QUEUE: queue } = process.env;
|
||||
|
|
@ -22,10 +22,14 @@ export async function init(cb: (key: string) => boolean | Promise<boolean>) {
|
|||
const parsed: Record<string, unknown> = JSON.parse(msg.content.toString());
|
||||
|
||||
if (typeof parsed.Key !== "string" || parsed.Key.includes(".keep")) return channel.ack(msg);
|
||||
if (typeof parsed.EventName !== "string" || parsed.EventName.includes("Copy")) {
|
||||
return channel.ack(msg);
|
||||
}
|
||||
|
||||
const key = parsed.Key;
|
||||
const event = parsed.EventName;
|
||||
|
||||
if (await cb(key)) return channel.ack(msg);
|
||||
if (await cb(key, event)) return channel.ack(msg);
|
||||
|
||||
return await new Promise((resolve) => setTimeout(() => resolve(channel.nack(msg)), 3000));
|
||||
},
|
||||
|
|
@ -33,6 +37,4 @@ export async function init(cb: (key: string) => boolean | Promise<boolean>) {
|
|||
);
|
||||
}
|
||||
|
||||
export default {
|
||||
init,
|
||||
};
|
||||
export default { init };
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ const models: TsoaRoute.Models = {
|
|||
"description": {"dataType":"string","required":true},
|
||||
"category": {"dataType":"array","array":{"dataType":"string"},"required":true},
|
||||
"keyword": {"dataType":"array","array":{"dataType":"string"},"required":true},
|
||||
"upload": {"dataType":"boolean","required":true},
|
||||
"updatedAt": {"dataType":"union","subSchemas":[{"dataType":"string"},{"dataType":"datetime"}],"required":true},
|
||||
"updatedBy": {"dataType":"string","required":true},
|
||||
"createdAt": {"dataType":"union","subSchemas":[{"dataType":"string"},{"dataType":"datetime"}],"required":true},
|
||||
|
|
@ -76,6 +77,7 @@ export function RegisterRoutes(app: Router) {
|
|||
// Please look into the "controllerPathGlobs" config option described in the readme: https://github.com/lukeautry/tsoa
|
||||
// ###########################################################################################################
|
||||
app.get('/cabinet',
|
||||
authenticateMiddleware([{"bearerAuth":[]}]),
|
||||
...(fetchMiddlewares<RequestHandler>(CabinetController)),
|
||||
...(fetchMiddlewares<RequestHandler>(CabinetController.prototype.listCabinet)),
|
||||
|
||||
|
|
@ -100,7 +102,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.post('/cabinet',
|
||||
authenticateMiddleware([{"bearerAuth":[]}]),
|
||||
authenticateMiddleware([{"bearerAuth":["admin"]}]),
|
||||
...(fetchMiddlewares<RequestHandler>(CabinetController)),
|
||||
...(fetchMiddlewares<RequestHandler>(CabinetController.prototype.createCabinet)),
|
||||
|
||||
|
|
@ -127,7 +129,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.put('/cabinet/:cabinetName',
|
||||
authenticateMiddleware([{"bearerAuth":[]}]),
|
||||
authenticateMiddleware([{"bearerAuth":["admin"]}]),
|
||||
...(fetchMiddlewares<RequestHandler>(CabinetController)),
|
||||
...(fetchMiddlewares<RequestHandler>(CabinetController.prototype.editCabinet)),
|
||||
|
||||
|
|
@ -154,7 +156,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',
|
||||
authenticateMiddleware([{"bearerAuth":[]}]),
|
||||
authenticateMiddleware([{"bearerAuth":["admin"]}]),
|
||||
...(fetchMiddlewares<RequestHandler>(CabinetController)),
|
||||
...(fetchMiddlewares<RequestHandler>(CabinetController.prototype.deleteCabinet)),
|
||||
|
||||
|
|
@ -180,6 +182,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',
|
||||
authenticateMiddleware([{"bearerAuth":[]}]),
|
||||
...(fetchMiddlewares<RequestHandler>(DrawerController)),
|
||||
...(fetchMiddlewares<RequestHandler>(DrawerController.prototype.listDrawer)),
|
||||
|
||||
|
|
@ -205,7 +208,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.post('/cabinet/:cabinetName/drawer',
|
||||
authenticateMiddleware([{"bearerAuth":[]}]),
|
||||
authenticateMiddleware([{"bearerAuth":["admin"]}]),
|
||||
...(fetchMiddlewares<RequestHandler>(DrawerController)),
|
||||
...(fetchMiddlewares<RequestHandler>(DrawerController.prototype.createDrawer)),
|
||||
|
||||
|
|
@ -233,7 +236,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.put('/cabinet/:cabinetName/drawer/:drawerName',
|
||||
authenticateMiddleware([{"bearerAuth":[]}]),
|
||||
authenticateMiddleware([{"bearerAuth":["admin"]}]),
|
||||
...(fetchMiddlewares<RequestHandler>(DrawerController)),
|
||||
...(fetchMiddlewares<RequestHandler>(DrawerController.prototype.editDrawer)),
|
||||
|
||||
|
|
@ -261,7 +264,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',
|
||||
authenticateMiddleware([{"bearerAuth":[]}]),
|
||||
authenticateMiddleware([{"bearerAuth":["admin"]}]),
|
||||
...(fetchMiddlewares<RequestHandler>(DrawerController)),
|
||||
...(fetchMiddlewares<RequestHandler>(DrawerController.prototype.deleteDrawer)),
|
||||
|
||||
|
|
@ -289,18 +292,13 @@ 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/file',
|
||||
authenticateMiddleware([{"bearerAuth":[]}]),
|
||||
upload.single('file'),
|
||||
...(fetchMiddlewares<RequestHandler>(FileController)),
|
||||
...(fetchMiddlewares<RequestHandler>(FileController.prototype.uploadFile)),
|
||||
|
||||
function FileController_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"},
|
||||
|
|
@ -351,7 +349,6 @@ 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/file/:fileName',
|
||||
authenticateMiddleware([{"bearerAuth":[]}]),
|
||||
upload.single('file'),
|
||||
...(fetchMiddlewares<RequestHandler>(FileController)),
|
||||
...(fetchMiddlewares<RequestHandler>(FileController.prototype.updateFile)),
|
||||
|
||||
|
|
@ -362,11 +359,7 @@ export function RegisterRoutes(app: Router) {
|
|||
drawerName: {"in":"path","name":"drawerName","required":true,"dataType":"string"},
|
||||
folderName: {"in":"path","name":"folderName","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
|
||||
|
|
@ -443,6 +436,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',
|
||||
authenticateMiddleware([{"bearerAuth":[]}]),
|
||||
...(fetchMiddlewares<RequestHandler>(FolderController)),
|
||||
...(fetchMiddlewares<RequestHandler>(FolderController.prototype.listFolder)),
|
||||
|
||||
|
|
@ -469,7 +463,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.post('/cabinet/:cabinetName/drawer/:drawerName/folder',
|
||||
authenticateMiddleware([{"bearerAuth":[]}]),
|
||||
authenticateMiddleware([{"bearerAuth":["admin"]}]),
|
||||
...(fetchMiddlewares<RequestHandler>(FolderController)),
|
||||
...(fetchMiddlewares<RequestHandler>(FolderController.prototype.createFolder)),
|
||||
|
||||
|
|
@ -498,7 +492,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.put('/cabinet/:cabinetName/drawer/:drawerName/folder/:folderName',
|
||||
authenticateMiddleware([{"bearerAuth":[]}]),
|
||||
authenticateMiddleware([{"bearerAuth":["admin"]}]),
|
||||
...(fetchMiddlewares<RequestHandler>(FolderController)),
|
||||
...(fetchMiddlewares<RequestHandler>(FolderController.prototype.editFolder)),
|
||||
|
||||
|
|
@ -527,7 +521,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',
|
||||
authenticateMiddleware([{"bearerAuth":[]}]),
|
||||
authenticateMiddleware([{"bearerAuth":["admin"]}]),
|
||||
...(fetchMiddlewares<RequestHandler>(FolderController)),
|
||||
...(fetchMiddlewares<RequestHandler>(FolderController.prototype.deleteFolder)),
|
||||
|
||||
|
|
@ -580,6 +574,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',
|
||||
authenticateMiddleware([{"bearerAuth":[]}]),
|
||||
...(fetchMiddlewares<RequestHandler>(SubFolderController)),
|
||||
...(fetchMiddlewares<RequestHandler>(SubFolderController.prototype.listFolder)),
|
||||
|
||||
|
|
@ -607,7 +602,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.post('/cabinet/:cabinetName/drawer/:drawerName/folder/:folderName/subfolder',
|
||||
authenticateMiddleware([{"bearerAuth":[]}]),
|
||||
authenticateMiddleware([{"bearerAuth":["admin"]}]),
|
||||
...(fetchMiddlewares<RequestHandler>(SubFolderController)),
|
||||
...(fetchMiddlewares<RequestHandler>(SubFolderController.prototype.createFolder)),
|
||||
|
||||
|
|
@ -637,7 +632,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.put('/cabinet/:cabinetName/drawer/:drawerName/folder/:folderName/subfolder/:subFolderName',
|
||||
authenticateMiddleware([{"bearerAuth":[]}]),
|
||||
authenticateMiddleware([{"bearerAuth":["admin"]}]),
|
||||
...(fetchMiddlewares<RequestHandler>(SubFolderController)),
|
||||
...(fetchMiddlewares<RequestHandler>(SubFolderController.prototype.editFolder)),
|
||||
|
||||
|
|
@ -667,7 +662,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',
|
||||
authenticateMiddleware([{"bearerAuth":[]}]),
|
||||
authenticateMiddleware([{"bearerAuth":["admin"]}]),
|
||||
...(fetchMiddlewares<RequestHandler>(SubFolderController)),
|
||||
...(fetchMiddlewares<RequestHandler>(SubFolderController.prototype.deleteFolder)),
|
||||
|
||||
|
|
|
|||
|
|
@ -79,6 +79,9 @@
|
|||
},
|
||||
"type": "array"
|
||||
},
|
||||
"upload": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"updatedAt": {
|
||||
"anyOf": [
|
||||
{
|
||||
|
|
@ -117,6 +120,7 @@
|
|||
"description",
|
||||
"category",
|
||||
"keyword",
|
||||
"upload",
|
||||
"updatedAt",
|
||||
"updatedBy",
|
||||
"createdAt",
|
||||
|
|
@ -178,9 +182,9 @@
|
|||
}
|
||||
},
|
||||
"info": {
|
||||
"title": "BMA EHR - Test Service API",
|
||||
"version": "0.0.1",
|
||||
"description": "Best practice for initialize express project",
|
||||
"title": "Enterprise Document Management(EDM) - API",
|
||||
"version": "0.0.2",
|
||||
"description": "Open API Specfication for Enterprise Document Management ",
|
||||
"license": {
|
||||
"name": "by Frappet",
|
||||
"url": "https://frappet.com"
|
||||
|
|
@ -193,7 +197,7 @@
|
|||
"operationId": "ListCabinet",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "",
|
||||
"description": "สำเร็จ",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
|
|
@ -204,19 +208,9 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"Cabinet"
|
||||
],
|
||||
"security": [],
|
||||
"parameters": []
|
||||
},
|
||||
"post": {
|
||||
"operationId": "CreateCabinet",
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": ""
|
||||
},
|
||||
"500": {
|
||||
"description": "เกิดข้อผิดพลาด ไม่สามารถแสดงรายการตู้เอกสารได้ กรุณาลองใหม่ในภายหลัง"
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
|
|
@ -227,6 +221,28 @@
|
|||
"bearerAuth": []
|
||||
}
|
||||
],
|
||||
"parameters": []
|
||||
},
|
||||
"post": {
|
||||
"operationId": "CreateCabinet",
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "สำเร็จ"
|
||||
},
|
||||
"500": {
|
||||
"description": "เกิดข้อผิดพลาดกับระบบจัดการไฟล์"
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"Cabinet"
|
||||
],
|
||||
"security": [
|
||||
{
|
||||
"bearerAuth": [
|
||||
"admin"
|
||||
]
|
||||
}
|
||||
],
|
||||
"parameters": [],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
|
|
@ -253,7 +269,10 @@
|
|||
"operationId": "EditCabinet",
|
||||
"responses": {
|
||||
"204": {
|
||||
"description": "Success"
|
||||
"description": "สำเร็จ"
|
||||
},
|
||||
"500": {
|
||||
"description": "เกิดข้อผิดพลาดไม่สามารถย้ายไฟล์ได้"
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
|
|
@ -261,7 +280,9 @@
|
|||
],
|
||||
"security": [
|
||||
{
|
||||
"bearerAuth": []
|
||||
"bearerAuth": [
|
||||
"admin"
|
||||
]
|
||||
}
|
||||
],
|
||||
"parameters": [
|
||||
|
|
@ -297,12 +318,10 @@
|
|||
"operationId": "DeleteCabinet",
|
||||
"responses": {
|
||||
"204": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {}
|
||||
}
|
||||
}
|
||||
"description": "สำเร็จ"
|
||||
},
|
||||
"500": {
|
||||
"description": "เกิดข้อผิดพลาด ไม่สามารถลบไฟล์ได้"
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
|
|
@ -310,7 +329,9 @@
|
|||
],
|
||||
"security": [
|
||||
{
|
||||
"bearerAuth": []
|
||||
"bearerAuth": [
|
||||
"admin"
|
||||
]
|
||||
}
|
||||
],
|
||||
"parameters": [
|
||||
|
|
@ -330,7 +351,7 @@
|
|||
"operationId": "ListDrawer",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "",
|
||||
"description": "สำเร็จ",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
|
|
@ -341,12 +362,19 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "เกิดข้อผิดพลาด ไม่สามารถแสดงรายการลิ้นชักได้ กรุณาลองใหม่ในภายหลัง"
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"Drawer"
|
||||
],
|
||||
"security": [],
|
||||
"security": [
|
||||
{
|
||||
"bearerAuth": []
|
||||
}
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"in": "path",
|
||||
|
|
@ -362,7 +390,13 @@
|
|||
"operationId": "CreateDrawer",
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": ""
|
||||
"description": "สำเร็จ"
|
||||
},
|
||||
"404": {
|
||||
"description": "ไม่พบลิ้นชัก"
|
||||
},
|
||||
"500": {
|
||||
"description": "เกิดข้อผิดพลาดกับระบบจัดการไฟล์"
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
|
|
@ -370,7 +404,9 @@
|
|||
],
|
||||
"security": [
|
||||
{
|
||||
"bearerAuth": []
|
||||
"bearerAuth": [
|
||||
"admin"
|
||||
]
|
||||
}
|
||||
],
|
||||
"parameters": [
|
||||
|
|
@ -408,7 +444,10 @@
|
|||
"operationId": "EditDrawer",
|
||||
"responses": {
|
||||
"204": {
|
||||
"description": ""
|
||||
"description": "สำเร็จ"
|
||||
},
|
||||
"500": {
|
||||
"description": "เกิดข้อผิดพลาดไม่สามารถย้ายไฟล์ได้"
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
|
|
@ -416,7 +455,9 @@
|
|||
],
|
||||
"security": [
|
||||
{
|
||||
"bearerAuth": []
|
||||
"bearerAuth": [
|
||||
"admin"
|
||||
]
|
||||
}
|
||||
],
|
||||
"parameters": [
|
||||
|
|
@ -460,12 +501,7 @@
|
|||
"operationId": "DeleteDrawer",
|
||||
"responses": {
|
||||
"204": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {}
|
||||
}
|
||||
}
|
||||
"description": "สำเร็จ"
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
|
|
@ -473,7 +509,9 @@
|
|||
],
|
||||
"security": [
|
||||
{
|
||||
"bearerAuth": []
|
||||
"bearerAuth": [
|
||||
"admin"
|
||||
]
|
||||
}
|
||||
],
|
||||
"parameters": [
|
||||
|
|
@ -501,7 +539,74 @@
|
|||
"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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
|
|
@ -541,34 +646,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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -628,7 +732,27 @@
|
|||
"operationId": "UpdateFile",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": ""
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"anyOf": [
|
||||
{},
|
||||
{
|
||||
"properties": {
|
||||
"upload": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"upload"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
|
|
@ -674,29 +798,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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -789,6 +912,9 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"upload": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"keyword": {
|
||||
"items": {
|
||||
"type": "string"
|
||||
|
|
@ -829,6 +955,7 @@
|
|||
"createdAt",
|
||||
"updatedBy",
|
||||
"updatedAt",
|
||||
"upload",
|
||||
"keyword",
|
||||
"category",
|
||||
"description",
|
||||
|
|
@ -890,7 +1017,7 @@
|
|||
"operationId": "ListFolder",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "",
|
||||
"description": "สำเร็จ",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
|
|
@ -901,12 +1028,19 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "เกิดข้อผิดพลาด ไม่สามารถแสดงรายการแฟ้มได้ กรุณาลองใหม่ในภายหลัง"
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"Folder"
|
||||
],
|
||||
"security": [],
|
||||
"security": [
|
||||
{
|
||||
"bearerAuth": []
|
||||
}
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"in": "path",
|
||||
|
|
@ -930,7 +1064,13 @@
|
|||
"operationId": "CreateFolder",
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": ""
|
||||
"description": "สำเร็จ"
|
||||
},
|
||||
"404": {
|
||||
"description": "ไม่พบของแฟ้ม"
|
||||
},
|
||||
"500": {
|
||||
"description": "เกิดข้อผิดพลาดกับระบบจัดการไฟล์"
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
|
|
@ -938,7 +1078,9 @@
|
|||
],
|
||||
"security": [
|
||||
{
|
||||
"bearerAuth": []
|
||||
"bearerAuth": [
|
||||
"admin"
|
||||
]
|
||||
}
|
||||
],
|
||||
"parameters": [
|
||||
|
|
@ -984,7 +1126,10 @@
|
|||
"operationId": "EditFolder",
|
||||
"responses": {
|
||||
"204": {
|
||||
"description": ""
|
||||
"description": "สำเร็จ"
|
||||
},
|
||||
"500": {
|
||||
"description": "เกิดข้อผิดพลาดไม่สามารถย้ายไฟล์ได้"
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
|
|
@ -992,7 +1137,9 @@
|
|||
],
|
||||
"security": [
|
||||
{
|
||||
"bearerAuth": []
|
||||
"bearerAuth": [
|
||||
"admin"
|
||||
]
|
||||
}
|
||||
],
|
||||
"parameters": [
|
||||
|
|
@ -1044,12 +1191,7 @@
|
|||
"operationId": "DeleteFolder",
|
||||
"responses": {
|
||||
"204": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {}
|
||||
}
|
||||
}
|
||||
"description": "สำเร็จ"
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
|
|
@ -1057,7 +1199,9 @@
|
|||
],
|
||||
"security": [
|
||||
{
|
||||
"bearerAuth": []
|
||||
"bearerAuth": [
|
||||
"admin"
|
||||
]
|
||||
}
|
||||
],
|
||||
"parameters": [
|
||||
|
|
@ -1128,7 +1272,7 @@
|
|||
"operationId": "ListFolder",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "",
|
||||
"description": "สำเร็จ",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
|
|
@ -1139,12 +1283,19 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "เกิดข้อผิดพลาด ไม่สามารถแสดงรายการแฟ้มได้ กรุณาลองใหม่ในภายหลัง"
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"SubFolder"
|
||||
],
|
||||
"security": [],
|
||||
"security": [
|
||||
{
|
||||
"bearerAuth": []
|
||||
}
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"in": "path",
|
||||
|
|
@ -1176,7 +1327,13 @@
|
|||
"operationId": "CreateFolder",
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": ""
|
||||
"description": "สำเร็จ"
|
||||
},
|
||||
"404": {
|
||||
"description": "ไม่พบของแฟ้ม"
|
||||
},
|
||||
"500": {
|
||||
"description": "เกิดข้อผิดพลาดกับระบบจัดการไฟล์"
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
|
|
@ -1184,7 +1341,9 @@
|
|||
],
|
||||
"security": [
|
||||
{
|
||||
"bearerAuth": []
|
||||
"bearerAuth": [
|
||||
"admin"
|
||||
]
|
||||
}
|
||||
],
|
||||
"parameters": [
|
||||
|
|
@ -1238,7 +1397,10 @@
|
|||
"operationId": "EditFolder",
|
||||
"responses": {
|
||||
"204": {
|
||||
"description": ""
|
||||
"description": "สำเร็จ"
|
||||
},
|
||||
"500": {
|
||||
"description": "เกิดข้อผิดพลาดไม่สามารถย้ายไฟล์ได้"
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
|
|
@ -1246,7 +1408,9 @@
|
|||
],
|
||||
"security": [
|
||||
{
|
||||
"bearerAuth": []
|
||||
"bearerAuth": [
|
||||
"admin"
|
||||
]
|
||||
}
|
||||
],
|
||||
"parameters": [
|
||||
|
|
@ -1306,12 +1470,7 @@
|
|||
"operationId": "DeleteFolder",
|
||||
"responses": {
|
||||
"204": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {}
|
||||
}
|
||||
}
|
||||
"description": "สำเร็จ"
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
|
|
@ -1319,7 +1478,9 @@
|
|||
],
|
||||
"security": [
|
||||
{
|
||||
"bearerAuth": []
|
||||
"bearerAuth": [
|
||||
"admin"
|
||||
]
|
||||
}
|
||||
],
|
||||
"parameters": [
|
||||
|
|
@ -1482,6 +1643,9 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"upload": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"keyword": {
|
||||
"items": {
|
||||
"type": "string"
|
||||
|
|
@ -1519,6 +1683,7 @@
|
|||
"createdAt",
|
||||
"updatedBy",
|
||||
"updatedAt",
|
||||
"upload",
|
||||
"keyword",
|
||||
"category",
|
||||
"description",
|
||||
|
|
@ -1758,6 +1923,9 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"upload": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"keyword": {
|
||||
"items": {
|
||||
"type": "string"
|
||||
|
|
@ -1798,6 +1966,7 @@
|
|||
"createdAt",
|
||||
"updatedBy",
|
||||
"updatedAt",
|
||||
"upload",
|
||||
"keyword",
|
||||
"category",
|
||||
"description",
|
||||
|
|
|
|||
|
|
@ -14,32 +14,30 @@ const jwtVerify = createVerifier({
|
|||
},
|
||||
});
|
||||
|
||||
export function expressAuthentication(
|
||||
export async function expressAuthentication(
|
||||
request: express.Request,
|
||||
securityName: string,
|
||||
scopes?: string[],
|
||||
) {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
if (securityName !== "bearerAuth") reject(new Error("Unknown authentication method."));
|
||||
if (process.env.AUTH_BYPASS) return { preferred_username: "bypassed" };
|
||||
|
||||
const token = request.headers["authorization"]?.includes("Bearer ")
|
||||
? request.headers["authorization"].split(" ")[1]
|
||||
: null;
|
||||
if (securityName !== "bearerAuth") throw new Error("Unknown authentication method.");
|
||||
|
||||
if (!token) return reject(new HttpError(HttpStatusCode.UNAUTHORIZED, "No token provided."));
|
||||
const token = request.headers["authorization"]?.includes("Bearer ")
|
||||
? request.headers["authorization"].split(" ")[1]
|
||||
: null;
|
||||
|
||||
const payload = await jwtVerify(token).catch((_) => null);
|
||||
if (!token) throw new HttpError(HttpStatusCode.UNAUTHORIZED, "No token provided.");
|
||||
|
||||
if (!payload) {
|
||||
return reject(new HttpError(HttpStatusCode.UNAUTHORIZED, "Invalid token provided."));
|
||||
}
|
||||
const payload = await jwtVerify(token).catch((_) => null);
|
||||
|
||||
if (scopes && !scopes.every((v) => payload.resource_access[payload.azp].roles.includes(v))) {
|
||||
return reject(
|
||||
new HttpError(HttpStatusCode.FORBIDDEN, "You are not allowed to perform this action."),
|
||||
);
|
||||
}
|
||||
if (!payload) {
|
||||
throw new HttpError(HttpStatusCode.UNAUTHORIZED, "Invalid token provided.");
|
||||
}
|
||||
|
||||
return resolve(payload);
|
||||
});
|
||||
if (scopes && !scopes.some((v) => payload.resource_access[payload.azp].roles.includes(v))) {
|
||||
throw new HttpError(HttpStatusCode.FORBIDDEN, "You are not allowed to perform this action.");
|
||||
}
|
||||
|
||||
return payload;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,38 +1,27 @@
|
|||
import { EhrFolder } from "../interfaces/ehr-fs";
|
||||
import * as Minio from "minio";
|
||||
import minioClient from "../storage";
|
||||
import minioClient from "../minio";
|
||||
|
||||
/**
|
||||
* Remove slash at the start and ensure slash at the end of the path
|
||||
* @param path - path to be check and ensure
|
||||
* @returns path without / at start and end with trailing slash
|
||||
*/
|
||||
function safePath(path: string) {
|
||||
return path.replace(/^\/|\/$/g, "") + "/";
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace illegal character eg. ? % < > / \ : | that can't be in path with "-".
|
||||
* Used when create folder / dir through api
|
||||
* Replace illegal character eg. ? % < > / \ : | that can't be in path with other char with dash by default.
|
||||
* @param path - string to check and replace
|
||||
* @returns path with illegal character replaced with "-"
|
||||
* @param replace - string to replace illegal character
|
||||
* @returns illegal character replaced path
|
||||
*/
|
||||
export function replaceIllegalChars(path: string, replaceChar = "-") {
|
||||
return path.replace(/[/\\?%*:|"<>]/g, replaceChar);
|
||||
export function replaceIllegalChars(path: string, replace = "-") {
|
||||
return path.replace(/[/\\?%*:|"<>]/g, replace);
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility function to check for .keep file if it is exist or not.
|
||||
* @returns true if .keep exist, false otherwise
|
||||
* Check if folder really exist by using ".keep" object.
|
||||
*/
|
||||
export async function pathExist(path: string): Promise<boolean> {
|
||||
return await minioClient
|
||||
.statObject("ehr", `${safePath(path)}.keep`)
|
||||
.then((_) => true)
|
||||
.catch((e) => {
|
||||
return Boolean(
|
||||
await minioClient.statObject("ehr", `${path.replace(/^\/|\/$/g, "")}/.keep`).catch((e) => {
|
||||
if (e.code === "NotFound") return false;
|
||||
throw new Error("Object Storage Error");
|
||||
});
|
||||
throw new Error("เกิดข้อผิดพลาดกับระบบจัดการไฟล์");
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -40,55 +29,62 @@ export async function pathExist(path: string): Promise<boolean> {
|
|||
* @param path - path to list
|
||||
* @return list of folder with metadata
|
||||
*/
|
||||
export function listFolder(path?: string): Promise<EhrFolder[]> {
|
||||
if (path) path = safePath(path);
|
||||
export async function listFolder(bucket: string, path?: string): Promise<EhrFolder[]> {
|
||||
if (path) path = `${path.replace(/^\/|\/$/g, "")}/`;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const folder: EhrFolder[] = [];
|
||||
|
||||
const stream = minioClient.listObjectsV2("ehr", path ?? "");
|
||||
const list = await new Promise<{ pathname: string; name: string }[]>((resolve, reject) => {
|
||||
const item: { pathname: string; name: string }[] = [];
|
||||
|
||||
const stream = minioClient.listObjectsV2(bucket, path ?? "");
|
||||
stream.on("data", (v) => {
|
||||
if (!(v && v.prefix)) return;
|
||||
|
||||
folder.push({
|
||||
pathname: v.prefix,
|
||||
name: v.prefix.slice(path?.length).split("/")[0],
|
||||
createdAt: "N/A",
|
||||
createdBy: "N/A",
|
||||
});
|
||||
});
|
||||
|
||||
stream.on("end", async () => {
|
||||
for (let i = 0; i < folder.length; i++) {
|
||||
const stat = await minioClient
|
||||
.statObject("ehr", `${folder[i].pathname}.keep`)
|
||||
.catch((e) => console.error(`Error List Folder: ${folder[i].pathname}`, e));
|
||||
|
||||
if (!stat) continue;
|
||||
|
||||
folder[i] = {
|
||||
...folder[i],
|
||||
createdAt: stat.metaData.createdat ?? "N/A",
|
||||
createdBy: stat.metaData.createdby ?? "N/A",
|
||||
};
|
||||
if (v && v.prefix) {
|
||||
item.push({
|
||||
pathname: v.prefix,
|
||||
name: v.prefix.slice(path?.length).split("/")[0],
|
||||
});
|
||||
}
|
||||
resolve(folder);
|
||||
});
|
||||
|
||||
stream.on("error", () => reject(new Error("Object storage error occured.")));
|
||||
stream.on("end", () => resolve(item));
|
||||
stream.on("error", () => reject(new Error("เกิดข้อผิดพลาดกับระบบจัดการไฟล์")));
|
||||
});
|
||||
|
||||
const folder = await Promise.all(
|
||||
list.map(async (v) => {
|
||||
// Get stat from hidden object that used to mark as folder as minio doesn't really have folder
|
||||
const stat = await minioClient
|
||||
.statObject(bucket, `${v.pathname}.keep`)
|
||||
.catch((e) => console.error(`MinIO Error: ${e}`));
|
||||
|
||||
if (!stat) return undefined;
|
||||
|
||||
const { createdat, createdby } = stat.metaData;
|
||||
|
||||
return {
|
||||
...v,
|
||||
createdAt: createdat ?? "n/a",
|
||||
createdBy: createdby ?? "n/a",
|
||||
} satisfies EhrFolder;
|
||||
}),
|
||||
);
|
||||
|
||||
return folder.filter((v: (typeof folder)[number]): v is EhrFolder => !!v);
|
||||
}
|
||||
|
||||
export async function listItem(path: string, recursive = false): Promise<Minio.BucketItem[]> {
|
||||
export async function listItem(
|
||||
bucket: string,
|
||||
path: string,
|
||||
recursive = false,
|
||||
): Promise<Minio.BucketItem[]> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const stream = minioClient.listObjectsV2("ehr", path, recursive);
|
||||
const stream = minioClient.listObjectsV2(bucket, path, recursive);
|
||||
const item: Minio.BucketItem[] = [];
|
||||
|
||||
stream.on("data", (v) => {
|
||||
if (v && v.name) item.push(v);
|
||||
});
|
||||
stream.on("end", () => resolve(item));
|
||||
stream.on("error", () => reject(new Error("Object storage error occured.")));
|
||||
stream.on("error", () => reject(new Error("เกิดข้อผิดพลาดกับระบบจัดการไฟล์")));
|
||||
});
|
||||
}
|
||||
|
||||
export const copyCond = new Minio.CopyConditions();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue