fix: path leading slash and remove unused endpoint

This commit is contained in:
Methapon2001 2023-12-15 15:15:50 +07:00
parent d1abd6684e
commit 51528afc74
No known key found for this signature in database
GPG key ID: 849924FEF46BD132
12 changed files with 295 additions and 5364 deletions

View file

@ -1,218 +0,0 @@
import {
Body,
Controller,
Delete,
Get,
Path,
Post,
Put,
Route,
Security,
SuccessResponse,
Tags,
Request,
Response,
Example,
} from "tsoa";
import minioClient from "../minio";
import esClient from "../elasticsearch";
import { copyCond, listFolder, listItem, replaceIllegalChars } from "../utils/minio";
import HttpStatusCode from "../interfaces/http-status";
import { StorageFile, StorageFolder } from "../interfaces/storage-fs";
import { getInstance } from "../lib/websocket";
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("ตู้เอกสาร")
@Security("bearerAuth")
@Response(
HttpStatusCode.INTERNAL_SERVER_ERROR,
"เกิดข้อผิดพลาด ไม่สามารถแสดงรายการตู้เอกสารได้ กรุณาลองใหม่ในภายหลัง",
)
@SuccessResponse(HttpStatusCode.OK, "สำเร็จ")
@Example([
{
path: "ตู้เอกสาร 1/",
name: "ตู้เอกสาร 1",
createdAt: "2021-07-20T12:33:13.018Z",
createdBy: "admin",
},
{
path: "ตู้เอกสาร 2/",
name: "ตู้เอกสาร 2",
createdAt: "2022-01-23T16:05:02.114Z",
createdBy: "admin",
},
])
public async listCabinet(): Promise<StorageFolder[]> {
const list = await listFolder(DEFAULT_BUCKET!).catch((e) =>
console.error(`Error List Folder: ${e}`),
);
if (!list)
throw new Error("เกิดข้อผิดพลาด ไม่สามารถแสดงรายการตู้เอกสารได้ กรุณาลองใหม่ในภายหลัง");
return list;
}
@Post("/")
@Tags("ตู้เอกสาร")
@Security("bearerAuth", ["admin", "management-role"])
@Response(HttpStatusCode.INTERNAL_SERVER_ERROR, "เกิดข้อผิดพลาดกับระบบจัดการไฟล์")
@SuccessResponse(HttpStatusCode.CREATED, "สำเร็จ")
public async createCabinet(
@Request() request: { user: { preferred_username: string } },
@Body()
body: {
/**
* @example "ตู้เอกสาร 1"
*/
name: string;
},
) {
const meta = {
createdAt: new Date().toISOString(),
createdBy: request.user.preferred_username,
};
const created = await minioClient
.putObject(DEFAULT_BUCKET!, `${replaceIllegalChars(body.name)}/.keep`, "", 0, meta)
.catch((e) => console.error(e));
if (!created) throw new Error("เกิดข้อผิดพลาดกับระบบจัดการไฟล์");
const io = getInstance();
io?.emit("CreateFolder", {
pathname: `${replaceIllegalChars(body.name)}/`,
name: replaceIllegalChars(body.name),
...meta,
});
return this.setStatus(HttpStatusCode.CREATED);
}
/**
* @example cabinetName "ตู้เอกสาร 1"
*/
@Put("/{cabinetName}")
@Tags("ตู้เอกสาร")
@Security("bearerAuth", ["admin", "management-role"])
@Response(HttpStatusCode.INTERNAL_SERVER_ERROR, "เกิดข้อผิดพลาดไม่สามารถย้ายไฟล์ได้")
@SuccessResponse(HttpStatusCode.NO_CONTENT, "สำเร็จ")
public async editCabinet(
@Path() cabinetName: string,
@Body()
body: {
/**
* @example "ตู้เอกสารใหม่"
*/
name: string;
},
): Promise<void> {
const path = `${cabinetName}/`;
const list = await listItem(DEFAULT_BUCKET!, path, true);
await Promise.all(
list.map(async (current) => {
if (!current.name) return;
const base = `${replaceIllegalChars(body.name)}/`;
const destination = `${base}${current.name.slice(path.length)}`;
const source = `/${DEFAULT_BUCKET}/${current.name}`;
return await minioClient
.copyObject(DEFAULT_BUCKET!, destination, source, copyCond)
.then(async () => {
if (current.name.includes(".keep")) {
return await minioClient.removeObject(DEFAULT_BUCKET!, current.name);
}
const search = await esClient.search<
StorageFile & { attachment: Record<string, string> }
>({
index: DEFAULT_INDEX!,
query: { match: { pathname: current.name } },
});
if (search && search.hits.hits.length === 0) throw new Error("ไม่พบข้อมูลในฐานข้อมูล");
const data = search.hits.hits[0];
await esClient.update({
index: DEFAULT_INDEX!,
id: data._id,
doc: {
pathname: destination,
path: destination.split("/").slice(0, -1).join("/") + "/",
},
refresh: "wait_for",
});
await minioClient.removeObject(DEFAULT_BUCKET!, current.name);
})
.catch((e) => {
console.error(e);
throw new Error("เกิดข้อผิดพลาด ไม่สามารถย้ายไฟล์ได้");
});
}),
);
const io = getInstance();
io?.emit("EditFolder", { from: `${cabinetName}/`, to: `${replaceIllegalChars(body.name)}/` });
return this.setStatus(HttpStatusCode.NO_CONTENT);
}
/**
* @example cabinetName "ตู้เอกสาร 1"
*/
@Delete("/{cabinetName}")
@Tags("ตู้เอกสาร")
@Security("bearerAuth", ["admin", "management-role"])
@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(DEFAULT_BUCKET!, `${cabinetName}/`, true);
stream.on("data", (v) => {
if (v && v.name) objects.push(v.name);
});
stream.on("close", async () =>
resolve(await minioClient.removeObjects(DEFAULT_BUCKET!, objects)),
);
stream.on("error", () => reject(new Error("เกิดข้อผิดพลาด ไม่สามารถลบไฟล์ได้")));
});
const io = getInstance();
io?.emit("DeleteFolder", { pathname: `${cabinetName}/` });
return this.setStatus(HttpStatusCode.NO_CONTENT);
}
/**
* @example cabinetName "ตู้เอกสาร 1"
*/
@Get("/{cabinetName}/size")
@Tags("ตู้เอกสาร")
@Security("bearerAuth")
@SuccessResponse(HttpStatusCode.OK, "สำเร็จ")
public async calc(@Path() cabinetName: string) {
const list = await listItem(DEFAULT_BUCKET!, `${cabinetName}/`, true).catch((e) =>
console.error(`Error List Folder: ${e}`),
);
if (!list) throw new Error("เกิดข้อผิดพลาด ไม่สามารถแสดงรายการแฟ้มได้ กรุณาลองใหม่ในภายหลัง");
return { size: list.reduce<number>((a, c) => a + c.size, 0) };
}
}

View file

@ -1,245 +0,0 @@
import {
Body,
Controller,
Delete,
Get,
Path,
Post,
Put,
Route,
Security,
SuccessResponse,
Tags,
Request,
Response,
Example,
} from "tsoa";
import minioClient from "../minio";
import esClient from "../elasticsearch";
import { copyCond, listFolder, listItem, pathExist, replaceIllegalChars } from "../utils/minio";
import HttpStatusCode from "../interfaces/http-status";
import { StorageFile, StorageFolder } from "../interfaces/storage-fs";
import HttpError from "../interfaces/http-error";
import { getInstance } from "../lib/websocket";
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 {
/**
* @example cabinetName "ตู้เอกสาร 1"
*/
@Get("/")
@Tags("ลิ้นชัก")
@Security("bearerAuth")
@Response(
HttpStatusCode.INTERNAL_SERVER_ERROR,
"เกิดข้อผิดพลาด ไม่สามารถแสดงรายการลิ้นชักได้ กรุณาลองใหม่ในภายหลัง",
)
@SuccessResponse(HttpStatusCode.OK, "สำเร็จ")
@Example([
{
path: "ตู้เอกสาร 1/ลิ้นชัก 1/",
name: "ลิ้นชัก 1",
createdAt: "2021-07-20T12:33:13.018Z",
createdBy: "admin",
},
{
path: "ตู้เอกสาร 1/ลิ้นชัก 2/",
name: "ลิ้นชัก 2",
createdAt: "2022-01-23T16:05:02.114Z",
createdBy: "admin",
},
])
public async listDrawer(@Path() cabinetName: string): Promise<StorageFolder[]> {
const list = await listFolder(DEFAULT_BUCKET!, `${cabinetName}/`).catch((e) =>
console.error(`Error List Folder: ${e}`),
);
if (!list)
throw new Error("เกิดข้อผิดพลาด ไม่สามารถแสดงรายการลิ้นชักได้ กรุณาลองใหม่ในภายหลัง");
return list;
}
/**
* @example cabinetName "ตู้เอกสาร 1"
*/
@Post("/")
@Tags("ลิ้นชัก")
@Security("bearerAuth", ["admin", "management-role"])
@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: {
/**
* @example "ลิ้นชัก 1"
*/
name: string;
},
) {
const basePath = `${cabinetName}/`;
if (!(await pathExist(DEFAULT_BUCKET!, basePath))) {
throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบตำแหน่งที่ต้องการสร้างลิ้นชัก");
}
const meta = {
createdAt: new Date().toISOString(),
createdBy: request.user.preferred_username,
};
const created = await minioClient
.putObject(DEFAULT_BUCKET!, `${basePath}${replaceIllegalChars(body.name)}/.keep`, "", 0, meta)
.catch((e) => console.error(e));
if (!created) throw new Error("เกิดข้อผิดพลาดกับระบบจัดการไฟล์");
const io = getInstance();
io?.emit("CreateFolder", {
pathname: `${basePath}${replaceIllegalChars(body.name)}/`,
name: replaceIllegalChars(body.name),
...meta,
});
return this.setStatus(HttpStatusCode.CREATED);
}
/**
* @example cabinetName "ตู้เอกสาร 1"
* @example drawerName "ลิ้นชัก 1"
*/
@Put("/{drawerName}")
@Tags("ลิ้นชัก")
@Security("bearerAuth", ["admin", "management-role"])
@Response(HttpStatusCode.INTERNAL_SERVER_ERROR, "เกิดข้อผิดพลาดไม่สามารถย้ายไฟล์ได้")
@SuccessResponse(HttpStatusCode.NO_CONTENT, "สำเร็จ")
public async editDrawer(
@Path() cabinetName: string,
@Path() drawerName: string,
@Body()
body: {
/**
* @example "ลิ้นชักใหม่"
*/
name: string;
},
): Promise<void> {
const path = `${cabinetName}/${drawerName}/`;
const list = await listItem(DEFAULT_BUCKET!, path, true);
await Promise.all(
list.map(async (current) => {
if (!current.name) return;
const base = `${cabinetName}/${replaceIllegalChars(body.name)}/`;
const destination = `${base}${current.name.slice(path.length)}`;
const source = `/${DEFAULT_BUCKET}/${current.name}`;
return await minioClient
.copyObject(DEFAULT_BUCKET!, destination, source, copyCond)
.then(async () => {
if (current.name.includes(".keep")) {
return await minioClient.removeObject(DEFAULT_BUCKET!, current.name);
}
const search = await esClient.search<
StorageFile & { attachment: Record<string, string> }
>({
index: DEFAULT_INDEX!,
query: { match: { pathname: current.name } },
});
if (search && search.hits.hits.length === 0) throw new Error("ไม่พบข้อมูลในฐานข้อมูล");
const data = search.hits.hits[0];
await esClient.update({
index: DEFAULT_INDEX!,
id: data._id,
doc: {
pathname: destination,
path: destination.split("/").slice(0, -1).join("/") + "/",
},
refresh: "wait_for",
});
await minioClient.removeObject(DEFAULT_BUCKET!, current.name);
})
.catch((e) => {
console.error(e);
throw new Error("เกิดข้อผิดพลาด ไม่สามารถย้ายไฟล์ได้");
});
}),
);
const io = getInstance();
io?.emit("EditFolder", {
from: `${cabinetName}/${drawerName}/`,
to: `${cabinetName}/${replaceIllegalChars(body.name)}/`,
});
return this.setStatus(HttpStatusCode.NO_CONTENT);
}
/**
* @example cabinetName "ตู้เอกสาร 1"
* @example drawerName "ลิ้นชัก 1"
*/
@Delete("/{drawerName}")
@Tags("ลิ้นชัก")
@Security("bearerAuth", ["admin", "management-role"])
@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(
DEFAULT_BUCKET!,
`${cabinetName}/${drawerName}/`,
true,
);
stream.on("data", (v) => {
if (v && v.name) objects.push(v.name);
});
stream.on("close", async () =>
resolve(await minioClient.removeObjects(DEFAULT_BUCKET!, objects)),
);
stream.on("error", () => reject(new Error("เกิดข้อผิดพลาด ไม่สามารถลบไฟล์ได้")));
});
const io = getInstance();
io?.emit("DeleteFolder", {
pathname: `${cabinetName}/${drawerName}/`,
});
return this.setStatus(HttpStatusCode.NO_CONTENT);
}
/**
* @example cabinetName "ตู้เอกสาร 1"
* @example drawerName "ลิ้นชัก 1"
*/
@Get("/{drawerName}/size")
@Tags("ลิ้นชัก")
@Security("bearerAuth")
@SuccessResponse(HttpStatusCode.OK, "สำเร็จ")
public async calc(@Path() cabinetName: string, @Path() drawerName: string) {
const list = await listItem(DEFAULT_BUCKET!, `${cabinetName}/${drawerName}/`, true).catch((e) =>
console.error(`Error List Folder: ${e}`),
);
if (!list) throw new Error("เกิดข้อผิดพลาด ไม่สามารถแสดงรายการแฟ้มได้ กรุณาลองใหม่ในภายหลัง");
return { size: list.reduce<number>((a, c) => a + c.size, 0) };
}
}

