import { NextRequest, NextResponse } from "next/server"; import { getServerSession } from "next-auth"; import { authOptions } from "@/lib/auth"; import { requireActiveUser } from "@/lib/auth-guards"; import { ALLOWED_TRIP_IMAGE_MIME, MAX_TRIP_IMAGE_UPLOAD_BYTES, processAndSaveTripImage, } from "@/lib/trip-image-storage"; export const runtime = "nodejs"; export const dynamic = "force-dynamic"; /** * Upload satu foto trip. Dipanggil dari form create-trip saat user memilih * file — gambar langsung dikompres & disimpan, route mengembalikan URL publik * yang nanti ikut disubmit bersama data trip. * * File yatim (di-upload tapi trip batal dibuat) dibersihkan cron * `/api/cron/cleanup-trip-images`. */ export async function POST(req: NextRequest) { const session = await getServerSession(authOptions); if (!session?.user) { return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); } try { await requireActiveUser(session.user.id); } catch (err) { return NextResponse.json( { error: (err as Error).message }, { status: 403 } ); } let form: FormData; try { form = await req.formData(); } catch { return NextResponse.json( { error: "Body bukan multipart/form-data" }, { status: 400 } ); } const file = form.get("file"); if (!(file instanceof File)) { return NextResponse.json({ error: "File wajib diisi" }, { status: 400 }); } if (!ALLOWED_TRIP_IMAGE_MIME.has(file.type)) { return NextResponse.json( { error: "Hanya menerima JPG, PNG, atau WebP" }, { status: 415 } ); } if (file.size > MAX_TRIP_IMAGE_UPLOAD_BYTES) { return NextResponse.json( { error: "Ukuran file maksimal 12MB" }, { status: 413 } ); } try { const buf = Buffer.from(await file.arrayBuffer()); const saved = await processAndSaveTripImage(buf); return NextResponse.json({ url: saved.url, size: saved.size }); } catch (err) { return NextResponse.json( { error: (err as Error).message || "Gagal memproses gambar" }, { status: 400 } ); } }