forked from Goutam/lynkeduppro-crm
72 lines
2.3 KiB
TypeScript
72 lines
2.3 KiB
TypeScript
import { type NextRequest, NextResponse } from "next/server";
|
|
|
|
import { RunpodError } from "@/lib/server/runpod/client";
|
|
import { rpDetect } from "@/lib/server/runpod/endpoints";
|
|
|
|
import { guard } from "../_guard";
|
|
|
|
export const runtime = "nodejs";
|
|
export const maxDuration = 60;
|
|
export const dynamic = "force-dynamic";
|
|
|
|
/**
|
|
* Object-detection proxy for the RunPod YOLO construction-material classifier (#1).
|
|
* The key + endpoint URL stay server-side. The client calls this only when
|
|
* NEXT_PUBLIC_APG_RUNPOD_DETECT is on; otherwise detection runs fully in-browser
|
|
* (COCO-SSD) with no server round-trip. Returns the SDK's DetectedObject[] shape
|
|
* (box as 0..1 fractions) so it drops straight into the Objects browser / smart
|
|
* albums / search.
|
|
*
|
|
* POST { imageBase64, width, height } -> { objects: [{ label, confidence, box }] }
|
|
* Auth: session-gated (see lib/server/session.ts). Rate limit: 30/min.
|
|
*/
|
|
|
|
const MAX_BASE64 = 4_000_000; // ~3 MB decoded — under serverless body limits
|
|
|
|
export async function POST(req: NextRequest) {
|
|
const gate = await guard(req, "classify");
|
|
if (!gate.ok) return gate.response;
|
|
|
|
if (!process.env.RUNPOD_API_KEY || !process.env.RUNPOD_YOLO_URL) {
|
|
return NextResponse.json(
|
|
{ error: "RunPod detection is not configured (set RUNPOD_API_KEY + RUNPOD_YOLO_URL)." },
|
|
{ status: 503 },
|
|
);
|
|
}
|
|
|
|
let body: unknown;
|
|
try {
|
|
body = await req.json();
|
|
} catch {
|
|
return NextResponse.json({ error: "Invalid request body." }, { status: 400 });
|
|
}
|
|
|
|
const { imageBase64, width, height } = (body ?? {}) as {
|
|
imageBase64?: unknown;
|
|
width?: unknown;
|
|
height?: unknown;
|
|
};
|
|
if (
|
|
typeof imageBase64 !== "string" ||
|
|
imageBase64.length === 0 ||
|
|
imageBase64.length > MAX_BASE64
|
|
) {
|
|
return NextResponse.json({ error: "Invalid or oversized image." }, { status: 400 });
|
|
}
|
|
const w = Number(width);
|
|
const h = Number(height);
|
|
|
|
try {
|
|
const objects = await rpDetect(
|
|
imageBase64,
|
|
Number.isFinite(w) && w > 0 ? w : 1,
|
|
Number.isFinite(h) && h > 0 ? h : 1,
|
|
);
|
|
return NextResponse.json({ objects });
|
|
} catch (err) {
|
|
const message = err instanceof Error ? err.message : "Detection failed.";
|
|
const status = err instanceof RunpodError ? err.status : 502;
|
|
return NextResponse.json({ error: message }, { status });
|
|
}
|
|
}
|