import { NextResponse } from "next/server" import { getSessionUser } from "@/lib/session" import { getAccountContext } from "@/lib/account" import { saveFile, isAllowedUploadExt, StorageNotConfiguredError } from "@/lib/storage" import { checkStorageLimit } from "@/lib/plan-limits" const ALLOWED_SCOPES = ["property-images", "maintenance", "documents", "misc"] // Generic authenticated upload endpoint. Persists the file under the user's // namespace (DigitalOcean Spaces when configured, else local disk in dev) and // returns a URL pointing at the auth-gated /api/files route. export async function POST(request: Request) { const user = await getSessionUser() if (!user) return NextResponse.json({ error: "Unauthorized" }, { status: 401 }) // Uploads belong to the effective owner's portfolio. Viewers are read-only. const ctx = await getAccountContext(user.id) if (!ctx.canWrite) return NextResponse.json({ error: "Forbidden" }, { status: 403 }) const ownerId = ctx.ownerId const fd = await request.formData() const file = fd.get("file") as File | null const scopeRaw = (fd.get("scope") as string) || "misc" const scope = ALLOWED_SCOPES.includes(scopeRaw) ? scopeRaw : "misc" const fixedName = (fd.get("fixed_name") as string) || undefined if (!file) return NextResponse.json({ error: "No file provided" }, { status: 400 }) if (file.size > 20 * 1024 * 1024) { return NextResponse.json({ error: "File too large (max 20 MB)" }, { status: 400 }) } if (!isAllowedUploadExt(file.name)) { return NextResponse.json({ error: "File type not allowed" }, { status: 400 }) } // Enforce per-plan storage quota (accounts for everything already stored in // the owner's portfolio namespace). const storageError = await checkStorageLimit(ownerId, file.size) if (storageError) return NextResponse.json({ error: storageError }, { status: 403 }) let saved try { saved = await saveFile(file, { userId: ownerId, scope, fixedName }) } catch (err) { if (err instanceof StorageNotConfiguredError) { console.error("[upload]", err.message) return NextResponse.json( { error: "File uploads are temporarily unavailable. Please try again later." }, { status: 503 } ) } throw err } const { key, size, type } = saved return NextResponse.json({ url: `/api/files/${key}`, key, size, type, name: file.name, }) }