import { NextResponse } from "next/server"; import { sql } from "@/db"; import { requireFamily } from "@/lib/auth"; import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3"; import { getSignedUrl } from "@aws-sdk/s3-request-presigner"; const ALLOWED_TYPES = ["image/jpeg", "image/jpg", "image/png", "image/webp", "image/heic"]; async function ownershipCheck(childId: string, familyId: string) { const rows = await sql.unsafe( `SELECT id FROM children WHERE id = $1 AND family_id = $2`, [childId, familyId] ); return rows.length > 0; } // POST — get a presigned R2 URL for uploading a child profile photo // (no memories row — this is purely for profile avatars) export async function POST( request: Request, { params }: { params: Promise<{ id: string }> } ) { const auth = await requireFamily(); if (!auth.success) return NextResponse.json({ error: auth.error }, { status: auth.status }); const familyId = auth.session!.familyId!; const { id } = await params; if (!(await ownershipCheck(id, familyId))) { return NextResponse.json({ error: "Not found" }, { status: 404 }); } const body = await request.json(); const { contentType, filename } = body; if (!contentType || !ALLOWED_TYPES.includes(contentType)) { return NextResponse.json({ error: "Unsupported file type" }, { status: 400 }); } const accountId = process.env.R2_ACCOUNT_ID; const accessKeyId = process.env.R2_ACCESS_KEY_ID; const secretKey = process.env.R2_SECRET_ACCESS_KEY; const bucket = process.env.R2_BUCKET_NAME; const publicUrl = process.env.R2_PUBLIC_URL; if (!accountId || !accessKeyId || !secretKey || !bucket) { return NextResponse.json({ error: "R2 not configured" }, { status: 500 }); } const ext = filename?.split(".").pop()?.toLowerCase() || "jpg"; const r2Key = `profiles/${id}/${Date.now()}.${ext}`; const baseUrl = publicUrl || `https://pub-${accountId}.r2.dev`; const client = new S3Client({ region: "auto", endpoint: `https://${accountId}.r2.cloudflarestorage.com`, credentials: { accessKeyId, secretAccessKey: secretKey }, }); const uploadUrl = await getSignedUrl( client, new PutObjectCommand({ Bucket: bucket, Key: r2Key, ContentType: contentType }), { expiresIn: 3600 } ); return NextResponse.json({ uploadUrl, publicUrl: `${baseUrl}/${r2Key}` }); } // PATCH — update child profile fields (imageUrl, and extensible for name/etc later) export async function PATCH( request: Request, { params }: { params: Promise<{ id: string }> } ) { const auth = await requireFamily(); if (!auth.success) return NextResponse.json({ error: auth.error }, { status: auth.status }); const familyId = auth.session!.familyId!; const { id } = await params; if (!(await ownershipCheck(id, familyId))) { return NextResponse.json({ error: "Not found" }, { status: 404 }); } const body = await request.json(); const { imageUrl } = body; if (imageUrl !== undefined) { await sql.unsafe( `UPDATE children SET image_url = $1 WHERE id = $2`, [imageUrl, id] ); } return NextResponse.json({ success: true }); }