135 lines
4.1 KiB
TypeScript
135 lines
4.1 KiB
TypeScript
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 }
|
|
);
|
|
}
|
|
}
|