import { randomUUID } from "node:crypto"; import { NextRequest, NextResponse } from "next/server"; import { z } from "zod"; import { auth } from "@/app/(auth)/auth"; import { executeRmbgJob, RmbgServiceError } from "@/lib/ai/rmbg/service"; import { upsertMediaAsset } from "@/lib/db/queries"; export const runtime = "nodejs"; export const maxDuration = 180; const payloadSchema = z.object({ source: z.enum(["primary", "secondary"]).default("primary"), query: z.string().trim().max(64).optional(), queryType: z.enum(["sku", "spu"]).optional(), matchedSku: z.string().trim().max(64).optional(), matchedSpu: z.string().trim().max(64).optional(), productModel: z.string().trim().max(128).optional(), }); function sanitizeFilenameStem(value: string) { return String(value || "") .trim() .replace(/\.[^.]+$/, "") .replace(/[\\/:*?"<>|]+/g, "-") .replace(/\s+/g, "-") .replace(/-+/g, "-") .replace(/^-|-$/g, "") .slice(0, 80); } export async function POST(request: NextRequest) { try { const session = await auth(); if (!session?.user?.id) { return NextResponse.json({ error: "未登录" }, { status: 401 }); } const formData = await request.formData(); const file = formData.get("file"); if (!(file instanceof File)) { return NextResponse.json({ error: "缺少图片文件" }, { status: 400 }); } if (!String(file.type || "").startsWith("image/")) { return NextResponse.json({ error: "请上传图片文件" }, { status: 400 }); } const payload = payloadSchema.parse({ source: formData.get("source"), query: formData.get("query"), queryType: formData.get("queryType"), matchedSku: formData.get("matchedSku"), matchedSpu: formData.get("matchedSpu"), productModel: formData.get("productModel"), }); const arrayBuffer = await file.arrayBuffer(); const imageBuffer = Buffer.from(arrayBuffer); const promptPrefix = payload.source === "secondary" ? "搭配SKU" : "主SPU"; const prompt = `${promptPrefix}素材抠图`; const result = await executeRmbgJob({ userId: session.user.id, imageBuffer, prompt, skipGallerySave: true, }); const originalStem = sanitizeFilenameStem(file.name) || `workflow-upload-${Date.now()}`; const displayName = `${originalStem}-rmbg.png`; const asset = await upsertMediaAsset({ userId: session.user.id, mediaType: "image", sourceType: "agent_workflow_material_upload", status: "ready", originalUrl: result.original_url, previewUrl: result.original_url, thumbnailUrl: result.thumbnail_url, mimeType: "image/png", displayName, prompt, modelId: result.provider, sourceTable: "agent_workflow_material_upload", metadata: { source: payload.source, query: payload.query || null, queryType: payload.queryType || "sku", matchedSku: payload.matchedSku || null, matchedSpu: payload.matchedSpu || null, productModel: payload.productModel || null, originalFileName: file.name, provider: result.provider, }, }); const imageId = asset?.id || `upload-${randomUUID()}`; return NextResponse.json({ success: true, data: { id: imageId, key: imageId, label: "用户上传", fileName: displayName, imageUrl: result.original_url, assetId: asset?.id ?? null, mimeType: "image/png", sourceType: "upload", group: "other", sortOrder: 0, }, }); } catch (error) { if (error instanceof z.ZodError) { return NextResponse.json( { error: error.issues[0]?.message || "请求参数不合法" }, { status: 400 } ); } if (error instanceof RmbgServiceError) { return NextResponse.json( { error: error.message, detail: error.detail || error.message }, { status: error.status } ); } console.error("[Agent Workflow Material Upload] Failed to process upload:", error); return NextResponse.json( { error: error instanceof Error ? error.message : "上传失败" }, { status: 500 } ); } }