145 lines
4.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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";
import { getAiModelFrontendAvailability } from "@/lib/ai/model-frontend-availability";
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 availability = getAiModelFrontendAvailability(selectedModel.model);
if (availability.selectable === false) {
return NextResponse.json(
{
success: false,
error: availability.disabledReason || "所选 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 }
);
}
}