134 lines
4.1 KiB
TypeScript
134 lines
4.1 KiB
TypeScript
import { NextResponse } from "next/server";
|
||
import { z } from "zod";
|
||
import { auth } from "@/app/(auth)/auth";
|
||
import {
|
||
createCanvasImageTask,
|
||
enqueueCanvasImageTask,
|
||
getCanvasImageTaskForUser,
|
||
} from "@/lib/canvas/image-task-queue";
|
||
import { type CanvasGenerateImageRequest } from "@/lib/canvas/generate-image";
|
||
import { getModelGenerationCreditCost } from "@/lib/ai/image-model-pricing";
|
||
import { prepareFeaturePointsCharge } from "@/lib/points/feature-charge";
|
||
import {
|
||
findStyleProImageModelByRef,
|
||
getStyleProImageModelEntries,
|
||
} from "@/lib/style-pro/model-options";
|
||
|
||
const requestSchema = z.object({
|
||
prompt: z.string().min(1).max(5000),
|
||
referenceImage: z.string().min(1).max(200000),
|
||
selectedModelId: z.string().min(1).max(128),
|
||
imageSize: z.string().min(1).max(16).optional(),
|
||
promptExtend: z.boolean().optional(),
|
||
aspectRatio: z.string().min(1).max(16).optional(),
|
||
title: z.string().max(128).optional(),
|
||
});
|
||
|
||
export const dynamic = "force-dynamic";
|
||
export const maxDuration = 120;
|
||
|
||
export async function POST(request: Request) {
|
||
try {
|
||
const session = await auth();
|
||
if (!session?.user?.id) {
|
||
return NextResponse.json(
|
||
{ success: false, error: "未登录" },
|
||
{ status: 401 }
|
||
);
|
||
}
|
||
|
||
const body = requestSchema.parse(await request.json());
|
||
const imageModelEntries = await getStyleProImageModelEntries({
|
||
includeHidden: true,
|
||
});
|
||
const selectedModel = findStyleProImageModelByRef(
|
||
imageModelEntries,
|
||
body.selectedModelId
|
||
);
|
||
|
||
if (!selectedModel) {
|
||
return NextResponse.json(
|
||
{ success: false, error: "所选 AI洗图 生图模型不可用" },
|
||
{ status: 400 }
|
||
);
|
||
}
|
||
|
||
const imageSize = body.imageSize?.trim() || "2K";
|
||
const unitCost = getModelGenerationCreditCost(selectedModel.model as any, "image", {
|
||
imageSize,
|
||
});
|
||
const pointsCharge = await prepareFeaturePointsCharge({
|
||
userId: session.user.id,
|
||
unitCost,
|
||
reason: "ai_wash_generation",
|
||
modelId: selectedModel.model.id,
|
||
note: `AI洗图消耗积分(${body.title || selectedModel.model.displayName})`,
|
||
});
|
||
|
||
if (!pointsCharge.ok) {
|
||
return NextResponse.json(pointsCharge.payload, {
|
||
status: pointsCharge.status,
|
||
});
|
||
}
|
||
|
||
const payload: CanvasGenerateImageRequest = {
|
||
prompt: body.prompt.trim(),
|
||
model: selectedModel.model.id,
|
||
referenceImage: body.referenceImage.trim(),
|
||
imageSize,
|
||
imageCount: 1,
|
||
promptExtend: body.promptExtend === true,
|
||
aspectRatio: body.aspectRatio?.trim() || "auto",
|
||
ownerKey: session.user.id,
|
||
debugLabel: "ai-wash",
|
||
debugMeta: {
|
||
selectedModelId: selectedModel.model.id,
|
||
selectedModelName: selectedModel.model.displayName,
|
||
title: body.title || "",
|
||
},
|
||
};
|
||
|
||
const task = await createCanvasImageTask({
|
||
userId: session.user.id,
|
||
request: payload as unknown as Record<string, unknown>,
|
||
});
|
||
await enqueueCanvasImageTask(task.id);
|
||
const currentTask = await getCanvasImageTaskForUser({
|
||
taskId: task.id,
|
||
userId: session.user.id,
|
||
});
|
||
|
||
if (pointsCharge.chargeable) {
|
||
try {
|
||
await pointsCharge.consume({
|
||
note: `AI洗图消耗积分(${body.title || selectedModel.model.displayName} / ${task.id})`,
|
||
});
|
||
} catch (pointsError) {
|
||
console.error("[AI Wash] Failed to consume points:", pointsError);
|
||
}
|
||
}
|
||
|
||
return NextResponse.json({
|
||
success: true,
|
||
taskId: task.id,
|
||
status: currentTask?.status || task.status,
|
||
queueStatus: currentTask?.queueStatus,
|
||
unitCost,
|
||
modelId: selectedModel.model.id,
|
||
modelName: selectedModel.model.displayName,
|
||
});
|
||
} catch (error) {
|
||
console.error("[AI Wash Generate] Failed:", error);
|
||
if (error instanceof z.ZodError) {
|
||
return NextResponse.json(
|
||
{ success: false, error: error.issues[0]?.message || "请求参数错误" },
|
||
{ status: 400 }
|
||
);
|
||
}
|
||
return NextResponse.json(
|
||
{ success: false, error: error instanceof Error ? error.message : "AI洗图失败" },
|
||
{ status: 500 }
|
||
);
|
||
}
|
||
}
|