import { eq } from "drizzle-orm"; import { NextResponse } from "next/server"; import { z } from "zod"; import { db } from "@/lib/db"; import { externalImageApiRequestLog } from "@/lib/db/schema"; import { saveGeneratedImages } from "@/lib/ai/runtime/generated-image-persistence"; import { authenticateExternalImageApiRequest, buildExternalImageApiKeyFingerprint, ensureHostedImageMarkdown, extractGeneratedImagesFromMarkdown, generateImagesWithModel, getRequesterIp, resolveExternalImageApiModel, resolveExternalImageApiOwnerUserId, serializeExternalImageApiModel, } from "@/lib/external-image-api/service"; export const runtime = "nodejs"; export const maxDuration = 120; const requestSchema = z.object({ model: z.string().trim().min(1).optional(), prompt: z.string().trim().min(1, "prompt 不能为空"), negativePrompt: z.string().trim().optional(), imageCount: z.coerce.number().int().min(1).max(8).optional(), aspectRatio: z.string().trim().max(32).optional(), imageSize: z.string().trim().max(32).optional(), metadata: z.record(z.string(), z.any()).optional(), }); export async function POST(request: Request) { const authResult = await authenticateExternalImageApiRequest(request); if (!authResult.accessKey) { return NextResponse.json( { success: false, error: authResult.error, }, { status: authResult.status }, ); } const startedAt = Date.now(); let logId: string | null = null; try { const json = await request.json(); const parsed = requestSchema.safeParse(json); if (!parsed.success) { return NextResponse.json( { success: false, error: parsed.error.errors[0]?.message || "参数错误", }, { status: 400 }, ); } const payload = parsed.data; const selectedModel = await resolveExternalImageApiModel(payload.model); const ownerUserId = await resolveExternalImageApiOwnerUserId( authResult.config.defaultOwnerEmail, ); const requesterIp = getRequesterIp(request); const requesterUserAgent = request.headers.get("user-agent") || null; const [createdLog] = await db .insert(externalImageApiRequestLog) .values({ apiKeyId: authResult.accessKey.id, apiKeyName: authResult.accessKey.name, ownerUserId, modelDbId: selectedModel.model.id, modelRuntimeId: selectedModel.model.modelId, modelDisplayName: selectedModel.model.displayName, prompt: payload.prompt, negativePrompt: payload.negativePrompt || null, imageCount: payload.imageCount || 1, aspectRatio: payload.aspectRatio || null, imageSize: payload.imageSize || null, status: "pending", requesterIp, requesterUserAgent, requestPayload: { ...payload, apiKeyFingerprint: buildExternalImageApiKeyFingerprint( authResult.accessKey, ), }, }) .returning({ id: externalImageApiRequestLog.id }); logId = createdLog?.id || null; const rawMarkdown = await generateImagesWithModel({ modelDbId: selectedModel.model.id, prompt: payload.prompt, negativePrompt: payload.negativePrompt || null, imageCount: payload.imageCount || 1, aspectRatio: payload.aspectRatio || null, imageSize: payload.imageSize || null, }); const hostedMarkdown = await ensureHostedImageMarkdown( rawMarkdown, payload.imageSize || "2K", ); const images = extractGeneratedImagesFromMarkdown(hostedMarkdown); if (!images.length) { throw new Error("模型返回结果里未解析到图片"); } const persistResult = await saveGeneratedImages( hostedMarkdown, ownerUserId, null, null, payload.prompt, selectedModel.model.id, { sourceType: "external_api_generated", userPrompt: payload.prompt, optimizedPrompt: payload.prompt, requestedImageCount: payload.imageCount || images.length, aspectRatio: payload.aspectRatio, imageSize: payload.imageSize, skipBilling: true, }, ); if (logId) { await db .update(externalImageApiRequestLog) .set({ status: "succeeded", responsePayload: { model: serializeExternalImageApiModel(selectedModel), markdown: hostedMarkdown, images, }, generatedImageIds: persistResult.savedImageIds, generatedAssetIds: persistResult.savedAssetIds, generatedImageUrls: persistResult.savedOriginalUrls, durationMs: Date.now() - startedAt, completedAt: new Date(), }) .where(eq(externalImageApiRequestLog.id, logId)); } return NextResponse.json({ success: true, data: { requestId: logId, model: serializeExternalImageApiModel(selectedModel), images, generatedImageIds: persistResult.savedImageIds, generatedAssetIds: persistResult.savedAssetIds, }, }); } catch (error) { console.error("[External Image API] Generate failed:", error); if (logId) { await db .update(externalImageApiRequestLog) .set({ status: "failed", errorMessage: error instanceof Error ? error.message : "图片生成失败", durationMs: Date.now() - startedAt, completedAt: new Date(), }) .where(eq(externalImageApiRequestLog.id, logId)); } return NextResponse.json( { success: false, error: error instanceof Error ? error.message : "图片生成失败", }, { status: 500 }, ); } }