View file

@ -1,438 +0,0 @@
import {
Body,
Controller,
Delete,
Example,
Get,
Patch,
Path,
Post,
Request,
Response,
Route,
Security,
SuccessResponse,
Tags,
} from "tsoa";
import esClient from "../elasticsearch";
import minioClient from "../minio";
import HttpStatusCode from "../interfaces/http-status";
import { StorageFile } from "../interfaces/storage-fs";
import HttpError from "../interfaces/http-error";
import { copyCond, pathExist, replaceIllegalChars } from "../utils/minio";
import { getInstance } from "../lib/websocket";
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}/file")
export class FileController extends Controller {
/**
* @example cabinetName "ตู้เอกสาร 1"
* @example drawerName "ลิ้นชัก 1"
* @example folderName "แฟ้ม 1"
*/
@Get("/")
@Tags("ไฟล์")
@Security("bearerAuth")
@SuccessResponse(HttpStatusCode.OK, "สำเร็จ")
@Example([
{
pathname: "ตู้เอกสาร 1/ลิ้นชัก 1/แฟ้ม 1/เอกสาร 1",
path: "ตู้เอกสาร 1/ลิ้นชัก 1/แฟ้ม 1/",
title: "เอกสาร",
description: "เอกสารการเงิน",
category: ["บัญชี"],
keyword: ["เงิน", "บัญชี", "รายจ่าย", "รายรับ"],
upload: false,
fileName: "เอกสาร 1",
fileSize: 10240,
fileType: "application/pdf",
createdAt: "2021-07-20T12:33:13.018Z",
createdBy: "admin",
updatedAt: "2021-07-20T12:33:13.018Z",
updatedBy: "admin",
},
])
public async getFile(
@Path() cabinetName: string,
@Path() drawerName: string,
@Path() folderName: string,
): Promise<StorageFile[]> {
const search = await esClient.search<StorageFile & { attachment: Record<string, string> }>({
index: DEFAULT_INDEX!,
query: {
match: {
path: `${cabinetName}/${drawerName}/${folderName}/`,
},
},
size: 10000,
});
const records = search.hits.hits
.map((v) => {
if (v._source) {
const { attachment, ...rest } = v._source;
return rest satisfies StorageFile;
}
})
.filter((v: StorageFile | undefined): v is StorageFile => !!v);
return records;
}
/**
* @example cabinetName "ตู้เอกสาร 1"
* @example drawerName "ลิ้นชัก 1"
* @example folderName "แฟ้ม 1"
*/
@Post("/")
@Tags("ไฟล์")
@Security("bearerAuth", ["admin", "management-role"])
@Response(
HttpStatusCode.NOT_FOUND,
"ตำแหน่งที่ระบุไม่พบ กรุณาเตรียมตำแหน่งที่ต้องการก่อนดำเนินการ",
)
@SuccessResponse(HttpStatusCode.CREATED, "สำเร็จ")
@Example({
pathname: "ตู้เอกสาร 1/ลิ้นชัก 1/แฟ้ม 1/เอกสาร 1",
path: "ตู้เอกสาร 1/ลิ้นชัก 1/แฟ้ม 1/",
title: "เอกสาร",
description: "เอกสารการเงิน",
category: ["บัญชี"],
keyword: ["เงิน", "บัญชี", "รายจ่าย", "รายรับ"],
upload: false,
fileName: "เอกสาร 1",
fileSize: 10240,
fileType: "application/pdf",
createdAt: "2021-07-20T12:33:13.018Z",
createdBy: "admin",
updatedAt: "2021-07-20T12:33:13.018Z",
updatedBy: "admin",
})
public async uploadFile(
@Request() request: { user: { preferred_username: string } },
@Body()
body: {
/**
* @example "เอกสาร 1"
*/
file: string;
/**
* @example "เอกสาร"
*/
title?: string;
/**
* @example "เอกสารการเงิน"
*/
description?: string;
/**
* @example ["บัญชี"]
*/
category?: string[];
/**
* @example ["เงิน", "บัญชี", "รายจ่าย", "รายรับ"]
*/
keyword?: string[];
},
@Path() cabinetName: string,
@Path() drawerName: string,
@Path() folderName: string,
) {
if (body.file.length > 85) {
throw new HttpError(HttpStatusCode.BAD_REQUEST, "ชื่อไฟล์ยาวเกินกำหนด");
}
const basePath = `${cabinetName}/${drawerName}/${folderName}/`;
const pathname = `${basePath}${replaceIllegalChars(body.file)}`;
if (!(await pathExist(DEFAULT_BUCKET!, basePath))) {
throw new HttpError(
HttpStatusCode.NOT_FOUND,
"ตำแหน่งที่ระบุไม่พบ กรุณาเตรียมตำแหน่งที่ต้องการก่อนดำเนินการ",
);
}
const result = await esClient
.search<StorageFile & { attachment?: Record<string, unknown> }>({
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.length > 0 ? result.hits.hits[0]._source : false;
const metadata: Partial<StorageFile> = {
pathname,
path: basePath,
fileName: replaceIllegalChars(body.file),
fileSize: 0,
fileType: "",
title: body.title ?? "",
description: body.description ?? "",
category: body.category ?? [],
keyword: body.keyword ?? [],
upload: false,
createdAt: new Date().toISOString(),
createdBy: rec ? rec.createdBy : "n/a",
updatedAt: new Date().toISOString(),
updatedBy: request.user.preferred_username ?? "n/a",
};
await esClient.index({
index: DEFAULT_INDEX!,
document: metadata,
refresh: "wait_for",
});
const io = getInstance();
io?.emit("FileUploadRequest", metadata);
return {
...body,
createdAt: metadata.createdAt,
createdBy: metadata.createdBy,
updatedAt: metadata.updatedAt,
updatedBy: metadata.updatedBy,
upload: await minioClient.presignedPutObject(DEFAULT_BUCKET!, pathname),
};
}
/**
* @example cabinetName "ตู้เอกสาร 1"
* @example drawerName "ลิ้นชัก 1"
* @example folderName "แฟ้ม 1"
* @example fileName "เอกสาร 1"
*/
@Patch("/{fileName}")
@Tags("ไฟล์")
@Security("bearerAuth", ["admin", "management-role"])
@Response(HttpStatusCode.NOT_FOUND, "ไม่พบตำแหน่งที่ต้องการสร้างแฟ้ม")
@Response(HttpStatusCode.NO_CONTENT, "สำเร็จ")
@SuccessResponse(HttpStatusCode.OK, "สำเร็จ")
public async updateFile(
@Request() request: { user: { preferred_username: string } },
@Path() cabinetName: string,
@Path() drawerName: string,
@Path() folderName: string,
@Path() fileName: string,
@Body()
body: {
/**
* @example "เอกสารใหม่"
*/
file?: string;
/**
* @example "เอกสารการเงิน"
*/
title?: string;
/**
* @example "เอกสารการเงินฉบับใหม่"
*/
description?: string;
/**
* @example ["บัญชี"]
*/
category?: string[];
/**
* @example ["เงิน", "บัญชี", "รายจ่าย", "รายรับ"]
*/
keyword?: string[];
},
): Promise<void | { upload: string }> {
if (body.file && body.file.length > 85) {
throw new HttpError(HttpStatusCode.BAD_REQUEST, "ชื่อไฟล์ยาวเกินกำหนด");
}
const basePath = `${cabinetName}/${drawerName}/${folderName}/`;
const pathname = `${basePath}${fileName}`;
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 { file, ...metadata } = body;
// assume user will probably replace file by re-upload but maybe just rename
if (file) {
const destination = `${basePath}${replaceIllegalChars(file)}`;
const source = `/${DEFAULT_BUCKET}/${basePath}${fileName}`;
const copy = await minioClient.copyObject(DEFAULT_BUCKET!, destination, source, copyCond);
if (copy) {
const search = await esClient
.search<StorageFile & { attachment?: Record<string, unknown> }>({
index: DEFAULT_INDEX!,
query: { match: { pathname } },
})
.catch((e) => console.error(e));
if (search && search.hits.hits.length > 0 && search.hits.hits[0]._source) {
const { _index: index, _id: id } = search.hits.hits[0];
const meta = {
updatedAt: new Date().toISOString(),
updatedBy: request.user.preferred_username ?? "n/a",
};
await esClient
.update({
index,
id,
doc: {
...metadata,
pathname: destination,
path: basePath,
fileName: replaceIllegalChars(file),
...meta,
},
refresh: "wait_for",
})
.then(() => minioClient.removeObject(DEFAULT_BUCKET!, pathname));
const io = getInstance();
io?.emit("FileUpdateMove", {
from: search.hits.hits[0]._source,
to: {
...search.hits.hits[0]._source,
...metadata,
pathname: destination,
path: basePath,
fileName: replaceIllegalChars(file),
...meta,
},
});
} else {
await minioClient.removeObject(DEFAULT_BUCKET!, pathname);
}
}
} else {
const search = await esClient
.search<StorageFile & { attachment?: Record<string, unknown> }>({
index: DEFAULT_INDEX!,
query: { match: { pathname } },
})
.catch((e) => console.error(e));
if (search && search.hits.hits.length > 0 && search.hits.hits[0]._source) {
const { _index: index, _id: id } = search.hits.hits[0];
const meta = {
updatedAt: new Date().toISOString(),
updatedBy: request.user.preferred_username ?? "n/a",
};
await esClient.update({
index,
id,
doc: { ...metadata, ...meta },
refresh: "wait_for",
});
const updated: StorageFile = {
...search.hits.hits[0]._source,
...metadata,
...meta,
};
const io = getInstance();
io?.emit("FileUpdate", updated);
}
}
return body.file
? {
upload: await minioClient.presignedPutObject(
DEFAULT_BUCKET!,
`${basePath}${replaceIllegalChars(body.file) ?? fileName}`,
),
}
: this.setStatus(HttpStatusCode.NO_CONTENT);
}
/**
* @example cabinetName "ตู้เอกสาร 1"
* @example drawerName "ลิ้นชัก 1"
* @example folderName "แฟ้ม 1"
* @example fileName "เอกสารใหม่"
*/
@Delete("/{fileName}")
@Tags("ไฟล์")
@Security("bearerAuth", ["admin", "management-role"])
@SuccessResponse(HttpStatusCode.OK, "สำเร็จ")
public async deleteFile(
@Path() cabinetName: string,
@Path() drawerName: string,
@Path() folderName: string,
@Path() fileName: string,
) {
await minioClient.removeObject(
DEFAULT_BUCKET!,
`${cabinetName}/${drawerName}/${folderName}/${fileName}`,
);
await esClient.deleteByQuery({
index: DEFAULT_INDEX!,
query: {
match: { pathname: `${cabinetName}/${drawerName}/${folderName}/${fileName}` },
},
});
return this.setStatus(HttpStatusCode.NO_CONTENT);
}
/**
* @example cabinetName "ตู้เอกสาร 1"
* @example drawerName "ลิ้นชัก 1"
* @example folderName "แฟ้ม 1"
* @example fileName "เอกสารใหม่"
*/
@Get("/{fileName}")
@Tags("ดาวน์โหลด")
@Security("bearerAuth")
@SuccessResponse(HttpStatusCode.OK, "สำเร็จ")
public async downloadFile(
@Path() cabinetName: string,
@Path() drawerName: string,
@Path() folderName: string,
@Path() fileName: string,
) {
const search = await esClient.search<StorageFile & { attachment: Record<string, string> }>({
index: DEFAULT_INDEX!,
query: {
match: { pathname: `${cabinetName}/${drawerName}/${folderName}/${fileName}` },
},
});
if (search && search.hits.hits.length === 0) {
throw new HttpError(HttpStatusCode.NOT_FOUND, "Not found");
}
const { attachment, ...rest } = search.hits.hits[0]._source!;
return {
...rest,
download: await minioClient.presignedGetObject(
DEFAULT_BUCKET!,
`${cabinetName}/${drawerName}/${folderName}/${fileName}`,
),
};
}
}

View file

@ -1,262 +0,0 @@
import {
Body,
Controller,
Delete,
Get,
Path,
Post,
Put,
Route,
Security,
SuccessResponse,
Tags,
Request,
Response,
Example,
} from "tsoa";
import minioClient from "../minio";
import esClient from "../elasticsearch";
import { copyCond, listFolder, listItem, pathExist, replaceIllegalChars } from "../utils/minio";
import HttpStatusCode from "../interfaces/http-status";
import { StorageFile, StorageFolder } from "../interfaces/storage-fs";
import HttpError from "../interfaces/http-error";
import { getInstance } from "../lib/websocket";
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 {
/**
* @example cabinetName "ตู้เอกสาร 1"
* @example drawerName "ลิ้นชัก 1"
*/
@Get("/")
@Tags("แฟ้ม")
@Security("bearerAuth")
@Response(
HttpStatusCode.INTERNAL_SERVER_ERROR,
"เกิดข้อผิดพลาด ไม่สามารถแสดงรายการแฟ้มได้ กรุณาลองใหม่ในภายหลัง",
)
@SuccessResponse(HttpStatusCode.OK, "สำเร็จ")
@Example([
{
path: "ตู้เอกสาร 1/ลิ้นชัก 1/แฟ้ม 1",
name: "แฟ้ม 1",
createdAt: "2021-07-20T12:33:13.018Z",
createdBy: "admin",
},
{
path: "ตู้เอกสาร 1/ลิ้นชัก 1/แฟ้ม 2",
name: "แฟ้ม 2",
createdAt: "2022-01-23T16:05:02.114Z",
createdBy: "admin",
},
])
public async listFolder(
@Path() cabinetName: string,
@Path() drawerName: string,
): Promise<StorageFolder[]> {
const list = await listFolder(DEFAULT_BUCKET!, `${cabinetName}/${drawerName}`).catch((e) =>
console.error(`Error List Folder: ${e}`),
);
if (!list) throw new Error("เกิดข้อผิดพลาด ไม่สามารถแสดงรายการแฟ้มได้ กรุณาลองใหม่ในภายหลัง");
return list;
}
/**
* @example cabinetName "ตู้เอกสาร 1"
* @example drawerName "ลิ้นชัก 1"
*/
@Post("/")
@Tags("แฟ้ม")
@Security("bearerAuth", ["admin", "management-role"])
@Response(HttpStatusCode.NOT_FOUND, "ไม่พบตำแหน่งที่ต้องการสร้างแฟ้ม")
@Response(HttpStatusCode.INTERNAL_SERVER_ERROR, "เกิดข้อผิดพลาดกับระบบจัดการไฟล์")
@SuccessResponse(HttpStatusCode.CREATED, "สำเร็จ")
public async createFolder(
@Request() request: { user: { preferred_username: string } },
@Body()
body: {
/**
* @example "แฟ้ม 1"
*/
name: string;
},
@Path() cabinetName: string,
@Path() drawerName: string,
) {
const basePath = `${cabinetName}/${drawerName}/`;
if (!(await pathExist(DEFAULT_BUCKET!, basePath))) {
throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบตำแหน่งที่ต้องการสร้างลิ้นชัก");
}
const meta = {
createdAt: new Date().toISOString(),
createdBy: request.user.preferred_username,
};
const created = await minioClient
.putObject(DEFAULT_BUCKET!, `${basePath}${replaceIllegalChars(body.name)}/.keep`, "", 0, meta)
.catch((e) => console.error(e));
if (!created) throw new Error("เกิดข้อผิดพลาดกับระบบจัดการไฟล์");
const io = getInstance();
io?.emit("CreateFolder", {
pathname: `${basePath}${replaceIllegalChars(body.name)}/`,
name: replaceIllegalChars(body.name),
...meta,
});
return this.setStatus(HttpStatusCode.CREATED);
}
/**
* @example cabinetName "ตู้เอกสาร 1"
* @example drawerName "ลิ้นชัก 1"
* @example folderName "แฟ้ม 1"
*/
@Put("/{folderName}")
@Tags("แฟ้ม")
@Security("bearerAuth", ["admin", "management-role"])
@Response(HttpStatusCode.INTERNAL_SERVER_ERROR, "เกิดข้อผิดพลาดไม่สามารถย้ายไฟล์ได้")
@SuccessResponse(HttpStatusCode.NO_CONTENT, "สำเร็จ")
public async editFolder(
@Body()
body: {
/**
* @example "แฟ้มใหม่"
*/
name: string;
},
@Path() cabinetName: string,
@Path() drawerName: string,
@Path() folderName: string,
) {
const path = `${cabinetName}/${drawerName}/${folderName}/`;
const list = await listItem(DEFAULT_BUCKET!, path, true);
await Promise.all(
list.map(async (current) => {
if (!current.name) return;
const base = `${cabinetName}/${drawerName}/${replaceIllegalChars(body.name)}/`;
const destination = `${base}${current.name.slice(path.length)}`;
const source = `/${DEFAULT_BUCKET}/${current.name}`;
return await minioClient
.copyObject(DEFAULT_BUCKET!, destination, source, copyCond)
.then(async () => {
if (current.name.includes(".keep")) {
return await minioClient.removeObject(DEFAULT_BUCKET!, current.name);
}
const search = await esClient.search<
StorageFile & { attachment: Record<string, string> }
>({
index: DEFAULT_INDEX!,
query: { match: { pathname: current.name } },
});
if (search && search.hits.hits.length === 0) throw new Error("ไม่พบข้อมูลในฐานข้อมูล");
const data = search.hits.hits[0];
await esClient.update({
index: DEFAULT_INDEX!,
id: data._id,
doc: {
pathname: destination,
path: destination.split("/").slice(0, -1).join("/") + "/",
},
refresh: "wait_for",
});
await minioClient.removeObject(DEFAULT_BUCKET!, current.name);
})
.catch((e) => {
console.error(e);
throw new Error("เกิดข้อผิดพลาด ไม่สามารถย้ายไฟล์ได้");
});
}),
);
const io = getInstance();
io?.emit("EditFolder", {
from: `${cabinetName}/${drawerName}/${folderName}/`,
to: `${cabinetName}/${drawerName}/${replaceIllegalChars(body.name)}/`,
});
return this.setStatus(HttpStatusCode.NO_CONTENT);
}
/**
* @example cabinetName "ตู้เอกสาร 1"
* @example drawerName "ลิ้นชัก 1"
* @example folderName "แฟ้ม 1"
*/
@Delete("/{folderName}")
@Tags("แฟ้ม")
@Security("bearerAuth", ["admin", "management-role"])
@SuccessResponse(HttpStatusCode.NO_CONTENT, "สำเร็จ")
public async deleteFolder(
@Path() cabinetName: string,
@Path() drawerName: string,
@Path() folderName: string,
) {
await new Promise<void>((resolve, reject) => {
const objects: string[] = [];
const stream = minioClient.listObjectsV2(
DEFAULT_BUCKET!,
`${cabinetName}/${drawerName}/${folderName}`,
true,
);
stream.on("data", (v) => {
if (v && v.name) objects.push(v.name);
});
stream.on("close", async () =>
resolve(await minioClient.removeObjects(DEFAULT_BUCKET!, objects)),
);
stream.on("error", () => reject(new Error("เกิดข้อผิดพลาด ไม่สามารถลบไฟล์ได้")));
});
const io = getInstance();
io?.emit("DeleteFolder", { pathname: `${cabinetName}/${drawerName}/${folderName}/` });
return this.setStatus(HttpStatusCode.NO_CONTENT);
}
/**
* @example cabinetName "ตู้เอกสาร 1"
* @example drawerName "ลิ้นชัก 1"
* @example folderName "แฟ้ม 1"
*/
@Get("/{folderName}/size")
@Tags("แฟ้ม")
@Security("bearerAuth")
@SuccessResponse(HttpStatusCode.OK, "สำเร็จ")
public async calc(
@Path() cabinetName: string,
@Path() drawerName: string,
@Path() folderName: string,
) {
const list = await listItem(
DEFAULT_BUCKET!,
`${cabinetName}/${drawerName}/${folderName}/`,
true,
).catch((e) => console.error(`Error List Folder: ${e}`));
if (!list) throw new Error("เกิดข้อผิดพลาด ไม่สามารถแสดงรายการแฟ้มได้ กรุณาลองใหม่ในภายหลัง");
return { size: list.reduce<number>((a, c) => a + c.size, 0) };
}
}

View file

@ -11,7 +11,7 @@ if (!DEFAULT_INDEX) throw Error("Default ElasticSearch index must be specified."
@Route("/search")
export class SearchController extends Controller {
@Post("/")
@Tags("ค้นหา")
@Tags("Search")
@Security("bearerAuth")
@SuccessResponse(HttpStatusCode.OK, "สำเร็จ")
public async searchFile(@Body() search: Search): Promise<StorageFile[]> {

View file

@ -6,6 +6,7 @@ import {
Post,
Put,
Request,
Response,
Route,
Security,
SuccessResponse,
@ -114,6 +115,10 @@ interface DownloadFileBody {
file: string;
}
function stripLeadingSlash(str: string) {
return str.replace(/^\//, "");
}
async function folderSize(path: string[]) {
const size = await new Promise<number>((resolve, reject) => {
const stream = minioClient.listObjectsV2(
@ -138,7 +143,7 @@ async function listFolder(path: string[], hidden: boolean = false) {
const stream = minioClient.listObjectsV2(
DEFAULT_BUCKET,
path.length === 0 ? "" : path.join("/") + "/",
stripLeadingSlash(`${path.join("/")}/`),
);
stream.on("data", (v) => {
if (v && v.prefix)
@ -182,7 +187,7 @@ async function listFile(path: string[], hidden: boolean = false) {
sort: [{ pathname: "asc" }],
query: {
bool: {
must: { match: { path: path.join("/") + "/" } },
must: { match: { path: stripLeadingSlash(`${path.join("/")}/`) } },
must_not: !hidden ? { match: { hidden: true } } : undefined,
},
},
@ -202,14 +207,14 @@ async function listFile(path: string[], hidden: boolean = false) {
return records;
}
async function checkPathExist(bucket: string, path: string) {
if (path.split("/").filter(Boolean).length === 0) return true; // root does not contain any mark
return await checkFileExist(bucket, `${path}/.keep`);
async function checkPathExist(bucket: string, path: string[]) {
if (path.filter(Boolean).length === 0) return true; // root does not contain any mark
return await checkFileExist(bucket, `${path.filter(Boolean).join("/")}/.keep`);
}
async function checkFileExist(bucket: string, pathname: string) {
return Boolean(
await minioClient.statObject(bucket, pathname).catch((e) => {
await minioClient.statObject(bucket, stripLeadingSlash(pathname)).catch((e) => {
if (e.code === "NotFound") return false;
console.error(`Storage Error: ${e}`);
throw new Error(MINIO_ERROR_MESSAGE);
@ -219,16 +224,21 @@ async function checkFileExist(bucket: string, pathname: string) {
@Route("storage")
export class StorageController extends Controller {
/**
* @summary
*/
@Post("list")
@Tags("Storage Folder", "Storage File")
@Security("bearerAuth")
@Example([
{
path: "ตู้เอกสาร 1/ลิ้นชัก 1/แฟ้ม 1",
path: "ตู้เอกสาร 1/ลิ้นชัก 1/แฟ้ม 1/",
name: "แฟ้ม 1",
createdAt: "2021-07-20T12:33:13.018Z",
createdBy: "admin",
},
{
path: "ตู้เอกสาร 1/ลิ้นชัก 1/แฟ้ม 2",
path: "ตู้เอกสาร 1/ลิ้นชัก 1/แฟ้ม 2/",
name: "แฟ้ม 2",
createdAt: "2022-01-23T16:05:02.114Z",
createdBy: "admin",
@ -243,6 +253,7 @@ export class StorageController extends Controller {
category: ["บัญชี"],
keyword: ["เงิน", "บัญชี", "รายจ่าย", "รายรับ"],
upload: false,
hidden: false,
fileName: "เอกสาร 1.pdf",
fileSize: 10240,
fileType: "application/pdf",
@ -252,15 +263,21 @@ export class StorageController extends Controller {
updatedBy: "admin",
},
])
@Tags("Storage Folder", "Storage File")
@Security("bearerAuth")
@SuccessResponse(HttpStatusCode.OK, "สำเร็จ")
public async getList(@Body() body: ListRequestBody) {
const path = body.path.filter(Boolean);
if (!(await checkPathExist(DEFAULT_BUCKET, path))) {
throw new HttpError(HttpStatusCode.NOT_FOUND, PATH_NOT_FOUND_MESSAGE);
}
if (body.operation === "folder") return await listFolder(path, body.hidden);
if (body.operation === "file") return await listFile(path);
}
/**
* @summary
*/
@Post("folder")
@Tags("Storage Folder")
@Security("bearerAuth", ["management-role", "admin"])
@ -271,7 +288,7 @@ export class StorageController extends Controller {
) {
const { path, name } = body;
if (!(await checkPathExist(DEFAULT_BUCKET, path.join("/")))) {
if (!(await checkPathExist(DEFAULT_BUCKET, path))) {
throw new HttpError(HttpStatusCode.NOT_FOUND, PATH_NOT_FOUND_MESSAGE);
}
@ -283,7 +300,7 @@ export class StorageController extends Controller {
const created = await minioClient
.putObject(
DEFAULT_BUCKET,
`${path.join("/")}/${name.replace(/[/\\?%*:|"<>#]/g, "-").trim()}/.keep`,
stripLeadingSlash(`${path.join("/")}/${name.replace(/[/\\?%*:|"<>#]/g, "-").trim()}/.keep`),
"",
0,
meta,
@ -293,7 +310,9 @@ export class StorageController extends Controller {
if (!created) throw new Error(MINIO_ERROR_MESSAGE);
io.getInstance()?.emit("FolderCreate", {
pathname: `${path.join("/")}/${name.replace(/[/\\?%*:|"<>#]/g, "-").trim()}/`,
pathname: stripLeadingSlash(
`${path.join("/")}/${name.replace(/[/\\?%*:|"<>#]/g, "-").trim()}/`,
),
name: name.replace(/[/\\?%*:|"<>#]/g, "-").trim(),
...meta,
});
@ -302,23 +321,26 @@ export class StorageController extends Controller {
}
/**
* Folder Folder (Path) Folder (Path)
*
*
*
* @summary
*/
@Put("folder")
@Tags("Storage Folder")
@Security("bearerAuth", ["management-role", "admin"])
@SuccessResponse(HttpStatusCode.NO_CONTENT, "สำเร็จ")
public async moveFolder(@Body() body: PutFolderBody) {
const src = `${body.from.path.join("/")}/${body.from.name}`;
const dst = `${body.to.path.join("/")}/${body.to.name}`;
const src = stripLeadingSlash(`${body.from.path.join("/")}/${body.from.name}`);
const dst = stripLeadingSlash(`${body.to.path.join("/")}/${body.to.name}`);
if (!(await checkPathExist(DEFAULT_BUCKET, src))) {
if (!(await checkPathExist(DEFAULT_BUCKET, src.split("/")))) {
throw new HttpError(HttpStatusCode.NOT_FOUND, PATH_NOT_FOUND_MESSAGE);
}
if (await checkPathExist(DEFAULT_BUCKET, dst)) {
if (await checkPathExist(DEFAULT_BUCKET, dst.split("/"))) {
throw new HttpError(HttpStatusCode.CONFLICT, PATH_ALREADY_EXIST);
}
if (!(await checkPathExist(DEFAULT_BUCKET, body.to.path.join("/")))) {
if (!(await checkPathExist(DEFAULT_BUCKET, body.to.path))) {
throw new HttpError(HttpStatusCode.PRECONDITION_FAILED, "ไม่พบตำแหน่งที่ต้องการย้าย");
}
@ -336,7 +358,7 @@ export class StorageController extends Controller {
await Promise.all(
list.map(async (v) => {
const from = `/${DEFAULT_BUCKET}/${v.pathname}`;
const to = `${dst}/${v.pathname.slice(`${src}/`.length)}`;
const to = stripLeadingSlash(`${dst}/${v.pathname.slice(`${src}/`.length)}`);
const result = await minioClient
.copyObject(DEFAULT_BUCKET, to, from, copyCond)
@ -376,7 +398,7 @@ export class StorageController extends Controller {
id: data._id,
doc: {
pathname: to,
path: to.split("/").slice(0, -1).join("/") + "/",
path: stripLeadingSlash(`${to.split("/").slice(0, -1).join("/")}/`),
},
refresh: "wait_for",
})
@ -390,8 +412,8 @@ export class StorageController extends Controller {
);
io.getInstance()?.emit("FolderMove", {
from: `${src}/`,
to: `${dst}/`,
from: stripLeadingSlash(`${src}/`),
to: stripLeadingSlash(`${dst}/`),
});
return this.setStatus(HttpStatusCode.NO_CONTENT);
@ -400,6 +422,10 @@ export class StorageController extends Controller {
@Post("folder/size")
@Tags("Storage Folder")
@Security("bearerAuth", ["management-role", "admin"])
@SuccessResponse(HttpStatusCode.OK, "สำเร็จ")
@Example({
size: 10240,
})
public async folderSize(@Body() body: FolderBody) {
return { size: await folderSize([...body.path, body.name]) };
}
@ -412,9 +438,17 @@ export class StorageController extends Controller {
@Security("bearerAuth", ["management-role", "admin"])
@SuccessResponse(HttpStatusCode.NO_CONTENT, "สำเร็จ")
public async deleteStorage(@Body() body: DeleteFolderBody) {
if (body.path.length === 0) {
throw new HttpError(HttpStatusCode.BAD_REQUEST, "ไม่สามารถลบได้");
}
await new Promise<void>((resolve, reject) => {
const objects: string[] = [];
const stream = minioClient.listObjectsV2(DEFAULT_BUCKET, body.path.join("/") + "/", true);
const stream = minioClient.listObjectsV2(
DEFAULT_BUCKET,
stripLeadingSlash(`${body.path.join("/")}/`),
true,
);
stream.on("data", (v) => v && v.name && objects.push(v.name));
stream.on("close", async () => {
@ -435,30 +469,53 @@ export class StorageController extends Controller {
@Tags("Storage File")
@Security("bearerAuth", ["management-role", "admin"])
@SuccessResponse(HttpStatusCode.OK, "สำเร็จ")
@Example({
pathname: "ตู้เอกสาร 1/ลิ้นชัก 1/แฟ้ม 1/เอกสาร 1.pdf",
path: "ตู้เอกสาร 1/ลิ้นชัก 1/แฟ้ม 1/",
title: "เอกสาร",
description: "เอกสารการเงิน",
category: ["บัญชี"],
keyword: ["เงิน", "บัญชี", "รายจ่าย", "รายรับ"],
upload: false,
hidden: false,
fileName: "เอกสาร 1.pdf",
fileSize: 10240,
fileType: "application/pdf",
createdAt: "2021-07-20T12:33:13.018Z",
createdBy: "admin",
updatedAt: "2021-07-20T12:33:13.018Z",
updatedBy: "admin",
uploadUrl: "s3.storage.upload",
})
public async postFile(
@Request() request: { user: { preferred_username: string } },
@Body() body: FileBody,
) {
const { path, file } = body;
const validFileName = file.replace(/[/\\?%*:|"<>#]/g, "-").trim();
if (!(await checkPathExist(DEFAULT_BUCKET, path.join("/")))) {
if (!(await checkPathExist(DEFAULT_BUCKET, path))) {
throw new HttpError(HttpStatusCode.NOT_FOUND, PATH_NOT_FOUND_MESSAGE);
}
const result = await esClient
.search<StorageFile & { attachment?: Record<string, unknown> }>({
index: DEFAULT_INDEX!,
query: { match: { pathname: path.join("/") + "/" } },
query: {
match: {
pathname: stripLeadingSlash(`${path.join("/")}/${validFileName}`),
},
},
})
.catch((e) => console.error(`MinIO Error: ${e}`));
const metadata: StorageFile = {
path: path.join("/") + "/",
pathname: path.join("/") + "/" + file.replace(/[/\\?%*:|"<>#]/g, "-").trim(),
fileName: file.replace(/[/\\?%*:|"<>#]/g, "-").trim(),
path: stripLeadingSlash(`${path.join("/")}/`),
pathname: stripLeadingSlash(`${path.join("/")}/${validFileName}`),
fileName: validFileName,
fileSize: 0, // Will be get by minio object storage after file is uploaded
fileType: "", // Will be determined by minio object storage after file is uploaded
title: body.title ?? file.replace(/[/\\?%*:|"<>#]/g, "-").trim(), // default to same as filename
title: body.title ?? validFileName, // default to same as filename
description: body.description ?? "",
category: body.category ?? [],
keyword: body.keyword ?? [],
@ -504,6 +561,8 @@ export class StorageController extends Controller {
@Tags("Storage File")
@Security("bearerAuth", ["management-role", "admin"])
@SuccessResponse(HttpStatusCode.OK, "สำเร็จ")
@Response(HttpStatusCode.NO_CONTENT, "สำเร็จ")
@Example({ uploadUrl: "s3.storage.upload" })
public async moveFile(
@Request() request: { user: { preferred_username: string } },
@Body() body: PutFileBody,
@ -512,7 +571,9 @@ export class StorageController extends Controller {
.search<StorageFile & { attachment: Record<string, any> }>({
index: DEFAULT_INDEX,
query: {
match: { pathname: body.from.path.join("/") + `/${body.from.file}` },
match: {
pathname: stripLeadingSlash(`${body.from.path.join("/")}/${body.from.file}`),
},
},
})
.catch((e) => console.error(`ElasticSearch Error: ${e}`));
@ -523,7 +584,12 @@ export class StorageController extends Controller {
if (search && search.hits.hits.length === 0) {
throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบไฟล์ดังกล่าว");
}
if (!(await checkFileExist(DEFAULT_BUCKET, body.from.path.join("/") + `/${body.from.file}`))) {
if (
!(await checkFileExist(
DEFAULT_BUCKET,
stripLeadingSlash(`${body.from.path.join("/")}/${body.from.file}`),
))
) {
await esClient.delete({
index: DEFAULT_INDEX,
id: search.hits.hits[0]._id,
@ -531,10 +597,15 @@ export class StorageController extends Controller {
throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบไฟล์ดังกล่าว");
}
if (body.to && JSON.stringify(body.from) !== JSON.stringify(body.to)) {
if (!(await checkPathExist(DEFAULT_BUCKET, body.to.path.join("/")))) {
if (!(await checkPathExist(DEFAULT_BUCKET, body.to.path))) {
throw new HttpError(HttpStatusCode.PRECONDITION_FAILED, "ไม่พบตำแหน่งที่ต้องการย้าย");
}
if (await checkFileExist(DEFAULT_BUCKET, body.to.path.join("/") + `/${body.to.file}`)) {
if (
await checkFileExist(
DEFAULT_BUCKET,
stripLeadingSlash(`${body.to.path.join("/")}/${body.to.file}`),
)
) {
throw new HttpError(
HttpStatusCode.PRECONDITION_FAILED,
"พบไฟล์ในต้ำแหน่งปลายทาง ไม่สามารถย้ายได้",
@ -563,7 +634,7 @@ export class StorageController extends Controller {
if (from && to && JSON.stringify(from) !== JSON.stringify(to)) {
const src = [DEFAULT_BUCKET, ...from.path, ""].join("/") + from.file;
const dst = to.path.join("/") + `/${to.file}`;
const dst = stripLeadingSlash(`${to.path.join("/")}/${to.file}`);
const result = await minioClient.copyObject(DEFAULT_BUCKET, dst, src, copyCond).catch((e) => {
console.error(`MinIO Error: ${e}`);
@ -577,7 +648,7 @@ export class StorageController extends Controller {
id: id,
doc: {
...metadata,
path: to.path.join("/") + "/",
path: stripLeadingSlash(`${to.path.join("/")}/`),
pathname: dst,
fileName: to.file,
...dateMeta,
@ -586,7 +657,10 @@ export class StorageController extends Controller {
})
.then(
async () =>
await minioClient.removeObject(DEFAULT_BUCKET, from.path.join("/") + `/${from.file}`),
await minioClient.removeObject(
DEFAULT_BUCKET,
stripLeadingSlash(`${from.path.join("/")}/${from.file}`),
),
)
.catch((e) => console.error(`ElasticSearch Error: ${e}`));
@ -595,7 +669,7 @@ export class StorageController extends Controller {
to: {
...source,
...metadata,
path: to.path.join("/") + "/",
path: stripLeadingSlash(`${to.path.join("/")}/`),
pathname: dst,
fileName: to.file,
...dateMeta,
@ -633,7 +707,7 @@ export class StorageController extends Controller {
});
if (upload) {
const src = from.path.join("/") + `/${from.file}`;
const src = stripLeadingSlash(`${from.path.join("/")}/${from.file}`);
const presignedUrl = await minioClient.presignedPutObject(DEFAULT_BUCKET, src);
return { uploadUrl: presignedUrl };
}
@ -650,7 +724,7 @@ export class StorageController extends Controller {
@Security("bearerAuth", ["management-role", "admin"])
@SuccessResponse(HttpStatusCode.NO_CONTENT, "สำเร็จ")
public async deleteFile(@Body() body: DeleteFileBody) {
const pathname = body.path.join("/") + `/${body.file}`;
const pathname = stripLeadingSlash(`${body.path.join("/")}/${body.file}`);
await minioClient
.removeObject(DEFAULT_BUCKET, pathname)
@ -671,8 +745,26 @@ export class StorageController extends Controller {
@Tags("Download")
@Security("bearerAuth", ["management-role", "admin"])
@SuccessResponse(HttpStatusCode.OK, "สำเร็จ")
@Example({
pathname: "ตู้เอกสาร 1/ลิ้นชัก 1/แฟ้ม 1/เอกสาร 1.pdf",
path: "ตู้เอกสาร 1/ลิ้นชัก 1/แฟ้ม 1/",
title: "เอกสาร",
description: "เอกสารการเงิน",
category: ["บัญชี"],
keyword: ["เงิน", "บัญชี", "รายจ่าย", "รายรับ"],
upload: false,
hidden: false,
fileName: "เอกสาร 1.pdf",
fileSize: 10240,
fileType: "application/pdf",
createdAt: "2021-07-20T12:33:13.018Z",
createdBy: "admin",
updatedAt: "2021-07-20T12:33:13.018Z",
updatedBy: "admin",
downloadUrl: "s3.storage.download",
})
public async downloadFile(@Body() body: DownloadFileBody) {
const pathname = body.path.join("/") + `/${body.file}`;
const pathname = stripLeadingSlash(`${body.path.join("/")}/${body.file}`);
const search = await esClient.search<StorageFile & { attachment: Record<string, string> }>({
index: DEFAULT_INDEX,

View file

@ -1,270 +0,0 @@
import {
Body,
Controller,
Delete,
Get,
Path,
Post,
Put,
Route,
Security,
SuccessResponse,
Tags,
Request,
Response,
Example,
} from "tsoa";
import minioClient from "../minio";
import esClient from "../elasticsearch";
import { copyCond, listFolder, listItem, pathExist, replaceIllegalChars } from "../utils/minio";
import HttpStatusCode from "../interfaces/http-status";
import { StorageFile, StorageFolder } from "../interfaces/storage-fs";
import HttpError from "../interfaces/http-error";
import { getInstance } from "../lib/websocket";
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 {
/**
* @example cabinetName "ตู้เอกสาร 1"
* @example drawerName "ลิ้นชัก 1"
* @example folderName "แฟ้ม 1"
*/
@Get("/")
@Tags("แฟ้มย่อย")
@Security("bearerAuth")
@Response(
HttpStatusCode.INTERNAL_SERVER_ERROR,
"เกิดข้อผิดพลาด ไม่สามารถแสดงรายการแฟ้มได้ กรุณาลองใหม่ในภายหลัง",
)
@SuccessResponse(HttpStatusCode.OK, "สำเร็จ")
@Example([
{
path: "ตู้เอกสาร 1/ลิ้นชัก 1/แฟ้ม 1/แฟ้มย่อย 1",
name: "แฟ้มย่อย 1",
createdAt: "2021-07-20T12:33:13.018Z",
createdBy: "admin",
},
{
path: "ตู้เอกสาร 1/ลิ้นชัก 1/แฟ้ม 1/แฟ้มย่อย 2",
name: "แฟ้มย่อย 2",
createdAt: "2022-01-23T16:05:02.114Z",
createdBy: "admin",
},
])
public async listFolder(
@Path() cabinetName: string,
@Path() drawerName: string,
@Path() folderName: string,
): Promise<StorageFolder[]> {
const list = await listFolder(
DEFAULT_BUCKET!,
`${cabinetName}/${drawerName}/${folderName}`,
).catch((e) => console.error(`Error List Folder: ${e}`));
if (!list) throw new Error("เกิดข้อผิดพลาด ไม่สามารถแสดงรายการแฟ้มได้ กรุณาลองใหม่ในภายหลัง");
return list;
}
/**
* @example cabinetName "ตู้เอกสาร 1"
* @example drawerName "ลิ้นชัก 1"
* @example folderName "แฟ้ม 1"
*/
@Post("/")
@Tags("แฟ้มย่อย")
@Security("bearerAuth", ["admin", "management-role"])
@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,
@Path() folderName: string,
) {
const basePath = `${cabinetName}/${drawerName}/${folderName}/`;
if (!(await pathExist(DEFAULT_BUCKET!, basePath))) {
throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบตำแหน่งที่ต้องการสร้างลิ้นชัก");
}
const meta = {
createdAt: new Date().toISOString(),
createdBy: request.user.preferred_username,
};
const created = await minioClient
.putObject(DEFAULT_BUCKET!, `${basePath}${replaceIllegalChars(body.name)}/.keep`, "", 0, meta)
.catch((e) => console.error(e));
if (!created) throw new Error("เกิดข้อผิดพลาดกับระบบจัดการไฟล์");
const io = getInstance();
io?.emit("CreateFolder", {
pathname: `${basePath}${replaceIllegalChars(body.name)}/`,
name: replaceIllegalChars(body.name),
...meta,
});
return this.setStatus(HttpStatusCode.CREATED);
}
/**
* @example cabinetName "ตู้เอกสาร 1"
* @example drawerName "ลิ้นชัก 1"
* @example folderName "แฟ้ม 1"
* @example subFolderName "แฟ้มย่อย 1"
*/
@Put("/{subFolderName}")
@Tags("แฟ้มย่อย")
@Security("bearerAuth", ["admin", "management-role"])
@Response(HttpStatusCode.INTERNAL_SERVER_ERROR, "เกิดข้อผิดพลาดไม่สามารถย้ายไฟล์ได้")
@SuccessResponse(HttpStatusCode.NO_CONTENT, "สำเร็จ")
public async editFolder(
@Body()
body: {
/**
* @example "แฟ้มใหม่"
*/
name: string;
},
@Path() cabinetName: string,
@Path() drawerName: string,
@Path() folderName: string,
@Path() subFolderName: string,
) {
const path = `${cabinetName}/${drawerName}/${folderName}/${subFolderName}/`;
const list = await listItem(DEFAULT_BUCKET!, path, true);
await Promise.all(
list.map(async (current) => {
if (!current.name) return;
const base = `${cabinetName}/${drawerName}/${folderName}/${replaceIllegalChars(
body.name,
)}/`;
const destination = `${base}${current.name.slice(path.length)}`;
const source = `/${DEFAULT_BUCKET}/${current.name}`;
return await minioClient
.copyObject(DEFAULT_BUCKET!, destination, source, copyCond)
.then(async () => {
if (current.name.includes(".keep")) {
return await minioClient.removeObject(DEFAULT_BUCKET!, current.name);
}
const search = await esClient.search<
StorageFile & { attachment: Record<string, string> }
>({
index: DEFAULT_INDEX!,
query: { match: { pathname: current.name } },
});
if (search && search.hits.hits.length === 0) throw new Error("ไม่พบข้อมูลในฐานข้อมูล");
const data = search.hits.hits[0];
await esClient.update({
index: DEFAULT_INDEX!,
id: data._id,
doc: {
pathname: destination,
path: destination.split("/").slice(0, -1).join("/") + "/",
},
refresh: "wait_for",
});
await minioClient.removeObject(DEFAULT_BUCKET!, current.name);
})
.catch((e) => {
console.error(e);
throw new Error("เกิดข้อผิดพลาด ไม่สามารถย้ายไฟล์ได้");
});
}),
);
const io = getInstance();
io?.emit("EditFolder", {
from: `${cabinetName}/${drawerName}/${folderName}/${subFolderName}/`,
to: `${cabinetName}/${drawerName}/${folderName}/${replaceIllegalChars(body.name)}/`,
});
return this.setStatus(HttpStatusCode.NO_CONTENT);
}
/**
* @example cabinetName "ตู้เอกสาร 1"
* @example drawerName "ลิ้นชัก 1"
* @example folderName "แฟ้ม 1"
* @example subFolderName "แฟ้มย่อย 1"
*/
@Delete("/{subFolderName}")
@Tags("แฟ้มย่อย")
@Security("bearerAuth", ["admin", "management-role"])
@SuccessResponse(HttpStatusCode.NO_CONTENT, "สำเร็จ")
public async deleteFolder(
@Path() cabinetName: string,
@Path() drawerName: string,
@Path() folderName: string,
@Path() subFolderName: string,
) {
await new Promise<void>((resolve, reject) => {
const objects: string[] = [];
const stream = minioClient.listObjectsV2(
DEFAULT_BUCKET!,
`${cabinetName}/${drawerName}/${folderName}/${subFolderName}`,
true,
);
stream.on("data", (v) => {
if (v && v.name) objects.push(v.name);
});
stream.on("close", async () =>
resolve(await minioClient.removeObjects(DEFAULT_BUCKET!, objects)),
);
stream.on("error", () => reject(new Error("เกิดข้อผิดพลาด ไม่สามารถลบไฟล์ได้")));
});
const io = getInstance();
io?.emit("DeleteFolder", {
pathname: `${cabinetName}/${drawerName}/${folderName}/${subFolderName}/`,
});
return this.setStatus(HttpStatusCode.NO_CONTENT);
}
/**
* @example cabinetName "ตู้เอกสาร 1"
* @example drawerName "ลิ้นชัก 1"
* @example folderName "แฟ้ม 1"
* @example subFolderName "แฟ้มย่อย 1"
*/
@Get("/{subFolderName}/size")
@Tags("แฟ้มย่อย")
@Security("bearerAuth")
@SuccessResponse(HttpStatusCode.OK, "สำเร็จ")
public async calc(
@Path() cabinetName: string,
@Path() drawerName: string,
@Path() folderName: string,
@Path() subFolderName: string,
) {
const list = await listItem(
DEFAULT_BUCKET!,
`${cabinetName}/${drawerName}/${folderName}/${subFolderName}`,
true,
).catch((e) => console.error(`Error List Folder: ${e}`));
if (!list) throw new Error("เกิดข้อผิดพลาด ไม่สามารถแสดงรายการแฟ้มได้ กรุณาลองใหม่ในภายหลัง");
return { size: list.reduce<number>((a, c) => a + c.size, 0) };
}
}

View file

@ -1,455 +0,0 @@
import {
Body,
Controller,
Delete,
Example,
Get,
Patch,
Path,
Post,
Request,
Response,
Route,
Security,
SuccessResponse,
Tags,
} from "tsoa";
import esClient from "../elasticsearch";
import minioClient from "../minio";
import HttpStatusCode from "../interfaces/http-status";
import { StorageFile } from "../interfaces/storage-fs";
import HttpError from "../interfaces/http-error";
import { copyCond, pathExist, replaceIllegalChars } from "../utils/minio";
import { getInstance } from "../lib/websocket";
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",
)
export class SubFolderFileController extends Controller {
/**
* @example cabinetName "ตู้เอกสาร 1"
* @example drawerName "ลิ้นชัก 1"
* @example folderName "แฟ้ม 1"
* @example subFolderName "แฟ้มย่อย 1"
*/
@Get("/")
@Tags("ไฟล์")
@Security("bearerAuth")
@SuccessResponse(HttpStatusCode.OK, "สำเร็จ")
@Example([
{
pathname: "ตู้เอกสาร 1/ลิ้นชัก 1/แฟ้ม 1/แฟ้มย่อย 1/เอกสาร 1",
path: "ตู้เอกสาร 1/ลิ้นชัก 1/แฟ้ม 1/แฟ้มย่อย 1/",
title: "เอกสาร",
description: "เอกสารการเงิน",
category: ["บัญชี"],
keyword: ["เงิน", "บัญชี", "รายจ่าย", "รายรับ"],
upload: false,
fileName: "เอกสาร 1",
fileSize: 10240,
fileType: "application/pdf",
createdAt: "2021-07-20T12:33:13.018Z",
createdBy: "admin",
updatedAt: "2021-07-20T12:33:13.018Z",
updatedBy: "admin",
},
])
public async getFile(
@Path() cabinetName: string,
@Path() drawerName: string,
@Path() folderName: string,
@Path() subFolderName: string,
): Promise<StorageFile[]> {
const search = await esClient.search<StorageFile & { attachment: Record<string, string> }>({
index: DEFAULT_INDEX!,
query: {
match: {
path: `${cabinetName}/${drawerName}/${folderName}/${subFolderName}/`,
},
},
size: 10000,
});
const records = search.hits.hits
.map((v) => {
if (v._source) {
const { attachment, ...rest } = v._source;
return rest satisfies StorageFile;
}
})
.filter((v: StorageFile | undefined): v is StorageFile => !!v);
return records;
}
/**
* @example cabinetName "ตู้เอกสาร 1"
* @example drawerName "ลิ้นชัก 1"
* @example folderName "แฟ้ม 1"
* @example subFolderName "แฟ้มย่อย 1"
*/
@Post("/")
@Tags("ไฟล์")
@Security("bearerAuth", ["admin", "management-role"])
@Response(
HttpStatusCode.NOT_FOUND,
"ตำแหน่งที่ระบุไม่พบ กรุณาเตรียมตำแหน่งที่ต้องการก่อนดำเนินการ",
)
@SuccessResponse(HttpStatusCode.CREATED, "สำเร็จ")
@Example({
pathname: "ตู้เอกสาร 1/ลิ้นชัก 1/แฟ้ม 1/แฟ้มย่อย 1/เอกสาร 1",
path: "ตู้เอกสาร 1/ลิ้นชัก 1/แฟ้ม 1/แฟ้มย่อย 1/",
title: "เอกสาร",
description: "เอกสารการเงิน",
category: ["บัญชี"],
keyword: ["เงิน", "บัญชี", "รายจ่าย", "รายรับ"],
upload: false,
fileName: "เอกสาร 1",
fileSize: 10240,
fileType: "application/pdf",
createdAt: "2021-07-20T12:33:13.018Z",
createdBy: "admin",
updatedAt: "2021-07-20T12:33:13.018Z",
updatedBy: "admin",
})
public async uploadFile(
@Request() request: { user: { preferred_username: string } },
@Body()
body: {
/**
* @example "เอกสาร 1"
*/
file: string;
/**
* @example "เอกสาร"
*/
title?: string;
/**
* @example "เอกสารการเงิน"
*/
description?: string;
/**
* @example ["บัญชี"]
*/
category?: string[];
/**
* @example ["เงิน", "บัญชี", "รายจ่าย", "รายรับ"]
*/
keyword?: string[];
},
@Path() cabinetName: string,
@Path() drawerName: string,
@Path() folderName: string,
@Path() subFolderName: string,
) {
if (body.file && body.file.length > 85) {
throw new HttpError(HttpStatusCode.BAD_REQUEST, "ชื่อไฟล์ยาวเกินกำหนด");
}
const basePath = `${cabinetName}/${drawerName}/${folderName}/${subFolderName}/`;
const pathname = `${basePath}${replaceIllegalChars(body.file)}`;
if (!(await pathExist(DEFAULT_BUCKET!, basePath))) {
throw new HttpError(
HttpStatusCode.NOT_FOUND,
"ตำแหน่งที่ระบุไม่พบ กรุณาเตรียมตำแหน่งที่ต้องการก่อนดำเนินการ",
);
}
const result = await esClient
.search<StorageFile & { attachment?: Record<string, unknown> }>({
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.length > 0 ? result.hits.hits[0]._source : false;
const metadata: Partial<StorageFile> = {
pathname,
path: basePath,
fileName: replaceIllegalChars(body.file),
fileSize: 0,
fileType: "",
title: body.title ?? "",
description: body.description ?? "",
category: body.category ?? [],
keyword: body.keyword ?? [],
upload: false,
createdAt: new Date().toISOString(),
createdBy: rec ? rec.createdBy : "n/a",
updatedAt: new Date().toISOString(),
updatedBy: request.user.preferred_username ?? "n/a",
};
await esClient.index({
index: DEFAULT_INDEX!,
document: metadata,
refresh: "wait_for",
});
const io = getInstance();
io?.emit("FileUploadRequest", metadata);
return {
...body,
createdAt: metadata.createdAt,
createdBy: metadata.createdBy,
updatedAt: metadata.updatedAt,
updatedBy: metadata.updatedBy,
upload: await minioClient.presignedPutObject(DEFAULT_BUCKET!, pathname),
};
}
/**
* @example cabinetName "ตู้เอกสาร 1"
* @example drawerName "ลิ้นชัก 1"
* @example folderName "แฟ้ม 1"
* @example subFolderName "แฟ้มย่อย 1"
* @example fileName "เอกสาร 1"
*/
@Patch("/{fileName}")
@Tags("ไฟล์")
@Security("bearerAuth", ["admin", "management-role"])
@Response(HttpStatusCode.NOT_FOUND, "ไม่พบตำแหน่งที่ต้องการสร้างแฟ้ม")
@SuccessResponse(HttpStatusCode.OK, "สำเร็จ")
public async updateFile(
@Request() request: { user: { preferred_username: string } },
@Path() cabinetName: string,
@Path() drawerName: string,
@Path() folderName: string,
@Path() subFolderName: string,
@Path() fileName: string,
@Body()
body: {
/**
* @example "เอกสารใหม่"
*/
file?: string;
/**
* @example "เอกสารการเงิน"
*/
title?: string;
/**
* @example "เอกสารการเงินฉบับใหม่"
*/
description?: string;
/**
* @example ["บัญชี"]
*/
category?: string[];
/**
* @example ["เงิน", "บัญชี", "รายจ่าย", "รายรับ"]
*/
keyword?: string[];
},
) {
if (body.file && body.file.length > 85) {
throw new HttpError(HttpStatusCode.BAD_REQUEST, "ชื่อไฟล์ยาวเกินกำหนด");
}
const basePath = `${cabinetName}/${drawerName}/${folderName}/${subFolderName}/`;
const pathname = `${basePath}${fileName}`;
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 { file, ...metadata } = body;
// assume user will probably replace file by re-upload but maybe just rename
if (file) {
const destination = `${basePath}${replaceIllegalChars(file)}`;
const source = `/${DEFAULT_BUCKET}/${basePath}${fileName}`;
const copy = await minioClient.copyObject(DEFAULT_BUCKET!, destination, source, copyCond);
if (copy) {
const search = await esClient
.search<StorageFile & { attachment?: Record<string, unknown> }>({
index: DEFAULT_INDEX!,
query: { match: { pathname } },
})
.catch((e) => console.error(e));
const meta = {
updatedAt: new Date().toISOString(),
updatedBy: request.user.preferred_username ?? "n/a",
};
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: {
...metadata,
pathname: destination,
path: basePath,
fileName: replaceIllegalChars(file),
...meta,
},
refresh: "wait_for",
})
.then(() => minioClient.removeObject(DEFAULT_BUCKET!, pathname));
const io = getInstance();
io?.emit("FileUpdateMove", {
from: search.hits.hits[0]._source,
to: {
...search.hits.hits[0]._source,
...metadata,
pathname: destination,
path: basePath,
fileName: replaceIllegalChars(file),
...meta,
},
});
} else {
await minioClient.removeObject(DEFAULT_BUCKET!, pathname);
}
}
} else {
const search = await esClient
.search<StorageFile & { attachment?: Record<string, unknown> }>({
index: DEFAULT_INDEX!,
query: { match: { pathname } },
})
.catch((e) => console.error(e));
if (search && search.hits.hits.length > 0 && search.hits.hits[0]._source) {
const { _index: index, _id: id } = search.hits.hits[0];
const meta = {
updatedAt: new Date().toISOString(),
updatedBy: request.user.preferred_username ?? "n/a",
};
await esClient.update({
index,
id,
doc: {
...metadata,
updatedAt: new Date().toISOString(),
updatedBy: request.user.preferred_username ?? "n/a",
},
refresh: "wait_for",
});
const updated: StorageFile = {
...search.hits.hits[0]._source,
...metadata,
...meta,
};
const io = getInstance();
io?.emit("FileUpdate", updated);
}
}
return body.file
? {
upload: await minioClient.presignedPutObject(
DEFAULT_BUCKET!,
`${basePath}${replaceIllegalChars(body.file) ?? fileName}`,
),
}
: this.setStatus(HttpStatusCode.NO_CONTENT);
}
/**
* @example cabinetName "ตู้เอกสาร 1"
* @example drawerName "ลิ้นชัก 1"
* @example folderName "แฟ้ม 1"
* @example subFolderName "แฟ้มย่อย 1"
* @example fileName "เอกสาร 1"
*/
@Delete("/{fileName}")
@Tags("ไฟล์")
@Security("bearerAuth", ["admin", "management-role"])
@SuccessResponse(HttpStatusCode.OK, "สำเร็จ")
public async deleteFile(
@Path() cabinetName: string,
@Path() drawerName: string,
@Path() folderName: string,
@Path() subFolderName: string,
@Path() fileName: string,
) {
await minioClient.removeObject(
DEFAULT_BUCKET!,
`${cabinetName}/${drawerName}/${folderName}/${subFolderName}/${fileName}`,
);
await esClient.deleteByQuery({
index: DEFAULT_INDEX!,
query: {
match: {
pathname: `${cabinetName}/${drawerName}/${folderName}/${subFolderName}/${fileName}`,
},
},
});
return this.setStatus(HttpStatusCode.NO_CONTENT);
}
/**
* @example cabinetName "ตู้เอกสาร 1"
* @example drawerName "ลิ้นชัก 1"
* @example folderName "แฟ้ม 1"
* @example folderName "แฟ้มย่อย 1"
* @example fileName "เอกสาร 1"
*/
@Get("/{fileName}")
@Tags("ดาวน์โหลด")
@Security("bearerAuth")
@SuccessResponse(HttpStatusCode.OK)
public async downloadFile(
@Path() cabinetName: string,
@Path() drawerName: string,
@Path() folderName: string,
@Path() subFolderName: string,
@Path() fileName: string,
) {
const search = await esClient.search<StorageFile & { attachment: Record<string, string> }>({
index: DEFAULT_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 { attachment, ...rest } = search.hits.hits[0]._source!;
return {
...rest,
download: await minioClient.presignedGetObject(
DEFAULT_BUCKET!,
`${cabinetName}/${drawerName}/${folderName}/${subFolderName}/${fileName}`,
),
};
}
}

View file

@ -111,7 +111,7 @@ async function handleNotFoundRecord(
buffer: Buffer,
stat: { size: number; type: string },
) {
const path = pathname.split("/").slice(0, -1).join("/") + "/";
const path = stripLeadingSlash(pathname.split("/").slice(0, -1).join("/") + "/");
const filename = pathname.split("/").at(-1);
const base64 = Buffer.from(buffer).toString("base64");
@ -175,3 +175,7 @@ async function handleFoundRecord(
return true;
}
function stripLeadingSlash(str: string) {
return str.replace(/^\//, "");
}

View file

@ -3,22 +3,10 @@
// 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
import { Controller, ValidationService, FieldErrors, ValidateError, TsoaRoute, HttpStatusCodeLiteral, TsoaResponse, fetchMiddlewares } from '@tsoa/runtime';
// 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
import { CabinetController } from './controllers/cabinetController';
// 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
import { DrawerController } from './controllers/drawerController';
// 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
import { FileController } from './controllers/fileController';
// 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
import { FolderController } from './controllers/folderController';
// 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
import { SearchController } from './controllers/searchController';
// 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
import { StorageController } from './controllers/storageController';
// 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
import { SubFolderController } from './controllers/subFolderController';
// 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
import { SubFolderFileController } from './controllers/subFolderFileController';
// 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
import { VersionController } from './controllers/versionController';
import { expressAuthentication } from './utils/auth';
// @ts-ignore - no great way to install types from subpackage
@ -28,17 +16,6 @@ import type { RequestHandler, Router } from 'express';
// 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
const models: TsoaRoute.Models = {
"StorageFolder": {
"dataType": "refObject",
"properties": {
"pathname": {"dataType":"string","required":true},
"name": {"dataType":"string","required":true},
"createdAt": {"dataType":"union","subSchemas":[{"dataType":"string"},{"dataType":"datetime"}],"required":true},
"createdBy": {"dataType":"union","subSchemas":[{"dataType":"string"},{"dataType":"datetime"}],"required":true},
},
"additionalProperties": false,
},
// 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
"StorageFile": {
"dataType": "refObject",
"properties": {
@ -71,6 +48,17 @@ const models: TsoaRoute.Models = {
"additionalProperties": false,
},
// 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
"StorageFolder": {
"dataType": "refObject",
"properties": {
"pathname": {"dataType":"string","required":true},
"name": {"dataType":"string","required":true},
"createdAt": {"dataType":"union","subSchemas":[{"dataType":"string"},{"dataType":"datetime"}],"required":true},
"createdBy": {"dataType":"union","subSchemas":[{"dataType":"string"},{"dataType":"datetime"}],"required":true},
},
"additionalProperties": false,
},
// 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
"ListRequestBody": {
"dataType": "refObject",
"properties": {
@ -124,11 +112,11 @@ const models: TsoaRoute.Models = {
"PutFileBody": {
"dataType": "refObject",
"properties": {
"hidden": {"dataType":"boolean"},
"keyword": {"dataType":"array","array":{"dataType":"string"}},
"title": {"dataType":"string"},
"description": {"dataType":"string"},
"category": {"dataType":"array","array":{"dataType":"string"}},
"keyword": {"dataType":"array","array":{"dataType":"string"}},
"hidden": {"dataType":"boolean"},
"from": {"dataType":"nestedObjectLiteral","nestedProperties":{"file":{"dataType":"string","required":true},"path":{"dataType":"array","array":{"dataType":"string"},"required":true}},"required":true},
"to": {"dataType":"nestedObjectLiteral","nestedProperties":{"file":{"dataType":"string","required":true},"path":{"dataType":"array","array":{"dataType":"string"},"required":true}}},
"upload": {"dataType":"boolean"},
@ -164,561 +152,6 @@ export function RegisterRoutes(app: Router) {
// NOTE: If you do not see routes for all of your controllers in this file, then you might not have informed tsoa of where to look
// 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)),
function CabinetController_listCabinet(request: any, response: any, next: any) {
const args = {
};
// 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
let validatedArgs: any[] = [];
try {
validatedArgs = getValidatedArgs(args, request, response);
const controller = new CabinetController();
const promise = controller.listCabinet.apply(controller, validatedArgs as any);
promiseHandler(controller, promise, response, 200, next);
} catch (err) {
return next(err);
}
});
// 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":["admin","management-role"]}]),
...(fetchMiddlewares<RequestHandler>(CabinetController)),
...(fetchMiddlewares<RequestHandler>(CabinetController.prototype.createCabinet)),
function CabinetController_createCabinet(request: any, response: any, next: any) {
const args = {
request: {"in":"request","name":"request","required":true,"dataType":"object"},
body: {"in":"body","name":"body","required":true,"dataType":"nestedObjectLiteral","nestedProperties":{"name":{"dataType":"string","required":true}}},
};
// 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
let validatedArgs: any[] = [];
try {
validatedArgs = getValidatedArgs(args, request, response);
const controller = new CabinetController();
const promise = controller.createCabinet.apply(controller, validatedArgs as any);
promiseHandler(controller, promise, response, 201, next);
} catch (err) {
return next(err);
}
});
// 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":["admin","management-role"]}]),
...(fetchMiddlewares<RequestHandler>(CabinetController)),
...(fetchMiddlewares<RequestHandler>(CabinetController.prototype.editCabinet)),
function CabinetController_editCabinet(request: any, response: any, next: any) {
const args = {
cabinetName: {"in":"path","name":"cabinetName","required":true,"dataType":"string"},
body: {"in":"body","name":"body","required":true,"dataType":"nestedObjectLiteral","nestedProperties":{"name":{"dataType":"string","required":true}}},
};
// 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
let validatedArgs: any[] = [];
try {
validatedArgs = getValidatedArgs(args, request, response);
const controller = new CabinetController();
const promise = controller.editCabinet.apply(controller, validatedArgs as any);
promiseHandler(controller, promise, response, 204, next);
} catch (err) {
return next(err);
}
});
// 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":["admin","management-role"]}]),
...(fetchMiddlewares<RequestHandler>(CabinetController)),
...(fetchMiddlewares<RequestHandler>(CabinetController.prototype.deleteCabinet)),
function CabinetController_deleteCabinet(request: any, response: any, next: any) {
const args = {
cabinetName: {"in":"path","name":"cabinetName","required":true,"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
let validatedArgs: any[] = [];
try {
validatedArgs = getValidatedArgs(args, request, response);
const controller = new CabinetController();
const promise = controller.deleteCabinet.apply(controller, validatedArgs as any);
promiseHandler(controller, promise, response, 204, next);
} catch (err) {
return next(err);
}
});
// 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/size',
authenticateMiddleware([{"bearerAuth":[]}]),
...(fetchMiddlewares<RequestHandler>(CabinetController)),
...(fetchMiddlewares<RequestHandler>(CabinetController.prototype.calc)),
function CabinetController_calc(request: any, response: any, next: any) {
const args = {
cabinetName: {"in":"path","name":"cabinetName","required":true,"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
let validatedArgs: any[] = [];
try {
validatedArgs = getValidatedArgs(args, request, response);
const controller = new CabinetController();
const promise = controller.calc.apply(controller, validatedArgs as any);
promiseHandler(controller, promise, response, 200, next);
} catch (err) {
return next(err);
}
});
// 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)),
function DrawerController_listDrawer(request: any, response: any, next: any) {
const args = {
cabinetName: {"in":"path","name":"cabinetName","required":true,"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
let validatedArgs: any[] = [];
try {
validatedArgs = getValidatedArgs(args, request, response);
const controller = new DrawerController();
const promise = controller.listDrawer.apply(controller, validatedArgs as any);
promiseHandler(controller, promise, response, 200, next);
} catch (err) {
return next(err);
}
});
// 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":["admin","management-role"]}]),
...(fetchMiddlewares<RequestHandler>(DrawerController)),
...(fetchMiddlewares<RequestHandler>(DrawerController.prototype.createDrawer)),
function DrawerController_createDrawer(request: any, response: any, next: any) {
const args = {
request: {"in":"request","name":"request","required":true,"dataType":"object"},
cabinetName: {"in":"path","name":"cabinetName","required":true,"dataType":"string"},
body: {"in":"body","name":"body","required":true,"dataType":"nestedObjectLiteral","nestedProperties":{"name":{"dataType":"string","required":true}}},
};
// 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
let validatedArgs: any[] = [];
try {
validatedArgs = getValidatedArgs(args, request, response);
const controller = new DrawerController();
const promise = controller.createDrawer.apply(controller, validatedArgs as any);
promiseHandler(controller, promise, response, 201, next);
} catch (err) {
return next(err);
}
});
// 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":["admin","management-role"]}]),
...(fetchMiddlewares<RequestHandler>(DrawerController)),
...(fetchMiddlewares<RequestHandler>(DrawerController.prototype.editDrawer)),
function DrawerController_editDrawer(request: any, response: any, next: any) {
const args = {
cabinetName: {"in":"path","name":"cabinetName","required":true,"dataType":"string"},
drawerName: {"in":"path","name":"drawerName","required":true,"dataType":"string"},
body: {"in":"body","name":"body","required":true,"dataType":"nestedObjectLiteral","nestedProperties":{"name":{"dataType":"string","required":true}}},
};
// 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
let validatedArgs: any[] = [];
try {
validatedArgs = getValidatedArgs(args, request, response);
const controller = new DrawerController();
const promise = controller.editDrawer.apply(controller, validatedArgs as any);
promiseHandler(controller, promise, response, 204, next);
} catch (err) {
return next(err);
}
});
// 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":["admin","management-role"]}]),
...(fetchMiddlewares<RequestHandler>(DrawerController)),
...(fetchMiddlewares<RequestHandler>(DrawerController.prototype.deleteDrawer)),
function DrawerController_deleteDrawer(request: any, response: any, next: any) {
const args = {
cabinetName: {"in":"path","name":"cabinetName","required":true,"dataType":"string"},
drawerName: {"in":"path","name":"drawerName","required":true,"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
let validatedArgs: any[] = [];
try {
validatedArgs = getValidatedArgs(args, request, response);
const controller = new DrawerController();
const promise = controller.deleteDrawer.apply(controller, validatedArgs as any);
promiseHandler(controller, promise, response, 204, next);
} catch (err) {
return next(err);
}
});
// 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/size',
authenticateMiddleware([{"bearerAuth":[]}]),
...(fetchMiddlewares<RequestHandler>(DrawerController)),
...(fetchMiddlewares<RequestHandler>(DrawerController.prototype.calc)),
function DrawerController_calc(request: any, response: any, next: any) {
const args = {
cabinetName: {"in":"path","name":"cabinetName","required":true,"dataType":"string"},
drawerName: {"in":"path","name":"drawerName","required":true,"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
let validatedArgs: any[] = [];
try {
validatedArgs = getValidatedArgs(args, request, response);
const controller = new DrawerController();
const promise = controller.calc.apply(controller, validatedArgs as any);
promiseHandler(controller, promise, response, 200, next);
} catch (err) {
return next(err);
}
});
// 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<RequestHandler>(FileController)),
...(fetchMiddlewares<RequestHandler>(FileController.prototype.getFile)),
function FileController_getFile(request: any, response: any, next: any) {
const args = {
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"},
};
// 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
let validatedArgs: any[] = [];
try {
validatedArgs = getValidatedArgs(args, request, response);
const controller = new FileController();
const promise = controller.getFile.apply(controller, validatedArgs as any);
promiseHandler(controller, promise, response, 200, next);
} catch (err) {
return next(err);
}
});
// 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":["admin","management-role"]}]),
...(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"},
body: {"in":"body","name":"body","required":true,"dataType":"nestedObjectLiteral","nestedProperties":{"keyword":{"dataType":"array","array":{"dataType":"string"}},"category":{"dataType":"array","array":{"dataType":"string"}},"description":{"dataType":"string"},"title":{"dataType":"string"},"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"},
};
// 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
let validatedArgs: any[] = [];
try {
validatedArgs = getValidatedArgs(args, request, response);
const controller = new FileController();
const promise = controller.uploadFile.apply(controller, validatedArgs as any);
promiseHandler(controller, promise, response, 201, next);
} catch (err) {
return next(err);
}
});
// 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":["admin","management-role"]}]),
...(fetchMiddlewares<RequestHandler>(FileController)),
...(fetchMiddlewares<RequestHandler>(FileController.prototype.updateFile)),
function FileController_updateFile(request: any, response: any, next: any) {
const args = {
request: {"in":"request","name":"request","required":true,"dataType":"object"},
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"},
fileName: {"in":"path","name":"fileName","required":true,"dataType":"string"},
body: {"in":"body","name":"body","required":true,"dataType":"nestedObjectLiteral","nestedProperties":{"keyword":{"dataType":"array","array":{"dataType":"string"}},"category":{"dataType":"array","array":{"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
let validatedArgs: any[] = [];
try {
validatedArgs = getValidatedArgs(args, request, response);
const controller = new FileController();
const promise = controller.updateFile.apply(controller, validatedArgs as any);
promiseHandler(controller, promise, response, 200, next);
} catch (err) {
return next(err);
}
});
// 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/file/:fileName',
authenticateMiddleware([{"bearerAuth":["admin","management-role"]}]),
...(fetchMiddlewares<RequestHandler>(FileController)),
...(fetchMiddlewares<RequestHandler>(FileController.prototype.deleteFile)),
function FileController_deleteFile(request: any, response: any, next: any) {
const args = {
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"},
fileName: {"in":"path","name":"fileName","required":true,"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
let validatedArgs: any[] = [];
try {
validatedArgs = getValidatedArgs(args, request, response);
const controller = new FileController();
const promise = controller.deleteFile.apply(controller, validatedArgs as any);
promiseHandler(controller, promise, response, 200, next);
} catch (err) {
return next(err);
}
});
// 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<RequestHandler>(FileController)),
...(fetchMiddlewares<RequestHandler>(FileController.prototype.downloadFile)),
function FileController_downloadFile(request: any, response: any, next: any) {
const args = {
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"},
fileName: {"in":"path","name":"fileName","required":true,"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
let validatedArgs: any[] = [];
try {
validatedArgs = getValidatedArgs(args, request, response);
const controller = new FileController();
const promise = controller.downloadFile.apply(controller, validatedArgs as any);
promiseHandler(controller, promise, response, 200, next);
} catch (err) {
return next(err);
}
});
// 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)),
function FolderController_listFolder(request: any, response: any, next: any) {
const args = {
cabinetName: {"in":"path","name":"cabinetName","required":true,"dataType":"string"},
drawerName: {"in":"path","name":"drawerName","required":true,"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
let validatedArgs: any[] = [];
try {
validatedArgs = getValidatedArgs(args, request, response);
const controller = new FolderController();
const promise = controller.listFolder.apply(controller, validatedArgs as any);
promiseHandler(controller, promise, response, 200, next);
} catch (err) {
return next(err);
}
});
// 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":["admin","management-role"]}]),
...(fetchMiddlewares<RequestHandler>(FolderController)),
...(fetchMiddlewares<RequestHandler>(FolderController.prototype.createFolder)),
function FolderController_createFolder(request: any, response: any, next: any) {
const args = {
request: {"in":"request","name":"request","required":true,"dataType":"object"},
body: {"in":"body","name":"body","required":true,"dataType":"nestedObjectLiteral","nestedProperties":{"name":{"dataType":"string","required":true}}},
cabinetName: {"in":"path","name":"cabinetName","required":true,"dataType":"string"},
drawerName: {"in":"path","name":"drawerName","required":true,"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
let validatedArgs: any[] = [];
try {
validatedArgs = getValidatedArgs(args, request, response);
const controller = new FolderController();
const promise = controller.createFolder.apply(controller, validatedArgs as any);
promiseHandler(controller, promise, response, 201, next);
} catch (err) {
return next(err);
}
});
// 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":["admin","management-role"]}]),
...(fetchMiddlewares<RequestHandler>(FolderController)),
...(fetchMiddlewares<RequestHandler>(FolderController.prototype.editFolder)),
function FolderController_editFolder(request: any, response: any, next: any) {
const args = {
body: {"in":"body","name":"body","required":true,"dataType":"nestedObjectLiteral","nestedProperties":{"name":{"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"},
};
// 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
let validatedArgs: any[] = [];
try {
validatedArgs = getValidatedArgs(args, request, response);
const controller = new FolderController();
const promise = controller.editFolder.apply(controller, validatedArgs as any);
promiseHandler(controller, promise, response, 204, next);
} catch (err) {
return next(err);
}
});
// 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":["admin","management-role"]}]),
...(fetchMiddlewares<RequestHandler>(FolderController)),
...(fetchMiddlewares<RequestHandler>(FolderController.prototype.deleteFolder)),
function FolderController_deleteFolder(request: any, response: any, next: any) {
const args = {
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"},
};
// 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
let validatedArgs: any[] = [];
try {
validatedArgs = getValidatedArgs(args, request, response);
const controller = new FolderController();
const promise = controller.deleteFolder.apply(controller, validatedArgs as any);
promiseHandler(controller, promise, response, 204, next);
} catch (err) {
return next(err);
}
});
// 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/size',
authenticateMiddleware([{"bearerAuth":[]}]),
...(fetchMiddlewares<RequestHandler>(FolderController)),
...(fetchMiddlewares<RequestHandler>(FolderController.prototype.calc)),
function FolderController_calc(request: any, response: any, next: any) {
const args = {
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"},
};
// 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
let validatedArgs: any[] = [];
try {
validatedArgs = getValidatedArgs(args, request, response);
const controller = new FolderController();
const promise = controller.calc.apply(controller, validatedArgs as any);
promiseHandler(controller, promise, response, 200, next);
} catch (err) {
return next(err);
}
});
// 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('/search',
authenticateMiddleware([{"bearerAuth":[]}]),
...(fetchMiddlewares<RequestHandler>(SearchController)),
@ -765,7 +198,7 @@ export function RegisterRoutes(app: Router) {
const promise = controller.getList.apply(controller, validatedArgs as any);
promiseHandler(controller, promise, response, undefined, next);
promiseHandler(controller, promise, response, 200, next);
} catch (err) {
return next(err);
}
@ -844,7 +277,7 @@ export function RegisterRoutes(app: Router) {
const promise = controller.folderSize.apply(controller, validatedArgs as any);
promiseHandler(controller, promise, response, undefined, next);
promiseHandler(controller, promise, response, 200, next);
} catch (err) {
return next(err);
}
@ -975,304 +408,6 @@ export function RegisterRoutes(app: Router) {
const controller = new StorageController();
const promise = controller.downloadFile.apply(controller, validatedArgs as any);
promiseHandler(controller, promise, response, 200, next);
} catch (err) {
return next(err);
}
});
// 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)),
function SubFolderController_listFolder(request: any, response: any, next: any) {
const args = {
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"},
};
// 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
let validatedArgs: any[] = [];
try {
validatedArgs = getValidatedArgs(args, request, response);
const controller = new SubFolderController();
const promise = controller.listFolder.apply(controller, validatedArgs as any);
promiseHandler(controller, promise, response, 200, next);
} catch (err) {
return next(err);
}
});
// 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":["admin","management-role"]}]),
...(fetchMiddlewares<RequestHandler>(SubFolderController)),
...(fetchMiddlewares<RequestHandler>(SubFolderController.prototype.createFolder)),
function SubFolderController_createFolder(request: any, response: any, next: any) {
const args = {
request: {"in":"request","name":"request","required":true,"dataType":"object"},
body: {"in":"body","name":"body","required":true,"dataType":"nestedObjectLiteral","nestedProperties":{"name":{"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"},
};
// 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
let validatedArgs: any[] = [];
try {
validatedArgs = getValidatedArgs(args, request, response);
const controller = new SubFolderController();
const promise = controller.createFolder.apply(controller, validatedArgs as any);
promiseHandler(controller, promise, response, 201, next);
} catch (err) {
return next(err);
}
});
// 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":["admin","management-role"]}]),
...(fetchMiddlewares<RequestHandler>(SubFolderController)),
...(fetchMiddlewares<RequestHandler>(SubFolderController.prototype.editFolder)),
function SubFolderController_editFolder(request: any, response: any, next: any) {
const args = {
body: {"in":"body","name":"body","required":true,"dataType":"nestedObjectLiteral","nestedProperties":{"name":{"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"},
subFolderName: {"in":"path","name":"subFolderName","required":true,"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
let validatedArgs: any[] = [];
try {
validatedArgs = getValidatedArgs(args, request, response);
const controller = new SubFolderController();
const promise = controller.editFolder.apply(controller, validatedArgs as any);
promiseHandler(controller, promise, response, 204, next);
} catch (err) {
return next(err);
}
});
// 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":["admin","management-role"]}]),
...(fetchMiddlewares<RequestHandler>(SubFolderController)),
...(fetchMiddlewares<RequestHandler>(SubFolderController.prototype.deleteFolder)),
function SubFolderController_deleteFolder(request: any, response: any, next: any) {
const args = {
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"},
subFolderName: {"in":"path","name":"subFolderName","required":true,"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
let validatedArgs: any[] = [];
try {
validatedArgs = getValidatedArgs(args, request, response);
const controller = new SubFolderController();
const promise = controller.deleteFolder.apply(controller, validatedArgs as any);
promiseHandler(controller, promise, response, 204, next);
} catch (err) {
return next(err);
}
});
// 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/size',
authenticateMiddleware([{"bearerAuth":[]}]),
...(fetchMiddlewares<RequestHandler>(SubFolderController)),
...(fetchMiddlewares<RequestHandler>(SubFolderController.prototype.calc)),
function SubFolderController_calc(request: any, response: any, next: any) {
const args = {
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"},
subFolderName: {"in":"path","name":"subFolderName","required":true,"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
let validatedArgs: any[] = [];
try {
validatedArgs = getValidatedArgs(args, request, response);
const controller = new SubFolderController();
const promise = controller.calc.apply(controller, validatedArgs as any);
promiseHandler(controller, promise, response, 200, next);
} catch (err) {
return next(err);
}
});
// 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<RequestHandler>(SubFolderFileController)),
...(fetchMiddlewares<RequestHandler>(SubFolderFileController.prototype.getFile)),
function SubFolderFileController_getFile(request: any, response: any, next: any) {
const args = {
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"},
subFolderName: {"in":"path","name":"subFolderName","required":true,"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
let validatedArgs: any[] = [];
try {
validatedArgs = getValidatedArgs(args, request, response);
const controller = new SubFolderFileController();
const promise = controller.getFile.apply(controller, validatedArgs as any);
promiseHandler(controller, promise, response, 200, next);
} catch (err) {
return next(err);
}
});
// 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":["admin","management-role"]}]),
...(fetchMiddlewares<RequestHandler>(SubFolderFileController)),
...(fetchMiddlewares<RequestHandler>(SubFolderFileController.prototype.uploadFile)),
function SubFolderFileController_uploadFile(request: any, response: any, next: any) {
const args = {
request: {"in":"request","name":"request","required":true,"dataType":"object"},
body: {"in":"body","name":"body","required":true,"dataType":"nestedObjectLiteral","nestedProperties":{"keyword":{"dataType":"array","array":{"dataType":"string"}},"category":{"dataType":"array","array":{"dataType":"string"}},"description":{"dataType":"string"},"title":{"dataType":"string"},"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"},
subFolderName: {"in":"path","name":"subFolderName","required":true,"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
let validatedArgs: any[] = [];
try {
validatedArgs = getValidatedArgs(args, request, response);
const controller = new SubFolderFileController();
const promise = controller.uploadFile.apply(controller, validatedArgs as any);
promiseHandler(controller, promise, response, 201, next);
} catch (err) {
return next(err);
}
});
// 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":["admin","management-role"]}]),
...(fetchMiddlewares<RequestHandler>(SubFolderFileController)),
...(fetchMiddlewares<RequestHandler>(SubFolderFileController.prototype.updateFile)),
function SubFolderFileController_updateFile(request: any, response: any, next: any) {
const args = {
request: {"in":"request","name":"request","required":true,"dataType":"object"},
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"},
subFolderName: {"in":"path","name":"subFolderName","required":true,"dataType":"string"},
fileName: {"in":"path","name":"fileName","required":true,"dataType":"string"},
body: {"in":"body","name":"body","required":true,"dataType":"nestedObjectLiteral","nestedProperties":{"keyword":{"dataType":"array","array":{"dataType":"string"}},"category":{"dataType":"array","array":{"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
let validatedArgs: any[] = [];
try {
validatedArgs = getValidatedArgs(args, request, response);
const controller = new SubFolderFileController();
const promise = controller.updateFile.apply(controller, validatedArgs as any);
promiseHandler(controller, promise, response, 200, next);
} catch (err) {
return next(err);
}
});
// 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":["admin","management-role"]}]),
...(fetchMiddlewares<RequestHandler>(SubFolderFileController)),
...(fetchMiddlewares<RequestHandler>(SubFolderFileController.prototype.deleteFile)),
function SubFolderFileController_deleteFile(request: any, response: any, next: any) {
const args = {
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"},
subFolderName: {"in":"path","name":"subFolderName","required":true,"dataType":"string"},
fileName: {"in":"path","name":"fileName","required":true,"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
let validatedArgs: any[] = [];
try {
validatedArgs = getValidatedArgs(args, request, response);
const controller = new SubFolderFileController();
const promise = controller.deleteFile.apply(controller, validatedArgs as any);
promiseHandler(controller, promise, response, 200, next);
} catch (err) {
return next(err);
}
});
// 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/:fileName',
authenticateMiddleware([{"bearerAuth":[]}]),
...(fetchMiddlewares<RequestHandler>(SubFolderFileController)),
...(fetchMiddlewares<RequestHandler>(SubFolderFileController.prototype.downloadFile)),
function SubFolderFileController_downloadFile(request: any, response: any, next: any) {
const args = {
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"},
subFolderName: {"in":"path","name":"subFolderName","required":true,"dataType":"string"},
fileName: {"in":"path","name":"fileName","required":true,"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
let validatedArgs: any[] = [];
try {
validatedArgs = getValidatedArgs(args, request, response);
const controller = new SubFolderFileController();
const promise = controller.downloadFile.apply(controller, validatedArgs as any);
promiseHandler(controller, promise, response, 200, next);
} catch (err) {

File diff suppressed because it is too large Load diff

View file

@ -25,16 +25,7 @@
"description": "Keycloak Bearer Token",
"in": "header"
}
},
"tags": [
{ "name": "ตู้เอกสาร" },
{ "name": "ลิ้นชัก" },
{ "name": "แฟ้ม" },
{ "name": "แฟ้มย่อย" },
{ "name": "ไฟล์" },
{ "name": "ดาวน์โหลด" },
{ "name": "ค้นหา" }
]
}
},
"routes": {
"routesDir": "src",