From 0e047e110f119b7c0f4a7dca1903a9511a9ae4af Mon Sep 17 00:00:00 2001 From: Mannu Date: Mon, 18 May 2026 21:57:48 +0530 Subject: [PATCH] fix(memories): route upload through server proxy to avoid R2 CORS failure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Direct PUT to R2 presigned URL is cross-origin, causing "Failed to fetch" in browsers without R2 CORS configured. Use the existing PUT /api/upload proxy handler instead — file goes client → Next.js → R2 server-side. Co-Authored-By: Claude Sonnet 4.6 --- src/app/memories/page.tsx | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/app/memories/page.tsx b/src/app/memories/page.tsx index cd0dfd2..5b2cec4 100644 --- a/src/app/memories/page.tsx +++ b/src/app/memories/page.tsx @@ -84,17 +84,26 @@ export default function MemoriesPage() { setUploading(true); try { - // Step 1: Get presigned URL + memoryId + // Step 1: Get key + memoryId from server const initRes = await fetch("/api/upload", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ filename: file.name, contentType: file.type, childId, sizeBytes: file.size }), }); - const { uploadUrl, memoryId, publicUrl, error } = await initRes.json(); + const { key, memoryId, publicUrl, error } = await initRes.json(); if (error) { alert("Error: " + error); return; } - // Step 2: Upload directly to R2 - await fetch(uploadUrl, { method: "PUT", body: file, headers: { "Content-Type": file.type } }); + // Step 2: Upload via server proxy — avoids cross-origin CORS restriction on R2 + 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) { + const e = await putRes.json().catch(() => ({})) as { error?: string }; + throw new Error(e.error ?? `Upload failed (${putRes.status})`); + } // Step 3: Confirm upload → fires thumbnail + vision pipeline await fetch(`/api/memories/${memoryId}/confirm`, { method: "POST" }); @@ -102,7 +111,7 @@ export default function MemoriesPage() { // Optimistic add const optimistic: Memory = { id: memoryId, - key: "", + key, url: publicUrl, thumbnailUrl: null, sizeBytes: file.size,