hrms-api-salary/src/services/minio.ts

187 lines
5.9 KiB
TypeScript
Raw Normal View History

2025-01-15 13:07:03 +07:00
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;
/**
* limit
* @param fileName -
* @param maxLength - (default: 50 )
* @returns
*/
export function truncateFileName(fileName: string, maxLength: number = 50): string {
const dot = fileName.lastIndexOf(".");
const name = dot !== -1 && !fileName.startsWith(".") ? fileName.slice(0, dot) : fileName;
const ext = dot !== -1 && !fileName.startsWith(".") ? fileName.slice(dot) : "";
if (name.length <= maxLength) {
return fileName;
}
// ตัดชื่อและเติม "..." ตามด้วย 5 ตัวสุดท้ายของชื่อเดิม
const truncated = name.slice(0, maxLength - 8) + "..." + name.slice(-5);
return truncated + ext;
}
2025-01-15 13:07:03 +07:00
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));
2025-01-15 17:33:22 +07:00
stream.on("error", (e) => {
console.error(e);
reject(new Error("MinIO error."));
});
2025-01-15 13:07:03 +07:00
});
}
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);
}
2025-01-28 17:31:40 +07:00
export async function s3DeleteFile(path: string) {
2025-01-15 13:07:03 +07:00
await minio.removeObject(MINIO_BUCKET, path, { forceDelete: true });
}
2025-01-28 17:31:40 +07:00
export async function s3DeleteFolder(path: string) {
2025-01-15 13:07:03 +07:00
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));
2025-01-15 17:33:22 +07:00
stream.on("error", (e) => {
console.error(e);
reject(new Error("MinIO error."));
});
2025-01-15 13:07:03 +07:00
}).then((list) => {
list.map(async (v) => {
await minio.removeObject(MINIO_BUCKET, v, { forceDelete: true });
});
});
}
2025-01-15 13:31:42 +07:00
export async function s3UploadFile(replace: boolean, ...pathname: string[]) {
2025-01-15 13:07:03 +07:00
if (!pathname.length) return;
// ตัดชื่อไฟล์ถ้ายาวเกิน
const originalFileName = pathname.at(-1) as string;
const truncatedFileName = truncateFileName(originalFileName);
pathname = [...pathname.slice(0, -1), truncatedFileName];
2025-01-15 13:31:42 +07:00
if (!replace) {
const list = await s3ListFile(...pathname.slice(0, -1)).catch(exception);
2025-01-15 13:28:33 +07:00
2025-01-15 13:31:42 +07:00
const fileName = pathname.at(-1) as string;
const dot = fileName.lastIndexOf(".");
const name = dot !== -1 && !fileName.startsWith(".") ? fileName.slice(0, dot) : fileName;
const ext = dot !== -1 && !fileName.startsWith(".") ? fileName.slice(dot) : "";
2025-01-15 13:28:33 +07:00
2025-01-15 13:31:42 +07:00
let copy = 0;
let copyFileName = fileName;
2025-01-15 13:28:33 +07:00
2025-01-15 13:31:42 +07:00
while (list.findIndex((v) => v.fileName === copyFileName) !== -1) {
copyFileName = `${name} (${++copy})${ext}`.trim();
}
2025-01-15 13:28:33 +07:00
2025-01-15 13:31:42 +07:00
if (copy > 0) {
await s3UpdateFile(pathname, pathname.slice(0, -1).concat(copyFileName));
}
2025-01-15 13:28:33 +07:00
}
2025-01-15 13:07:03 +07:00
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
2025-01-15 13:28:33 +07:00
.copyObject(MINIO_BUCKET, to.join("/"), [MINIO_BUCKET].concat(from).join("/"))
.catch(exception);
await minio.removeObject(MINIO_BUCKET, from.join("/")).catch(exception);
2025-01-15 13:07:03 +07:00
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 async function duplicateAvatarFile(refId: string, prefix: string, fileName: string) {
try {
await minio.statObject(MINIO_BUCKET, refId);
const avatar = `${prefix}/${fileName}`;
await minio.copyObject(
MINIO_BUCKET,
avatar,
`/${MINIO_BUCKET}/${refId}`
);
} catch (error) {
exception(error);
}
}
export const fileLocation = (...path: string[]) => path.join("/");