import { Client } from 'minio'; export default defineEventHandler(async (event) => { const xTagHeader = getRequestHeader(event, "x-tag"); if (!xTagHeader || (xTagHeader !== "094ut234" && xTagHeader !== "pjnvü1230")) { throw createError({ statusCode: 401, statusMessage: "unauthenticated" }); } try { const query = getQuery(event); const prefix = (query.prefix as string) || ''; const recursive = query.recursive === 'true'; const includeEmailAttachments = query.includeEmailAttachments === 'true'; const currentUserEmail = query.userEmail as string; // Get files from main bucket (client-portal) const mainFiles = await listFilesFromBucket('client-portal', prefix, recursive); let allFiles = [...mainFiles]; // If requested, also include email attachments from client-emails bucket if (includeEmailAttachments && currentUserEmail) { try { // Create the folder name from the user's email const username = currentUserEmail.split('@')[0] .toLowerCase() .replace(/[^a-z0-9-]/g, ''); const attachmentFolder = `${username}-attachments`; // List files from the user's attachment folder const attachmentFiles = await listFilesFromBucket('client-emails', attachmentFolder + '/', true); // Add these files with a special flag to identify them as email attachments const formattedAttachmentFiles = attachmentFiles.map((file: any) => ({ ...file, isEmailAttachment: true, displayPath: `Email Attachments/${file.name.replace(attachmentFolder + '/', '')}`, bucket: 'client-emails' })); // Create a virtual folder for email attachments if (formattedAttachmentFiles.length > 0 && !prefix) { allFiles.push({ name: 'Email Attachments/', size: 0, lastModified: new Date(), etag: '', isFolder: true, isVirtualFolder: true, icon: 'mdi-email-multiple' }); } // If we're inside the Email Attachments folder, show the files if (prefix === 'Email Attachments/') { allFiles = formattedAttachmentFiles.map((file: any) => ({ ...file, displayName: file.name.replace(attachmentFolder + '/', ''), // Keep the full path in 'name' for operations like delete name: file.name })); } } catch (error) { console.error('Error fetching email attachments:', error); // Continue without email attachments if there's an error } } // Format file list with additional metadata const formattedFiles = allFiles.map(file => ({ ...file, sizeFormatted: file.isFolder ? '-' : formatFileSize(file.size), extension: file.isFolder ? 'folder' : getFileExtension(file.name), icon: file.icon || (file.isFolder ? 'mdi-folder' : getFileIcon(file.name)), displayName: file.displayPath || getDisplayName(file.name), })); // Sort folders first, then files formattedFiles.sort((a, b) => { if (a.isFolder && !b.isFolder) return -1; if (!a.isFolder && b.isFolder) return 1; return a.displayName.localeCompare(b.displayName); }); return { success: true, files: formattedFiles, count: formattedFiles.length, currentPath: prefix, }; } catch (error: any) { console.error('Failed to list files:', error); throw createError({ statusCode: 500, statusMessage: error.message || 'Failed to list files', }); } }); // List files from a specific bucket async function listFilesFromBucket(bucketName: string, prefix: string = '', recursive: boolean = false): Promise { const config = useRuntimeConfig().minio; const client = new Client({ endPoint: config.endPoint, port: config.port, useSSL: config.useSSL, accessKey: config.accessKey, secretKey: config.secretKey, }); const files: any[] = []; const folders = new Set(); return new Promise(async (resolve, reject) => { try { const stream = client.listObjectsV2(bucketName, prefix, recursive); stream.on('data', (obj) => { if (obj && obj.prefix) { folders.add(obj.prefix); return; } if (!obj || typeof obj.name !== 'string') { return; } if (!recursive) { if (prefix) { const relativePath = obj.name.substring(prefix.length); if (!relativePath) return; const firstSlash = relativePath.indexOf('/'); if (firstSlash > -1) { const folderName = relativePath.substring(0, firstSlash); folders.add(prefix + folderName + '/'); } else if (relativePath && !obj.name.endsWith('/')) { files.push({ name: obj.name, size: obj.size || 0, lastModified: obj.lastModified || new Date(), etag: obj.etag || '', isFolder: false, bucket: bucketName }); } } else { const firstSlash = obj.name.indexOf('/'); if (obj.name.endsWith('/')) { folders.add(obj.name); } else if (firstSlash > -1) { const folderName = obj.name.substring(0, firstSlash); folders.add(folderName + '/'); } else { files.push({ name: obj.name, size: obj.size || 0, lastModified: obj.lastModified || new Date(), etag: obj.etag || '', isFolder: false, bucket: bucketName }); } } } else { if (!obj.name.endsWith('/')) { files.push({ name: obj.name, size: obj.size || 0, lastModified: obj.lastModified || new Date(), etag: obj.etag || '', isFolder: false, bucket: bucketName }); } } }); stream.on('error', (error) => { console.error('Stream error:', error); reject(error); }); stream.on('end', () => { const folderItems = Array.from(folders).map(folder => ({ name: folder, size: 0, lastModified: new Date(), etag: '', isFolder: true, bucket: bucketName })); resolve([...folderItems, ...files]); }); } catch (error) { console.error('Error in listFilesFromBucket:', error); reject(error); } }); } // Helper functions function formatFileSize(bytes: number): string { const sizes = ['Bytes', 'KB', 'MB', 'GB']; if (bytes === 0) return '0 Bytes'; const i = Math.floor(Math.log(bytes) / Math.log(1024)); return Math.round(bytes / Math.pow(1024, i) * 100) / 100 + ' ' + sizes[i]; } function getFileExtension(filename: string): string { const parts = filename.split('.'); return parts.length > 1 ? parts.pop()?.toLowerCase() || '' : ''; } function getFileIcon(filename: string): string { const ext = getFileExtension(filename); const iconMap: Record = { pdf: 'mdi-file-pdf-box', doc: 'mdi-file-document', docx: 'mdi-file-document', xls: 'mdi-file-excel', xlsx: 'mdi-file-excel', jpg: 'mdi-file-image', jpeg: 'mdi-file-image', png: 'mdi-file-image', gif: 'mdi-file-image', svg: 'mdi-file-image', zip: 'mdi-folder-zip', rar: 'mdi-folder-zip', txt: 'mdi-file-document-outline', csv: 'mdi-file-delimited', mp4: 'mdi-file-video', mp3: 'mdi-file-music', }; return iconMap[ext] || 'mdi-file'; } function getDisplayName(filepath: string): string { if (filepath.endsWith('/')) { const parts = filepath.slice(0, -1).split('/'); return parts[parts.length - 1]; } const parts = filepath.split('/'); const filename = parts[parts.length - 1]; const match = filename.match(/^\d{10,}-(.+)$/); return match ? match[1] : filename; }