mirror of
https://github.com/xavidop/cardex.git
synced 2026-08-28 23:01:22 +00:00
fix: cors issue
This commit is contained in:
@@ -0,0 +1,8 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"origin": ["https://cardex.xavidop.me", "http://localhost:3000", "http://localhost:9002"],
|
||||||
|
"method": ["GET", "HEAD"],
|
||||||
|
"maxAgeSeconds": 3600
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
import { NextRequest, NextResponse } from 'next/server';
|
||||||
|
|
||||||
|
export async function GET(request: NextRequest) {
|
||||||
|
try {
|
||||||
|
const url = request.nextUrl.searchParams.get('url');
|
||||||
|
|
||||||
|
if (!url) {
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: 'URL parameter is required' },
|
||||||
|
{ status: 400 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate that the URL is from Firebase Storage (production or emulator)
|
||||||
|
const isFirebaseStorage = url.includes('firebasestorage.googleapis.com');
|
||||||
|
const isFirebaseEmulator = url.includes('localhost:9199') || url.includes('127.0.0.1:9199');
|
||||||
|
|
||||||
|
if (!isFirebaseStorage && !isFirebaseEmulator) {
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: 'Only Firebase Storage URLs are allowed' },
|
||||||
|
{ status: 403 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch the file from Firebase Storage
|
||||||
|
const response = await fetch(url, {
|
||||||
|
headers: {
|
||||||
|
'User-Agent': 'Cardex-Download-Proxy/1.0',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`Failed to fetch: ${response.status} ${response.statusText}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the content type from the original response
|
||||||
|
const contentType = response.headers.get('content-type') || 'application/octet-stream';
|
||||||
|
|
||||||
|
// Stream the response
|
||||||
|
return new NextResponse(response.body, {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': contentType,
|
||||||
|
'Content-Disposition': 'attachment',
|
||||||
|
'Cache-Control': 'public, max-age=31536000',
|
||||||
|
'Access-Control-Allow-Origin': '*',
|
||||||
|
'Access-Control-Allow-Methods': 'GET',
|
||||||
|
'Access-Control-Allow-Headers': 'Content-Type',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Download proxy error:', error);
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: 'Failed to download file' },
|
||||||
|
{ status: 500 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
+58
-20
@@ -21,16 +21,35 @@ export async function downloadCardImage(imageUrl: string, cardName: string): Pro
|
|||||||
// Data URL - use directly
|
// Data URL - use directly
|
||||||
link.href = imageUrl;
|
link.href = imageUrl;
|
||||||
} else if (imageUrl.startsWith('http')) {
|
} else if (imageUrl.startsWith('http')) {
|
||||||
// Firebase Storage URL or other HTTP URL - fetch and convert to blob URL
|
// Firebase Storage URL or other HTTP URL - use proxy approach to avoid CORS
|
||||||
const response = await fetch(imageUrl);
|
try {
|
||||||
const blob = await response.blob();
|
const proxyUrl = `/api/download?url=${encodeURIComponent(imageUrl)}`;
|
||||||
const blobUrl = URL.createObjectURL(blob);
|
const response = await fetch(proxyUrl);
|
||||||
link.href = blobUrl;
|
|
||||||
|
if (!response.ok) {
|
||||||
// Clean up the blob URL after download
|
throw new Error('Failed to fetch through proxy');
|
||||||
link.addEventListener('click', () => {
|
}
|
||||||
setTimeout(() => URL.revokeObjectURL(blobUrl), 100);
|
|
||||||
});
|
const blob = await response.blob();
|
||||||
|
const blobUrl = URL.createObjectURL(blob);
|
||||||
|
link.href = blobUrl;
|
||||||
|
|
||||||
|
// Clean up the blob URL after download
|
||||||
|
link.addEventListener('click', () => {
|
||||||
|
setTimeout(() => URL.revokeObjectURL(blobUrl), 100);
|
||||||
|
});
|
||||||
|
} catch (proxyError) {
|
||||||
|
console.error('Proxy download failed, trying direct fetch:', proxyError);
|
||||||
|
// Fallback to direct fetch (might still hit CORS)
|
||||||
|
const response = await fetch(imageUrl, { mode: 'cors' });
|
||||||
|
const blob = await response.blob();
|
||||||
|
const blobUrl = URL.createObjectURL(blob);
|
||||||
|
link.href = blobUrl;
|
||||||
|
|
||||||
|
link.addEventListener('click', () => {
|
||||||
|
setTimeout(() => URL.revokeObjectURL(blobUrl), 100);
|
||||||
|
});
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Assume it's a base64 string without data URL prefix
|
// Assume it's a base64 string without data URL prefix
|
||||||
const dataUrl = `data:image/png;base64,${imageUrl}`;
|
const dataUrl = `data:image/png;base64,${imageUrl}`;
|
||||||
@@ -84,16 +103,35 @@ export async function downloadCardVideo(videoUrl: string, cardName: string): Pro
|
|||||||
// Data URL - use directly
|
// Data URL - use directly
|
||||||
link.href = videoUrl;
|
link.href = videoUrl;
|
||||||
} else if (videoUrl.startsWith('http')) {
|
} else if (videoUrl.startsWith('http')) {
|
||||||
// Firebase Storage URL or other HTTP URL - fetch and convert to blob URL
|
// Firebase Storage URL or other HTTP URL - use proxy approach to avoid CORS
|
||||||
const response = await fetch(videoUrl);
|
try {
|
||||||
const blob = await response.blob();
|
const proxyUrl = `/api/download?url=${encodeURIComponent(videoUrl)}`;
|
||||||
const blobUrl = URL.createObjectURL(blob);
|
const response = await fetch(proxyUrl);
|
||||||
link.href = blobUrl;
|
|
||||||
|
if (!response.ok) {
|
||||||
// Clean up the blob URL after download
|
throw new Error('Failed to fetch through proxy');
|
||||||
link.addEventListener('click', () => {
|
}
|
||||||
setTimeout(() => URL.revokeObjectURL(blobUrl), 100);
|
|
||||||
});
|
const blob = await response.blob();
|
||||||
|
const blobUrl = URL.createObjectURL(blob);
|
||||||
|
link.href = blobUrl;
|
||||||
|
|
||||||
|
// Clean up the blob URL after download
|
||||||
|
link.addEventListener('click', () => {
|
||||||
|
setTimeout(() => URL.revokeObjectURL(blobUrl), 100);
|
||||||
|
});
|
||||||
|
} catch (proxyError) {
|
||||||
|
console.error('Proxy download failed, trying direct fetch:', proxyError);
|
||||||
|
// Fallback to direct fetch (might still hit CORS)
|
||||||
|
const response = await fetch(videoUrl, { mode: 'cors' });
|
||||||
|
const blob = await response.blob();
|
||||||
|
const blobUrl = URL.createObjectURL(blob);
|
||||||
|
link.href = blobUrl;
|
||||||
|
|
||||||
|
link.addEventListener('click', () => {
|
||||||
|
setTimeout(() => URL.revokeObjectURL(blobUrl), 100);
|
||||||
|
});
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Assume it's a base64 string without data URL prefix
|
// Assume it's a base64 string without data URL prefix
|
||||||
const dataUrl = `data:video/mp4;base64,${videoUrl}`;
|
const dataUrl = `data:video/mp4;base64,${videoUrl}`;
|
||||||
|
|||||||
Reference in New Issue
Block a user