Your Name c7ffcda87b
Some checks failed
Lint / build (20) (push) Has been cancelled
Playwright Tests / test (push) Has been cancelled
feat: add infinite canvas feature with AI image generation
- Add canvas project and node database tables
- Create canvas workspace with drag, resize, zoom and pan
- Add ImageNode with background removal, enhance, extend, angle edit, scene fusion
- Add VideoNode with playback controls
- Add TextNode for text editing
- Integrate AI image generation API (Gemini/Alibaba)
- Implement viewport culling for performance optimization
- Add canvas entry in top navigation bar
2026-01-24 19:48:13 +08:00

150 lines
4.2 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 { auth } from "@/app/(auth)/auth";
import { getEnabledImageModels, getProviderById } from "@/lib/db/queries";
import { createGeminiImage } from "@/lib/ai/providers/gemini-image";
import { createAlibabaImage } from "@/lib/ai/providers/alibaba-image";
export const maxDuration = 120;
interface GenerateImageRequest {
prompt: string;
negativePrompt?: string;
model?: string; // 模型 ID
aspectRatio?: string; // 宽高比 "1:1", "16:9" 等
imageCount?: number; // 生成数量
referenceImage?: string; // 参考图片(用于图生图)
}
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: GenerateImageRequest = await request.json();
const { prompt, negativePrompt, model, aspectRatio = "1:1", imageCount = 1, referenceImage } = body;
if (!prompt) {
return NextResponse.json(
{ success: false, error: "请输入提示词" },
{ status: 400 }
);
}
// 获取可用的图片生成模型
const imageModels = await getEnabledImageModels();
if (!imageModels || imageModels.length === 0) {
return NextResponse.json(
{ success: false, error: "暂无可用的图片生成模型" },
{ status: 400 }
);
}
// 选择模型(如果指定了 model,使用指定的;否则使用默认的)
let selectedModel = model
? imageModels.find(m => m.model.modelId === model)
: imageModels.find(m => m.model.isDefault) || imageModels[0];
if (!selectedModel) {
selectedModel = imageModels[0];
}
const provider = await getProviderById(selectedModel.model.providerId);
if (!provider) {
return NextResponse.json(
{ success: false, error: "模型提供商配置错误" },
{ status: 500 }
);
}
// 计算尺寸
const sizeMap: Record<string, { width: number; height: number }> = {
"1:1": { width: 1024, height: 1024 },
"3:2": { width: 1536, height: 1024 },
"2:3": { width: 1024, height: 1536 },
"4:3": { width: 1365, height: 1024 },
"3:4": { width: 1024, height: 1365 },
"16:9": { width: 1820, height: 1024 },
"9:16": { width: 1024, height: 1820 },
};
const size = sizeMap[aspectRatio] || sizeMap["1:1"];
// 构建消息
const messages = [
{
role: "user" as const,
content: prompt,
}
];
// 图片参数
const imageParams = {
aspectRatio,
imageSize: `${size.width}x${size.height}`,
imageCount,
negativePrompt,
};
let result;
// 根据提供商类型调用不同的生成器
if (provider.type === "gemini") {
const geminiProvider = createGeminiImage({
apiKey: provider.apiKey,
baseURL: provider.baseUrl,
});
result = await geminiProvider.doGenerate({
messages,
modelId: selectedModel.model.modelId,
imageParams,
});
} else if (provider.type === "alibaba-image") {
const alibabaProvider = createAlibabaImage({
apiKey: provider.apiKey,
baseURL: provider.baseUrl,
});
result = await alibabaProvider.doGenerate({
messages,
modelId: selectedModel.model.modelId,
imageParams,
referenceImage,
});
} else {
return NextResponse.json(
{ success: false, error: "不支持的图片生成模型类型" },
{ status: 400 }
);
}
if (result.images && result.images.length > 0) {
return NextResponse.json({
success: true,
data: {
images: result.images,
model: selectedModel.model.displayName,
prompt,
}
});
} else {
return NextResponse.json(
{ success: false, error: "图片生成失败" },
{ status: 500 }
);
}
} catch (error) {
console.error("[Canvas Generate Image] Error:", error);
return NextResponse.json(
{ success: false, error: error instanceof Error ? error.message : "生成失败" },
{ status: 500 }
);
}
}