import { NextRequest, NextResponse } from "next/server"; import { auth } from "@/app/(auth)/auth"; import { BoxCutoutServiceError, executeBoxCutoutJob, } from "@/lib/ai/box-cutout/service"; import { RmbgServiceError } from "@/lib/ai/rmbg/service"; import { getPointsSystemConfig } from "@/lib/db/queries"; import { prepareFeaturePointsCharge } from "@/lib/points/feature-charge"; import { getImageToolCreditCost } from "@/lib/points/config"; export const runtime = "nodejs"; export const maxDuration = 60; type BoxCutoutRequest = { imageUrl?: string; cropRect?: { x?: number; y?: number; width?: number; height?: number; }; paddingRatio?: number; prompt?: string; chatId?: string | null; messageId?: string | null; skipGallerySave?: boolean; sourceNodeId?: string; }; export async function POST(request: NextRequest) { try { const session = await auth(); if (!session?.user?.id) { return NextResponse.json({ success: false, error: "未授权,请先登录" }, { status: 401 }); } const body = (await request.json()) as BoxCutoutRequest; const rawImageUrl = String(body.imageUrl || "").trim(); if (!rawImageUrl) { return NextResponse.json({ success: false, error: "缺少 imageUrl 参数" }, { status: 400 }); } const resolvedImageUrl = new URL(rawImageUrl, request.url).toString(); const pointsConfig = await getPointsSystemConfig(); const pointsCharge = await prepareFeaturePointsCharge({ userId: session.user.id, unitCost: getImageToolCreditCost(pointsConfig, "box-cutout"), reason: "image_box_cutout", modelId: "box-cutout", note: "框选抠图消耗积分", pointsConfig, }); if (!pointsCharge.ok) { return NextResponse.json(pointsCharge.payload, { status: pointsCharge.status }); } const result = await executeBoxCutoutJob({ userId: session.user.id, imageUrl: resolvedImageUrl, cropRect: body.cropRect || {}, paddingRatio: body.paddingRatio, prompt: body.prompt || "框选抠图", chatId: body.chatId || null, messageId: body.messageId || null, skipGallerySave: body.skipGallerySave === true, sourceNodeId: body.sourceNodeId, }); if (pointsCharge.chargeable) { try { await pointsCharge.consume(); } catch (pointsError) { console.error("[Points] Failed to consume box cutout points:", pointsError); } } return NextResponse.json(result); } catch (error) { if (error instanceof BoxCutoutServiceError || error instanceof RmbgServiceError) { return NextResponse.json( { success: false, error: error.detail || error.message, }, { status: error.status }, ); } console.error("[BoxCutout] 处理失败:", error); return NextResponse.json( { success: false, error: error instanceof Error ? error.message : "框选抠图失败", }, { status: 500 }, ); } }