import { NextRequest, NextResponse } from "next/server"; import { z } from "zod"; import { auth } from "@/app/(auth)/auth"; import { getEditableAgentPsdTemplateLayers, isAgentPsdImageLayerSlot, isAgentPsdTemplateReady, isAgentPsdTextLayerSlot, type AgentPsdTemplateRenderResponse, } from "@/lib/agents/psd-template-config"; import { readAgentPsdFonts } from "@/lib/agents/psd-font-settings"; import { renderAgentPsdTemplate } from "@/lib/agents/psd-template-renderer"; import { readAgentPsdTemplates } from "@/lib/agents/psd-template-settings"; export const runtime = "nodejs"; const renderSchema = z.object({ templateId: z.string().trim().min(1), sourceImageUrl: z.string().trim().optional().nullable(), imageAssignments: z.record(z.string(), z.string()).default({}), textAssignments: z.record(z.string(), z.string()).default({}), }); function jsonError(status: number, error: string) { const payload: AgentPsdTemplateRenderResponse = { success: false, error, }; return NextResponse.json(payload, { status }); } export async function POST(request: NextRequest) { const session = await auth().catch(() => null); if (!session?.user?.id) { return jsonError(401, "未登录"); } let body: z.infer; try { body = renderSchema.parse(await request.json()); } catch { return jsonError(400, "渲染参数不合法"); } try { const templates = await readAgentPsdTemplates(); const template = templates.find( (entry) => entry.id === body.templateId && entry.status === "active" && isAgentPsdTemplateReady(entry) ); if (!template) { return jsonError(404, "PSD 模板不存在或未启用"); } const editableLayers = getEditableAgentPsdTemplateLayers(template); if (editableLayers.length === 0) { return jsonError(400, "当前模板未配置可编辑图层"); } const fonts = await readAgentPsdFonts(); const fontMap = new Map(fonts.map((font) => [font.id, font])); const missingFontLayer = editableLayers.find( (layer) => isAgentPsdTextLayerSlot(layer) && layer.fontId && !fontMap.has(layer.fontId) ); if (missingFontLayer) { return jsonError(400, `文字图层“${missingFontLayer.layerName}”引用的字体已不存在`); } const imageAssignments: Record = {}; const textAssignments: Record = {}; for (const layer of editableLayers) { if (isAgentPsdImageLayerSlot(layer)) { const explicitUrl = String(body.imageAssignments[layer.id] || "").trim(); if (explicitUrl) { imageAssignments[layer.id] = explicitUrl; continue; } if (layer.isDefaultResultSource && body.sourceImageUrl?.trim()) { imageAssignments[layer.id] = body.sourceImageUrl.trim(); } } if (isAgentPsdTextLayerSlot(layer)) { const nextText = String(body.textAssignments[layer.id] || layer.defaultText || "").trim(); if (nextText) { textAssignments[layer.id] = nextText; } } } const missingRequiredLayer = editableLayers.find( (layer) => layer.isRequired && (isAgentPsdImageLayerSlot(layer) ? !imageAssignments[layer.id] : !textAssignments[layer.id]) ); if (missingRequiredLayer) { return jsonError(400, `缺少必传素材:${missingRequiredLayer.layerName}`); } const uploaded = await renderAgentPsdTemplate({ template, imageAssignments, textAssignments, fonts, }); return NextResponse.json({ success: true, result: { url: uploaded.url, pathname: uploaded.pathname, contentType: uploaded.contentType, }, } satisfies AgentPsdTemplateRenderResponse); } catch (error) { console.error("[PSD Templates] render failed:", error); return jsonError(502, "PSD 模板渲染失败"); } }