From afae041208c1afe0222cab2d56fdd497d8658ee9 Mon Sep 17 00:00:00 2001 From: Mannu Date: Sun, 24 May 2026 13:37:36 +0530 Subject: [PATCH] fix(homepage): fix baby profile photo upload failing due to CORS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Direct PUT to R2 presigned URL is cross-origin — browser blocks it. Route the upload through the existing PUT /api/upload server proxy instead, same pattern used for memories. Also return `key` from children POST so the proxy call has the R2 object key without needing the presigned URL. Co-Authored-By: Claude Sonnet 4.6 --- src/app/api/children/[id]/route.ts | 2 +- src/app/page.tsx | 19 +++++++++++++------ 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/app/api/children/[id]/route.ts b/src/app/api/children/[id]/route.ts index 68a5ca5..eaf0de8 100644 --- a/src/app/api/children/[id]/route.ts +++ b/src/app/api/children/[id]/route.ts @@ -63,7 +63,7 @@ export async function POST( { expiresIn: 3600 } ); - return NextResponse.json({ uploadUrl, publicUrl: `${baseUrl}/${r2Key}` }); + return NextResponse.json({ uploadUrl, key: r2Key, publicUrl: `${baseUrl}/${r2Key}` }); } // PATCH — update child profile fields (imageUrl, and extensible for name/etc later) diff --git a/src/app/page.tsx b/src/app/page.tsx index 55284dd..7400d3d 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -188,17 +188,23 @@ export default function HomePage() { if (!file || !childId) return; setUploadingPhoto(true); try { - // 1. Get presigned R2 URL - const presignRes = await fetch(`/api/children/${childId}`, { + // 1. Get R2 key + public URL from server + const initRes = await fetch(`/api/children/${childId}`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ contentType: file.type, filename: file.name }), }); - if (!presignRes.ok) throw new Error("Failed to get upload URL"); - const { uploadUrl, publicUrl } = await presignRes.json(); + if (!initRes.ok) throw new Error("Failed to get upload URL"); + const { key, publicUrl } = await initRes.json(); - // 2. Upload directly to R2 - await fetch(uploadUrl, { method: "PUT", body: file, headers: { "Content-Type": file.type } }); + // 2. Upload via server proxy — avoids CORS on direct R2 PUT + const putParams = new URLSearchParams({ key, contentType: file.type }); + const putRes = await fetch(`/api/upload?${putParams}`, { + method: "PUT", + body: file, + headers: { "Content-Type": file.type }, + }); + if (!putRes.ok) throw new Error("Upload failed"); // 3. Save URL to DB await fetch(`/api/children/${childId}`, { @@ -211,6 +217,7 @@ export default function HomePage() { updateChildImage(childId, publicUrl); } catch (err) { console.error("Photo upload failed:", err); + alert("Photo upload failed. Please try again."); } setUploadingPhoto(false); if (photoInputRef.current) photoInputRef.current.value = "";