167 lines
5.0 KiB
TypeScript
167 lines
5.0 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() || "未命名字体";
|
|
}
|
|
|
|
function getUploadFontFiles(formData: FormData) {
|
|
const multipleFiles = formData.getAll("files").filter((item): item is File => item instanceof File);
|
|
if (multipleFiles.length > 0) {
|
|
return multipleFiles;
|
|
}
|
|
|
|
const singleFile = formData.get("file");
|
|
return singleFile instanceof File ? [singleFile] : [];
|
|
}
|
|
|
|
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 files = getUploadFontFiles(formData);
|
|
const customName = String(formData.get("name") || "").trim();
|
|
const customFamily = String(formData.get("family") || "").trim();
|
|
const brandName = String(formData.get("brandName") || "").trim();
|
|
|
|
if (files.length === 0) {
|
|
return jsonMutationError(400, "缺少字体文件");
|
|
}
|
|
|
|
if (brandName.length > 64) {
|
|
return jsonMutationError(400, "字体品牌不能超过 64 个字符");
|
|
}
|
|
|
|
for (const file of files) {
|
|
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 now = new Date().toISOString();
|
|
const currentFonts = await readAgentPsdFonts();
|
|
const createdFonts = await Promise.all(
|
|
files.map(async (file, index) => {
|
|
const fileBuffer = await file.arrayBuffer();
|
|
const uploaded = await uploadFile(file.name, fileBuffer);
|
|
const defaultName = getDefaultFontName(file.name);
|
|
const resolvedName =
|
|
files.length === 1 ? customName || defaultName : defaultName;
|
|
const resolvedFamily =
|
|
customFamily ||
|
|
(files.length === 1 ? customName || defaultName : defaultName);
|
|
|
|
const nextFont: AgentPsdFont = {
|
|
id: randomUUID(),
|
|
name: resolvedName,
|
|
family: resolvedFamily,
|
|
brandName: brandName || undefined,
|
|
fileName: file.name,
|
|
fileUrl: uploaded.url,
|
|
filePath: uploaded.pathname,
|
|
contentType: uploaded.contentType || file.type || undefined,
|
|
createdAt: now,
|
|
updatedAt: new Date(Date.parse(now) + index).toISOString(),
|
|
uploadedById: session.user.id,
|
|
};
|
|
|
|
return nextFont;
|
|
})
|
|
);
|
|
|
|
const fonts = createdFonts.reduce(
|
|
(collection, font) => upsertAgentPsdFont(collection, font),
|
|
currentFonts
|
|
);
|
|
|
|
await upsertSystemSetting({
|
|
key: AGENT_PSD_FONTS_KEY,
|
|
value: fonts,
|
|
updatedBy: session.user.id,
|
|
});
|
|
|
|
return NextResponse.json({
|
|
success: true,
|
|
font: createdFonts[0],
|
|
fonts: createdFonts,
|
|
} satisfies AgentPsdFontMutationResponse);
|
|
} catch (error) {
|
|
console.error("[PSD Fonts][Admin] create failed:", error);
|
|
return jsonMutationError(502, "字体上传失败");
|
|
}
|
|
}
|