Add proxy endpoint to fix CORS issues for file previews
- Create new `/api/files/proxy-preview` endpoint that fetches and serves files directly - Update FilePreviewModal to use proxy endpoint for images and PDFs - Set appropriate headers for inline display and caching - Remove trailing slash from folder display names in file list
This commit is contained in:
parent
7d5b39b29d
commit
9e9c667d1f
|
|
@ -155,13 +155,16 @@ const loadPreview = async () => {
|
||||||
previewUrl.value = '';
|
previewUrl.value = '';
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await $fetch('/api/files/preview', {
|
// For images and PDFs, use the proxy endpoint to avoid CORS issues
|
||||||
params: { fileName: props.file.name },
|
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)}`;
|
||||||
previewUrl.value = response.url;
|
// 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) {
|
} 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;
|
loading.value = false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -81,7 +81,7 @@ function getDisplayName(filepath: string): string {
|
||||||
// Handle folders (ending with /)
|
// Handle folders (ending with /)
|
||||||
if (filepath.endsWith('/')) {
|
if (filepath.endsWith('/')) {
|
||||||
const parts = filepath.slice(0, -1).split('/');
|
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
|
// Get just the filename from the full path
|
||||||
|
|
|
||||||
|
|
@ -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',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
Loading…
Reference in New Issue