import { Client } from 'minio'; import type { BucketItem } from 'minio'; // Initialize MinIO client export const getMinioClient = () => { const config = useRuntimeConfig().minio; console.log('MinIO Config:', { endPoint: config.endPoint, port: config.port, useSSL: config.useSSL, bucketName: config.bucketName, hasAccessKey: !!config.accessKey, hasSecretKey: !!config.secretKey, }); return new Client({ endPoint: config.endPoint, port: config.port, useSSL: config.useSSL, accessKey: config.accessKey, secretKey: config.secretKey, }); }; // File listing with metadata export const listFiles = async (prefix: string = '', recursive: boolean = false) => { const client = getMinioClient(); const bucketName = useRuntimeConfig().minio.bucketName; console.log(`Listing files from bucket: ${bucketName}, prefix: ${prefix}, recursive: ${recursive}`); const files: any[] = []; const folders = new Set(); return new Promise(async (resolve, reject) => { try { // First check if bucket exists const bucketExists = await client.bucketExists(bucketName); console.log(`Bucket ${bucketName} exists:`, bucketExists); if (!bucketExists) { console.error(`Bucket ${bucketName} does not exist`); reject(new Error(`Bucket ${bucketName} does not exist`)); return; } const stream = client.listObjectsV2(bucketName, prefix, recursive); let objectCount = 0; stream.on('data', (obj) => { objectCount++; console.log('Object found:', obj.name); if (!recursive && prefix) { // Extract folder structure when not recursive const relativePath = obj.name.substring(prefix.length); const firstSlash = relativePath.indexOf('/'); if (firstSlash > -1) { // This is a folder const folderName = relativePath.substring(0, firstSlash); folders.add(prefix + folderName + '/'); } else if (relativePath) { // This is a file in the current folder files.push({ name: obj.name, size: obj.size, lastModified: obj.lastModified, etag: obj.etag, isFolder: false, }); } } else { // When recursive or at root, include all files if (!obj.name.endsWith('/')) { files.push({ name: obj.name, size: obj.size, lastModified: obj.lastModified, etag: obj.etag, isFolder: false, }); } } }); stream.on('error', (error) => { console.error('Stream error:', error); reject(error); }); stream.on('end', () => { console.log(`Stream ended. Total objects processed: ${objectCount}`); // Add folders to the result const folderItems = Array.from(folders).map(folder => ({ name: folder, size: 0, lastModified: new Date(), etag: '', isFolder: true, })); console.log(`Returning ${folderItems.length} folders and ${files.length} files`); resolve([...folderItems, ...files]); }); } catch (error) { console.error('Error in listFiles:', error); reject(error); } }); }; // Upload file export const uploadFile = async (filePath: string, fileBuffer: Buffer, contentType: string) => { const client = getMinioClient(); const bucketName = useRuntimeConfig().minio.bucketName; return await client.putObject(bucketName, filePath, fileBuffer, fileBuffer.length, { 'Content-Type': contentType, }); }; // Generate presigned URL for download export const getDownloadUrl = async (fileName: string, expiry: number = 60 * 60) => { const client = getMinioClient(); const bucketName = useRuntimeConfig().minio.bucketName; return await client.presignedGetObject(bucketName, fileName, expiry); }; // Delete file export const deleteFile = async (fileName: string) => { const client = getMinioClient(); const bucketName = useRuntimeConfig().minio.bucketName; return await client.removeObject(bucketName, fileName); }; // Delete folder (recursively delete all contents) export const deleteFolder = async (folderPath: string) => { const client = getMinioClient(); const bucketName = useRuntimeConfig().minio.bucketName; // List all objects in the folder const objectsList: string[] = []; return new Promise((resolve, reject) => { const stream = client.listObjectsV2(bucketName, folderPath, true); stream.on('data', (obj) => { objectsList.push(obj.name); }); stream.on('error', reject); stream.on('end', async () => { try { // Delete all objects if (objectsList.length > 0) { await client.removeObjects(bucketName, objectsList); } resolve(true); } catch (error) { reject(error); } }); }); }; // Get file stats export const getFileStats = async (fileName: string) => { const client = getMinioClient(); const bucketName = useRuntimeConfig().minio.bucketName; return await client.statObject(bucketName, fileName); }; // Create folder (MinIO doesn't have explicit folders, so we create a placeholder) export const createFolder = async (folderPath: string) => { const client = getMinioClient(); const bucketName = useRuntimeConfig().minio.bucketName; // Ensure folder path ends with / const normalizedPath = folderPath.endsWith('/') ? folderPath : folderPath + '/'; // Create an empty object to represent the folder return await client.putObject(bucketName, normalizedPath, Buffer.from(''), 0); }; // Get presigned URL for file preview export const getPreviewUrl = async (fileName: string, contentType: string) => { const client = getMinioClient(); const bucketName = useRuntimeConfig().minio.bucketName; // For images and PDFs, generate a presigned URL with appropriate response headers const responseHeaders = { 'response-content-type': contentType, 'response-content-disposition': 'inline', }; return await client.presignedGetObject(bucketName, fileName, 60 * 60, responseHeaders); };