feat: direct minio upload

This commit is contained in:
Methapon2001 2025-01-15 13:07:03 +07:00
parent d17462cb92
commit a1b88c070b
4 changed files with 575 additions and 34 deletions

View file

@ -22,6 +22,7 @@ import {
listFile,
updateFile,
} from "../services/edm";
import { s3DownloadFile, s3ListFile, s3UploadFile } from "../services/minio";
@Route("api/v1/salary/file/{name}/{group}")
@Security("bearerAuth")
@ -56,6 +57,12 @@ export class FileController extends Controller {
},
])
public async getFile(@Path() name: string, @Path() group: string, @Path() id: string) {
if (name !== "ระบบประเมิน") {
return await s3ListFile(name, group, id).catch((e) => {
console.error(e);
throw new Error("เกิดข้อผิดพลาดกับระบบจัดการไฟล์");
});
}
const list = await listFile([name, group, id]);
if (!list) throw new Error("เกิดข้อผิดพลาดกับระบบจัดการไฟล์");
return list;
@ -99,7 +106,12 @@ export class FileController extends Controller {
@Path() id: string,
@Path() file: string,
) {
if (name !== "ระบบประเมิน") {
return await s3DownloadFile(name, group, id, file);
}
const data = await downloadFile([name, group, id], file);
if (!data) throw new Error("เกิดข้อผิดพลาดกับระบบจัดการไฟล์");
return data;
}
@ -176,6 +188,16 @@ export class FileController extends Controller {
) {
const path = [name, group, id];
if (name !== "ระบบประเมิน") {
const ret = await Promise.all(
body.fileList.map(async ({ fileName }) => [
fileName,
await s3UploadFile(...path, fileName),
]),
);
return Object.fromEntries(ret);
}
if (!(await createFolder(path.slice(0, -1), id, true))) {
throw new Error("เกิดข้อผิดพลาดกับระบบจัดการไฟล์ ไม่สามารถสร้างแฟ้มได้");
}
@ -272,6 +294,8 @@ export class FileController extends Controller {
metadata?: { [key: string]: unknown };
},
) {
if (name !== "ระบบประเมิน") return; // do nothing, no metadata to update
const props = body;
const result = await updateFile([name, group, id], file, props);
@ -362,6 +386,9 @@ export class SubFileController extends Controller {
@Path() id: string,
@Path() subId: string,
) {
if (name !== "ระบบประเมิน") {
return await s3ListFile(name, group, id);
}
const list = await listFile([name, group, id, subId]);
if (!list) throw new Error("เกิดข้อผิดพลาดกับระบบจัดการไฟล์");
return list;
@ -408,6 +435,9 @@ export class SubFileController extends Controller {
@Path() subId: string,
@Path() file: string,
) {
if (name !== "ระบบประเมิน") {
return await s3DownloadFile(name, group, id, subId);
}
const data = await downloadFile([name, group, id, subId], file);
if (!data) throw new Error("เกิดข้อผิดพลาดกับระบบจัดการไฟล์");
return data;
@ -488,6 +518,10 @@ export class SubFileController extends Controller {
) {
const path = [name, group, id, subId];
if (name !== "ระบบประเมิน") {
return await s3ListFile(name, group, id, subId);
}
if (!(await createFolder(path.slice(0, -1), subId, true))) {
throw new Error("เกิดข้อผิดพลาดกับระบบจัดการไฟล์ ไม่สามารถสร้างแฟ้มได้");
}
@ -586,6 +620,8 @@ export class SubFileController extends Controller {
metadata?: { [key: string]: unknown };
},
) {
if (name !== "ระบบประเมิน") return; // do nothing, no metadata to update
const props = body;
const result = await updateFile([name, group, id, subId], file, props);

123
src/services/minio.ts Normal file
View file

@ -0,0 +1,123 @@
import { Client } from "minio";
if (!process.env.MINIO_BUCKET) {
throw Error("Require MinIO bucket.");
}
const MINIO_BUCKET = process.env.MINIO_BUCKET;
const minio = new Client({
endPoint: process.env.MINIO_HOST ?? "localhost",
port: process.env.MINIO_PORT ? +process.env.MINIO_PORT : undefined,
useSSL: /true/.test(process.env.MINIO_SSL || "false"),
accessKey: process.env.MINIO_ACCESS_KEY ?? "",
secretKey: process.env.MINIO_SECRET_KEY ?? "",
});
export default minio;
function exception(e: any): never {
console.error(e);
throw new Error("เกิดข้อผิดพลาดกับระบบจัดการไฟล์");
}
export async function listFile(path: string) {
return await new Promise<string[]>((resolve, reject) => {
const item: string[] = [];
const stream = minio.listObjectsV2(MINIO_BUCKET, path);
stream.on("data", (v) => v && v.name && item.push(v.name.split("/").at(-1) || ""));
stream.on("end", () => resolve(item));
stream.on("error", () => reject(new Error("MinIO error.")));
});
}
export async function getPresigned(method: string, path: string, exp = 60 * 60) {
return await minio.presignedUrl(method, MINIO_BUCKET, path, exp);
}
export async function getFile(path: string, exp = 60 * 60) {
return await minio.presignedGetObject(MINIO_BUCKET, path, exp);
}
export async function setFile(path: string, exp = 6 * 60 * 60) {
return await minio.presignedPutObject(MINIO_BUCKET, path, exp);
}
export async function deleteFile(path: string) {
await minio.removeObject(MINIO_BUCKET, path, { forceDelete: true });
}
export async function deleteFolder(path: string) {
new Promise<string[]>((resolve, reject) => {
const item: string[] = [];
const stream = minio.listObjectsV2(MINIO_BUCKET, path, true);
stream.on("data", (v) => v && v.name && item.push(v.name));
stream.on("end", () => resolve(item));
stream.on("error", () => reject(new Error("MinIO error.")));
}).then((list) => {
list.map(async (v) => {
await minio.removeObject(MINIO_BUCKET, v, { forceDelete: true });
});
});
}
export async function s3UploadFile(...pathname: string[]) {
if (!pathname.length) return;
const data = {
path: pathname.slice(0, -1).join("/"),
pathname: pathname.join("/"),
fileName: pathname.at(-1),
title: pathname.at(-1),
uploadUrl: await setFile(fileLocation(...pathname)).catch(exception),
};
return data;
}
export async function s3UpdateFile(from: string[], to: string[], upload?: boolean) {
await minio
.copyObject(MINIO_BUCKET, from.join("/"), [MINIO_BUCKET].concat(to).join("/"))
.then(() => {
minio.removeObject(MINIO_BUCKET, from.join("/"));
});
const data = {
path: to.slice(0, -1).join("/"),
pathname: to.join("/"),
fileName: to.at(-1),
title: to.at(-1),
uploadUrl: upload ? await setFile(fileLocation(...to)).catch(exception) : undefined,
};
return data;
}
export async function s3ListFile(...path: string[]) {
const list = await listFile(fileLocation(...path) + "/").catch(exception);
return list.map((name) => ({
path: path.join("/"),
pathname: path.concat(name).join("/"),
fileName: name,
title: name,
}));
}
export async function s3DownloadFile(...pathname: string[]) {
const downloadUrl = await getFile(fileLocation(...pathname)).catch(exception);
return {
path: pathname.slice(0, -1).join("/"),
pathname: pathname.join("/"),
fileName: pathname.at(-1),
title: pathname.at(-1),
downloadUrl,
};
}
export const fileLocation = (...path: string[]) => path.join("/");