import dayjs from 'dayjs'; import { oss } from '@/app.ts'; import { StatObjectResult } from '@kevisual/oss'; type MinioListOpt = { prefix: string; recursive?: boolean; }; export type MinioFile = { name: string; size: number; lastModified: Date; etag: string; }; export type MinioDirectory = { prefix: string; size: number; }; export type MinioList = (MinioFile | MinioDirectory)[]; export const getMinioList = async (opts: MinioListOpt): Promise => { const prefix = opts.prefix; const recursive = opts.recursive ?? false; const res = await oss.listObjects(prefix, { recursive }); return res as IS_FILE extends true ? MinioFile[] : MinioDirectory[]; }; export const getFileStat = async (prefix: string, isFile?: boolean): Promise => { try { const obj = await oss.statObject(prefix); if (isFile && obj?.size === 0) { return null; } return obj; } catch (e) { if (e.code === 'NotFound') { return null; } console.error('get File Stat Error not handle', e); return null; } }; export const deleteFile = async (prefix: string): Promise<{ code: number; message: string }> => { try { const fileStat = await getFileStat(prefix); if (!fileStat) { console.warn(`File not found: ${prefix}`); return { code: 404, message: 'file not found', }; } await oss.deleteObject(prefix); return { code: 200, message: 'delete success', }; } catch (e) { console.error('delete File Error not handle', e); return { code: 500, message: 'delete failed', }; } }; // 批量删除文件 export const deleteFiles = async (prefixs: string[]): Promise => { try { for (const prefix of prefixs) { await oss.deleteObject(prefix); } return true; } catch (e) { console.error('delete Files Error not handle', e); return false; } }; export const deleteFileByPrefix = async (prefix: string): Promise => { try { const allFiles = await getMinioList({ prefix, recursive: true }); const files = allFiles.filter((item) => item.name.startsWith(prefix)); await deleteFiles(files.map((item) => item.name)); return true; } catch (e) { console.error('delete File Error not handle', e); return false; } }; type GetMinioListAndSetToAppListOpts = { username: string; appKey: string; version: string; }; // 批量列出文件,并设置到appList的files中 export const getMinioListAndSetToAppList = async (opts: GetMinioListAndSetToAppListOpts) => { const { username, appKey, version } = opts; const minioList = await getMinioList({ prefix: `${username}/${appKey}/${version}`, recursive: true }); const files = minioList; return files as MinioFile[]; }; /** * 更新文件的元数据 * @param prefix 文件前缀 * @param newMetadata 新的元数据 * @returns */ export const updateFileStat = async ( prefix: string, newMetadata: Record, ): Promise<{ code: number; data: any; message?: string; }> => { try { const copyResult = await oss.replaceObject(prefix, { ...newMetadata }); console.log('copyResult', copyResult); console.log(`Metadata for ${prefix} updated successfully.`); return { code: 200, data: copyResult, message: 'update metadata success', }; } catch (e) { console.error('Error updating file stat', e); return { code: 500, data: null, message: `update metadata failed. ${e.message}`, }; } }; /** * 将用户A的文件移动到用户B * @param usernameA * @param usernameB * @param clearOldUser 是否清除用户A的文件 */ export const mvUserAToUserB = async (usernameA: string, usernameB: string, clearOldUser = false) => { const oldPrefix = `${usernameA}/`; const newPrefix = `${usernameB}/`; const listSource = await getMinioList({ prefix: oldPrefix, recursive: true }); for (const item of listSource) { const newName = item.name.slice(oldPrefix.length); await oss.copyObject(item.name, `${newPrefix}${newName}`); } if (clearOldUser) { const files = await getMinioList({ prefix: oldPrefix, recursive: true }); for (const file of files) { await oss.deleteObject(file.name); } } }; export const backupUserA = async (usernameA: string, id: string, backName?: string) => { const today = backName || dayjs().format('YYYY-MM-DD-HH-mm'); const backupAllPrefix = `private/backup/${id}/`; const backupPrefix = `private/backup/${id}/${today}`; const backupList = await getMinioList({ prefix: backupAllPrefix }); const backupListSort = backupList.sort((a, b) => -a.prefix.localeCompare(b.prefix)); if (backupListSort.length > 2) { const deleteBackup = backupListSort.slice(2); for (const item of deleteBackup) { const files = await getMinioList({ prefix: item.prefix, recursive: true }); for (const file of files) { await oss.deleteObject(file.name); } } } await mvUserAToUserB(usernameA, backupPrefix, false); }; /** * 删除用户 * @param username */ export const deleteUser = async (username: string) => { const list = await getMinioList({ prefix: `${username}/`, recursive: true }); for (const item of list) { await oss.deleteObject(item.name); } };