diff --git a/src/app/api/admin/upload-category/route.ts b/src/app/api/admin/upload-category/route.ts index 5892aa1..db4f5b7 100644 --- a/src/app/api/admin/upload-category/route.ts +++ b/src/app/api/admin/upload-category/route.ts @@ -36,7 +36,7 @@ async function handler(req: AuthenticatedRequest) { const filepath = path.join(uploadDir, filename); await writeFile(filepath, buffer); - const url = `/uploads/categories/${filename}`; + const url = `/api/uploads/categories/${filename}`; return NextResponse.json({ url }); } catch (error) { diff --git a/src/app/api/admin/upload/route.ts b/src/app/api/admin/upload/route.ts index 1c7a3c0..6c13ff5 100644 --- a/src/app/api/admin/upload/route.ts +++ b/src/app/api/admin/upload/route.ts @@ -39,7 +39,7 @@ async function handler(req: AuthenticatedRequest) { const filepath = path.join(uploadDir, filename); await writeFile(filepath, buffer); - uploadedUrls.push(`/uploads/products/${filename}`); + uploadedUrls.push(`/api/uploads/products/${filename}`); } return NextResponse.json({ urls: uploadedUrls }); diff --git a/src/app/api/uploads/[...path]/route.ts b/src/app/api/uploads/[...path]/route.ts new file mode 100644 index 0000000..76acd0a --- /dev/null +++ b/src/app/api/uploads/[...path]/route.ts @@ -0,0 +1,45 @@ +import { NextRequest, NextResponse } from 'next/server'; +import { readFile } from 'fs/promises'; +import path from 'path'; + +// Serve uploaded images in standalone mode +export async function GET( + request: NextRequest, + { params }: { params: Promise<{ path: string[] }> } +) { + try { + const { path: pathSegments } = await params; + const filePath = path.join(process.cwd(), 'public', 'uploads', ...pathSegments); + + // Security: Prevent directory traversal + const normalizedPath = path.normalize(filePath); + const uploadsDir = path.join(process.cwd(), 'public', 'uploads'); + if (!normalizedPath.startsWith(uploadsDir)) { + return NextResponse.json({ error: 'Invalid path' }, { status: 400 }); + } + + const fileBuffer = await readFile(filePath); + + // Determine content type + const ext = path.extname(filePath).toLowerCase(); + const contentTypes: Record = { + '.jpg': 'image/jpeg', + '.jpeg': 'image/jpeg', + '.png': 'image/png', + '.gif': 'image/gif', + '.webp': 'image/webp', + '.svg': 'image/svg+xml', + }; + const contentType = contentTypes[ext] || 'application/octet-stream'; + + return new NextResponse(fileBuffer, { + headers: { + 'Content-Type': contentType, + 'Cache-Control': 'public, max-age=31536000, immutable', + }, + }); + } catch (error) { + console.error('Image serve error:', error); + return NextResponse.json({ error: 'Image not found' }, { status: 404 }); + } +}