fix(homepage): fix baby profile photo upload failing due to CORS
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 <noreply@anthropic.com>
This commit is contained in:
parent
fa5e27bfd9
commit
afae041208
2 changed files with 14 additions and 7 deletions
|
|
@ -63,7 +63,7 @@ export async function POST(
|
||||||
{ expiresIn: 3600 }
|
{ 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)
|
// PATCH — update child profile fields (imageUrl, and extensible for name/etc later)
|
||||||
|
|
|
||||||
|
|
@ -188,17 +188,23 @@ export default function HomePage() {
|
||||||
if (!file || !childId) return;
|
if (!file || !childId) return;
|
||||||
setUploadingPhoto(true);
|
setUploadingPhoto(true);
|
||||||
try {
|
try {
|
||||||
// 1. Get presigned R2 URL
|
// 1. Get R2 key + public URL from server
|
||||||
const presignRes = await fetch(`/api/children/${childId}`, {
|
const initRes = await fetch(`/api/children/${childId}`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: { "Content-Type": "application/json" },
|
headers: { "Content-Type": "application/json" },
|
||||||
body: JSON.stringify({ contentType: file.type, filename: file.name }),
|
body: JSON.stringify({ contentType: file.type, filename: file.name }),
|
||||||
});
|
});
|
||||||
if (!presignRes.ok) throw new Error("Failed to get upload URL");
|
if (!initRes.ok) throw new Error("Failed to get upload URL");
|
||||||
const { uploadUrl, publicUrl } = await presignRes.json();
|
const { key, publicUrl } = await initRes.json();
|
||||||
|
|
||||||
// 2. Upload directly to R2
|
// 2. Upload via server proxy — avoids CORS on direct R2 PUT
|
||||||
await fetch(uploadUrl, { method: "PUT", body: file, headers: { "Content-Type": file.type } });
|
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
|
// 3. Save URL to DB
|
||||||
await fetch(`/api/children/${childId}`, {
|
await fetch(`/api/children/${childId}`, {
|
||||||
|
|
@ -211,6 +217,7 @@ export default function HomePage() {
|
||||||
updateChildImage(childId, publicUrl);
|
updateChildImage(childId, publicUrl);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("Photo upload failed:", err);
|
console.error("Photo upload failed:", err);
|
||||||
|
alert("Photo upload failed. Please try again.");
|
||||||
}
|
}
|
||||||
setUploadingPhoto(false);
|
setUploadingPhoto(false);
|
||||||
if (photoInputRef.current) photoInputRef.current.value = "";
|
if (photoInputRef.current) photoInputRef.current.value = "";
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue