port-nimara-client-portal/server/utils/minio.ts

205 lines
6.2 KiB
TypeScript

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<string>();
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);
};