Your Name 17dcfcd55d
Some checks failed
Lint / build (20) (push) Has been cancelled
Playwright Tests / test (push) Has been cancelled
feat: add PSD template font rendering workflow
2026-03-27 11:01:13 +08:00

134 lines
3.8 KiB
TypeScript

import { randomUUID } from "node:crypto";
import { NextRequest, NextResponse } from "next/server";
import { upsertSystemSetting } from "@/lib/db/queries";
import { requireAgentWorkflowAdmin } from "@/lib/middleware/admin";
import { uploadFile } from "@/lib/storage/service";
import {
AGENT_PSD_FONTS_KEY,
upsertAgentPsdFont,
type AgentPsdFont,
type AgentPsdFontListResponse,
type AgentPsdFontMutationResponse,
} from "@/lib/agents/psd-font-config";
import { readAgentPsdFonts } from "@/lib/agents/psd-font-settings";
export const runtime = "nodejs";
const MAX_FONT_FILE_SIZE = 20 * 1024 * 1024;
const ALLOWED_FONT_EXTENSIONS = new Set(["ttf", "otf", "woff", "woff2"]);
function jsonListError(status: number, error: string) {
const payload: AgentPsdFontListResponse = {
success: false,
fonts: [],
error,
};
return NextResponse.json(payload, { status });
}
function jsonMutationError(status: number, error: string) {
const payload: AgentPsdFontMutationResponse = {
success: false,
error,
};
return NextResponse.json(payload, { status });
}
function getFileExtension(fileName: string) {
return fileName.toLowerCase().split(".").pop() || "";
}
function getDefaultFontName(fileName: string) {
return fileName.replace(/\.[^.]+$/, "").trim() || "未命名字体";
}
export async function GET() {
const session = await requireAgentWorkflowAdmin().catch(() => null);
if (!session?.user?.id) {
return jsonListError(401, "未登录");
}
try {
const fonts = await readAgentPsdFonts();
return NextResponse.json({
success: true,
fonts,
} satisfies AgentPsdFontListResponse);
} catch (error) {
console.error("[PSD Fonts][Admin] list failed:", error);
return jsonListError(503, "字体库读取失败");
}
}
export async function POST(request: NextRequest) {
const session = await requireAgentWorkflowAdmin().catch(() => null);
if (!session?.user?.id) {
return jsonMutationError(401, "未登录");
}
let formData: FormData;
try {
formData = await request.formData();
} catch {
return jsonMutationError(400, "上传表单解析失败");
}
const file = formData.get("file");
const name =
String(formData.get("name") || "").trim() ||
(file instanceof File ? getDefaultFontName(file.name) : "");
const family = String(formData.get("family") || "").trim() || name;
if (!(file instanceof File)) {
return jsonMutationError(400, "缺少字体文件");
}
const extension = getFileExtension(file.name);
if (!ALLOWED_FONT_EXTENSIONS.has(extension)) {
return jsonMutationError(400, "字体文件仅支持 ttf / otf / woff / woff2");
}
if (file.size <= 0) {
return jsonMutationError(400, "字体文件不能为空");
}
if (file.size > MAX_FONT_FILE_SIZE) {
return jsonMutationError(400, "字体文件不能超过 20MB");
}
try {
const fileBuffer = await file.arrayBuffer();
const uploaded = await uploadFile(file.name, fileBuffer);
const now = new Date().toISOString();
const nextFont: AgentPsdFont = {
id: randomUUID(),
name,
family,
fileName: file.name,
fileUrl: uploaded.url,
filePath: uploaded.pathname,
contentType: uploaded.contentType || file.type || undefined,
createdAt: now,
updatedAt: now,
uploadedById: session.user.id,
};
const currentFonts = await readAgentPsdFonts();
const fonts = upsertAgentPsdFont(currentFonts, nextFont);
await upsertSystemSetting({
key: AGENT_PSD_FONTS_KEY,
value: fonts,
updatedBy: session.user.id,
});
return NextResponse.json({
success: true,
font: nextFont,
} satisfies AgentPsdFontMutationResponse);
} catch (error) {
console.error("[PSD Fonts][Admin] create failed:", error);
return jsonMutationError(502, "字体上传失败");
}
}