diff --git a/components/FilePreviewModal.vue b/components/FilePreviewModal.vue index 4e5220d..33acb2a 100644 --- a/components/FilePreviewModal.vue +++ b/components/FilePreviewModal.vue @@ -155,13 +155,16 @@ const loadPreview = async () => { previewUrl.value = ''; try { - const response = await $fetch('/api/files/preview', { - params: { fileName: props.file.name }, - }); - - previewUrl.value = response.url; + // For images and PDFs, use the proxy endpoint to avoid CORS issues + if (isImage.value || isPdf.value) { + // Use the proxy endpoint that serves the file directly + previewUrl.value = `/api/files/proxy-preview?fileName=${encodeURIComponent(props.file.name)}`; + // The loading state will be handled by the image/iframe onload event + } else { + throw new Error('File type does not support preview'); + } } catch (err: any) { - error.value = err.data?.statusMessage || 'Failed to load preview'; + error.value = err.data?.statusMessage || err.message || 'Failed to load preview'; loading.value = false; } }; diff --git a/server/api/files/list.ts b/server/api/files/list.ts index 87f33c0..b4f6ef8 100644 --- a/server/api/files/list.ts +++ b/server/api/files/list.ts @@ -81,7 +81,7 @@ function getDisplayName(filepath: string): string { // Handle folders (ending with /) if (filepath.endsWith('/')) { const parts = filepath.slice(0, -1).split('/'); - return parts[parts.length - 1] + '/'; + return parts[parts.length - 1]; // Return folder name without trailing slash } // Get just the filename from the full path diff --git a/server/api/files/proxy-preview.ts b/server/api/files/proxy-preview.ts new file mode 100644 index 0000000..37bb0ed --- /dev/null +++ b/server/api/files/proxy-preview.ts @@ -0,0 +1,49 @@ +import { getDownloadUrl } from '~/server/utils/minio'; +import mime from 'mime-types'; + +export default defineEventHandler(async (event) => { + try { + const query = getQuery(event); + const fileName = query.fileName as string; + + if (!fileName) { + throw createError({ + statusCode: 400, + statusMessage: 'File name is required', + }); + } + + // Get content type + const contentType = mime.lookup(fileName) || 'application/octet-stream'; + + // Get the download URL + const url = await getDownloadUrl(fileName); + + // Fetch the file content from MinIO + const response = await fetch(url); + + if (!response.ok) { + throw createError({ + statusCode: response.status, + statusMessage: 'Failed to fetch file from storage', + }); + } + + // Get the file buffer + const buffer = Buffer.from(await response.arrayBuffer()); + + // Set appropriate headers for preview + setHeader(event, 'Content-Type', contentType); + setHeader(event, 'Content-Disposition', 'inline'); + setHeader(event, 'Cache-Control', 'public, max-age=3600'); + + // Return the file buffer + return buffer; + } catch (error: any) { + console.error('Failed to proxy preview:', error); + throw createError({ + statusCode: 500, + statusMessage: error.message || 'Failed to proxy preview', + }); + } +});