fix: remove psd runtime source fallback
This commit is contained in:
parent
ebbab98a41
commit
e74586c8a0
@ -1,11 +1,11 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { z } from "zod";
|
||||
import { auth } from "@/app/(auth)/auth";
|
||||
import { upsertSystemSetting } from "@/lib/db/queries";
|
||||
import {
|
||||
AGENT_PSD_TEMPLATES_KEY,
|
||||
getEditableAgentPsdTemplateLayers,
|
||||
getAgentPsdTemplateRuntimeIssue,
|
||||
isAgentPsdImageLayerSlot,
|
||||
isAgentPsdTemplateRuntimeReady,
|
||||
isAgentPsdTemplateReady,
|
||||
isAgentPsdTextLayerSlot,
|
||||
type AgentPsdTemplateRenderResponse,
|
||||
@ -13,9 +13,8 @@ import {
|
||||
import { readAgentPsdFonts } from "@/lib/agents/psd-font-settings";
|
||||
import { normalizeAgentPsdImagePlacement } from "@/lib/agents/psd-image-placement";
|
||||
import { describeRemoteAssetLocation } from "@/lib/agents/psd-remote-asset";
|
||||
import { repairAgentPsdTemplateAssetsIfNeeded } from "@/lib/agents/psd-template-repair";
|
||||
import { repairAgentPsdTemplateFontBindings } from "@/lib/agents/psd-template-repair";
|
||||
import { renderAgentPsdTemplate } from "@/lib/agents/psd-template-renderer";
|
||||
import { resolveAgentPsdTemplateTextStyles } from "@/lib/agents/psd-template-style-resolver";
|
||||
import { normalizeHexColor } from "@/lib/agents/psd-text-layout";
|
||||
import { readAgentPsdTemplates } from "@/lib/agents/psd-template-settings";
|
||||
|
||||
@ -62,43 +61,32 @@ export async function POST(request: NextRequest) {
|
||||
|
||||
try {
|
||||
const templates = await readAgentPsdTemplates();
|
||||
const templateIndex = templates.findIndex(
|
||||
const template = templates.find(
|
||||
(entry) =>
|
||||
entry.id === body.templateId &&
|
||||
entry.status === "active" &&
|
||||
isAgentPsdTemplateReady(entry)
|
||||
);
|
||||
const template = templateIndex >= 0 ? templates[templateIndex] : undefined;
|
||||
|
||||
if (!template) {
|
||||
return jsonError(404, "PSD 模板不存在或未启用");
|
||||
}
|
||||
|
||||
const repairedTemplate = await repairAgentPsdTemplateAssetsIfNeeded(template);
|
||||
if (repairedTemplate !== template) {
|
||||
await upsertSystemSetting({
|
||||
key: AGENT_PSD_TEMPLATES_KEY,
|
||||
value: templates.map((entry, index) =>
|
||||
index === templateIndex ? repairedTemplate : entry
|
||||
),
|
||||
updatedBy: session.user.id,
|
||||
}).catch((error) => {
|
||||
console.warn("[PSD Templates] failed to persist repaired template before render:", {
|
||||
templateId: template.id,
|
||||
error,
|
||||
});
|
||||
});
|
||||
const fonts = await readAgentPsdFonts();
|
||||
const resolvedTemplate = repairAgentPsdTemplateFontBindings(template, fonts);
|
||||
if (!isAgentPsdTemplateRuntimeReady(resolvedTemplate)) {
|
||||
return jsonError(
|
||||
409,
|
||||
getAgentPsdTemplateRuntimeIssue(resolvedTemplate) ||
|
||||
"PSD 模板资源不完整,请在后台重新解析后再导出"
|
||||
);
|
||||
}
|
||||
|
||||
const resolvedTemplate = await resolveAgentPsdTemplateTextStyles(repairedTemplate).catch(
|
||||
() => repairedTemplate
|
||||
);
|
||||
const editableLayers = getEditableAgentPsdTemplateLayers(resolvedTemplate);
|
||||
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)
|
||||
|
||||
@ -7,6 +7,7 @@ import {
|
||||
getEditableAgentPsdTemplateLayers,
|
||||
isAgentPsdTextLayerSlot,
|
||||
isAgentPsdTemplateReady,
|
||||
isAgentPsdTemplateRuntimeReady,
|
||||
normalizeAgentPsdTemplateMode,
|
||||
type AgentPsdTemplateListResponse,
|
||||
} from "@/lib/agents/psd-template-config";
|
||||
@ -92,6 +93,7 @@ export async function GET(request: NextRequest) {
|
||||
requestedBrand ? template.brandName === requestedBrand : true
|
||||
)
|
||||
.filter((template) => (requestedMode ? template.mode === requestedMode : true))
|
||||
.filter(isAgentPsdTemplateRuntimeReady)
|
||||
.filter((template) => getEditableAgentPsdTemplateLayers(template).length > 0)
|
||||
.map((template) => repairAgentPsdTemplateFontBindings(template, fonts))
|
||||
.map((template) => ({
|
||||
|
||||
@ -503,6 +503,58 @@ export function isAgentPsdTemplateReady(template: AgentPsdTemplate) {
|
||||
return template.parseStatus === "completed" && template.layers.length > 0;
|
||||
}
|
||||
|
||||
export function hasAgentPsdTemplateStoredLayerSource(
|
||||
layer: Pick<AgentPsdTemplateLayerSlot, "sourceUrl" | "sourcePath">
|
||||
) {
|
||||
return Boolean(
|
||||
String(layer.sourcePath || "").trim() || String(layer.sourceUrl || "").trim()
|
||||
);
|
||||
}
|
||||
|
||||
export function hasAgentPsdTemplateDedicatedRenderBase(
|
||||
template: Pick<AgentPsdTemplate, "renderBaseUrl" | "renderBasePath" | "previewUrl">
|
||||
) {
|
||||
const renderBaseUrl = String(template.renderBaseUrl || "").trim();
|
||||
const previewUrl = String(template.previewUrl || "").trim();
|
||||
return Boolean(String(template.renderBasePath || "").trim()) || (
|
||||
Boolean(renderBaseUrl) &&
|
||||
renderBaseUrl !== previewUrl
|
||||
);
|
||||
}
|
||||
|
||||
export function getAgentPsdTemplateRuntimeMissingLayers(template: AgentPsdTemplate) {
|
||||
return template.layers.filter(
|
||||
(layer) => !hasAgentPsdTemplateStoredLayerSource(layer)
|
||||
);
|
||||
}
|
||||
|
||||
export function getAgentPsdTemplateRuntimeIssue(template: AgentPsdTemplate) {
|
||||
if (!isAgentPsdTemplateReady(template)) {
|
||||
return "PSD 模板尚未完成解析,请先在后台重新解析后再使用";
|
||||
}
|
||||
|
||||
const missingLayers = getAgentPsdTemplateRuntimeMissingLayers(template);
|
||||
if (missingLayers.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const layerNames = Array.from(
|
||||
new Set(missingLayers.map((layer) => String(layer.layerName || "").trim()).filter(Boolean))
|
||||
);
|
||||
const summarizedLayerNames =
|
||||
layerNames.length <= 3
|
||||
? layerNames.join("、")
|
||||
: `${layerNames.slice(0, 3).join("、")} 等 ${layerNames.length} 个图层`;
|
||||
|
||||
return summarizedLayerNames
|
||||
? `PSD 模板图层资源不完整(${summarizedLayerNames}),请在后台重新解析该模板后再导出`
|
||||
: "PSD 模板图层资源不完整,请在后台重新解析该模板后再导出";
|
||||
}
|
||||
|
||||
export function isAgentPsdTemplateRuntimeReady(template: AgentPsdTemplate) {
|
||||
return getAgentPsdTemplateRuntimeIssue(template) === null;
|
||||
}
|
||||
|
||||
export function getEditableAgentPsdTemplateLayers(template: AgentPsdTemplate) {
|
||||
return template.layers.filter((layer) => layer.isEditable);
|
||||
}
|
||||
|
||||
@ -2,8 +2,8 @@ import "server-only";
|
||||
|
||||
import { uploadFile } from "@/lib/storage/service";
|
||||
import { fetchRemoteAssetBuffer } from "@/lib/agents/psd-remote-asset";
|
||||
import { loadPsdTemplateSourceFromUrl } from "@/lib/agents/psd-template-parser";
|
||||
import {
|
||||
hasAgentPsdTemplateStoredLayerSource,
|
||||
isAgentPsdImageLayerSlot,
|
||||
isAgentPsdTextLayerSlot,
|
||||
type AgentPsdTemplate,
|
||||
@ -18,19 +18,6 @@ async function getSharp() {
|
||||
return sharp.default;
|
||||
}
|
||||
|
||||
async function rgbaToPngBuffer(rgba: Uint8ClampedArray, width: number, height: number) {
|
||||
const sharp = await getSharp();
|
||||
return sharp(Buffer.from(rgba), {
|
||||
raw: {
|
||||
width,
|
||||
height,
|
||||
channels: 4,
|
||||
},
|
||||
})
|
||||
.png()
|
||||
.toBuffer();
|
||||
}
|
||||
|
||||
async function fitOverlayBufferIntoCanvas(params: {
|
||||
input: Buffer;
|
||||
left: number;
|
||||
@ -341,9 +328,6 @@ export async function renderAgentPsdTemplate(params: {
|
||||
const remoteBufferCache = new Map<string, Buffer>();
|
||||
const fontMap = new Map(fonts.map((font) => [font.id, font]));
|
||||
const shouldUseDedicatedRenderBase = hasDedicatedRenderBase(template);
|
||||
let sourceLayerMapPromise:
|
||||
| Promise<Map<string, Awaited<ReturnType<typeof loadPsdTemplateSourceFromUrl>>["layers"][number]>>
|
||||
| null = null;
|
||||
|
||||
async function readCachedBuffer(url: string, label = "PSD 模板资源") {
|
||||
const normalizedUrl = String(url || "").trim();
|
||||
@ -365,16 +349,6 @@ export async function renderAgentPsdTemplate(params: {
|
||||
return remoteBufferCache.get(normalizedUrl)!;
|
||||
}
|
||||
|
||||
async function getSourceLayerMap() {
|
||||
if (!sourceLayerMapPromise) {
|
||||
sourceLayerMapPromise = loadPsdTemplateSourceFromUrl(template.fileUrl).then(
|
||||
(source) => new Map(source.layers.map((entry) => [entry.layer.layerId, entry]))
|
||||
);
|
||||
}
|
||||
|
||||
return sourceLayerMapPromise;
|
||||
}
|
||||
|
||||
const compositeInputs: Array<{ input: Buffer; left: number; top: number }> = [];
|
||||
let didAppendDedicatedRenderBase = false;
|
||||
|
||||
@ -402,32 +376,14 @@ export async function renderAgentPsdTemplate(params: {
|
||||
return true;
|
||||
}
|
||||
|
||||
async function appendOriginalLayer(layerId: string) {
|
||||
const sourceLayerMap = await getSourceLayerMap();
|
||||
const sourceLayer = sourceLayerMap.get(layerId);
|
||||
if (!sourceLayer) return;
|
||||
const rgba = await sourceLayer.composite();
|
||||
const overlayBuffer = await rgbaToPngBuffer(
|
||||
rgba,
|
||||
sourceLayer.layer.width,
|
||||
sourceLayer.layer.height
|
||||
);
|
||||
await appendBufferOverlay({
|
||||
input: overlayBuffer,
|
||||
left: sourceLayer.layer.x,
|
||||
top: sourceLayer.layer.y,
|
||||
width: sourceLayer.layer.width,
|
||||
height: sourceLayer.layer.height,
|
||||
});
|
||||
}
|
||||
|
||||
async function appendStoredLayer(layer: AgentPsdTemplate["layers"][number]) {
|
||||
const sourceUrl = String(layer.sourceUrl || "").trim();
|
||||
if (!sourceUrl) {
|
||||
await appendOriginalLayer(layer.layerId);
|
||||
return;
|
||||
if (!hasAgentPsdTemplateStoredLayerSource(layer)) {
|
||||
throw new Error(
|
||||
`PSD 模板图层“${layer.layerName}”缺少已打包资源,请在后台重新解析该模板`
|
||||
);
|
||||
}
|
||||
|
||||
const sourceUrl = String(layer.sourceUrl || "").trim();
|
||||
try {
|
||||
const overlayBuffer = await readCachedBuffer(sourceUrl, "PSD 图层素材");
|
||||
const didAppend = await appendBufferOverlay({
|
||||
@ -446,9 +402,10 @@ export async function renderAgentPsdTemplate(params: {
|
||||
layerId: layer.layerId,
|
||||
error,
|
||||
});
|
||||
throw new Error(
|
||||
`PSD 模板图层“${layer.layerName}”资源加载失败,请稍后重试或在后台重新解析该模板`
|
||||
);
|
||||
}
|
||||
|
||||
await appendOriginalLayer(layer.layerId);
|
||||
}
|
||||
|
||||
if (shouldUseDedicatedRenderBase && template.renderBaseUrl) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user