From ae8aa4fe6338279bf64891da0a10db4759111649 Mon Sep 17 00:00:00 2001 From: zhishi <1951671751@qq.com> Date: Sun, 29 Mar 2026 02:10:43 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E8=A7=86=E8=A7=89=E6=89=8B?= =?UTF-8?q?=E5=86=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chinese_sweet_romance/images/image | 1 + .../chinese_sweet_romance/images/image222 | 0 src/router.ts | 208 +++++++++--------- src/routes/project/addVisual.ts | 85 +++++++ src/routes/project/deleteVisualManual.ts | 43 ++++ src/routes/project/editVisualManual.ts | 100 +++++++++ src/routes/project/getVisualManual.ts | 68 +++--- src/utils/oss.ts | 6 +- 8 files changed, 376 insertions(+), 135 deletions(-) create mode 100644 data/skills/art_prompts/chinese_sweet_romance/images/image create mode 100644 data/skills/art_prompts/chinese_sweet_romance/images/image222 create mode 100644 src/routes/project/addVisual.ts create mode 100644 src/routes/project/deleteVisualManual.ts create mode 100644 src/routes/project/editVisualManual.ts diff --git a/data/skills/art_prompts/chinese_sweet_romance/images/image b/data/skills/art_prompts/chinese_sweet_romance/images/image new file mode 100644 index 0000000..93a5f6a --- /dev/null +++ b/data/skills/art_prompts/chinese_sweet_romance/images/image @@ -0,0 +1 @@ +123123 \ No newline at end of file diff --git a/data/skills/art_prompts/chinese_sweet_romance/images/image222 b/data/skills/art_prompts/chinese_sweet_romance/images/image222 new file mode 100644 index 0000000..e69de29 diff --git a/src/router.ts b/src/router.ts index f0cb3c0..e56f1c7 100644 --- a/src/router.ts +++ b/src/router.ts @@ -1,4 +1,4 @@ -// @routes-hash 9f9223eb87c107e9ece3145f1b4b254a +// @routes-hash ebc81322f1e60446bf41d1ecb72f2b96 import { Express } from "express"; import route1 from "./routes/agents/clearMemory"; @@ -65,56 +65,59 @@ import route61 from "./routes/production/workbench/getChatLines"; import route62 from "./routes/production/workbench/getVideoModelDetail"; import route63 from "./routes/production/workbench/videoPolling"; import route64 from "./routes/project/addProject"; -import route65 from "./routes/project/delProject"; -import route66 from "./routes/project/editProject"; -import route67 from "./routes/project/getProject"; -import route68 from "./routes/project/getVisualManual"; -import route69 from "./routes/project/visualManual"; -import route70 from "./routes/script/addScript"; -import route71 from "./routes/script/delScript"; -import route72 from "./routes/script/exportScript"; -import route73 from "./routes/script/extractAssets"; -import route74 from "./routes/script/getScrptApi"; -import route75 from "./routes/script/pollScriptAssets"; -import route76 from "./routes/script/updateScript"; -import route77 from "./routes/scriptAgent/getPlanData"; -import route78 from "./routes/scriptAgent/setPlanData"; -import route79 from "./routes/setting/about/checkUpdate"; -import route80 from "./routes/setting/about/downloadApp"; -import route81 from "./routes/setting/agentDeploy/agentSetKey"; -import route82 from "./routes/setting/agentDeploy/deployAgentModel"; -import route83 from "./routes/setting/agentDeploy/getAgentDeploy"; -import route84 from "./routes/setting/dbConfig/clearData"; -import route85 from "./routes/setting/dev/getSwitchAiDevTool"; -import route86 from "./routes/setting/dev/updateSwitchAiDevTool"; -import route87 from "./routes/setting/fileManagement/openFolder"; -import route88 from "./routes/setting/getTextModel"; -import route89 from "./routes/setting/loginConfig/getUser"; -import route90 from "./routes/setting/loginConfig/updateUserPwd"; -import route91 from "./routes/setting/memoryConfig/delAllMemory"; -import route92 from "./routes/setting/memoryConfig/getMemory"; -import route93 from "./routes/setting/memoryConfig/sureMemory"; -import route94 from "./routes/setting/promptManage/getPrompt"; -import route95 from "./routes/setting/promptManage/updatePrompt"; -import route96 from "./routes/setting/skillManagement/backup/addSkill"; -import route97 from "./routes/setting/skillManagement/backup/deleteSkill"; -import route98 from "./routes/setting/skillManagement/backup/embeddingSkill"; -import route99 from "./routes/setting/skillManagement/backup/generateDescription"; -import route100 from "./routes/setting/skillManagement/backup/getSkillList"; -import route101 from "./routes/setting/skillManagement/backup/scanSkills"; -import route102 from "./routes/setting/skillManagement/backup/updateSkill"; -import route103 from "./routes/setting/skillManagement/getSkillList"; -import route104 from "./routes/setting/vendorConfig/addVendor"; -import route105 from "./routes/setting/vendorConfig/deleteVendor"; -import route106 from "./routes/setting/vendorConfig/getVendorList"; -import route107 from "./routes/setting/vendorConfig/modelTest"; -import route108 from "./routes/setting/vendorConfig/updateCode"; -import route109 from "./routes/setting/vendorConfig/updateVendor"; -import route110 from "./routes/task/getProject"; -import route111 from "./routes/task/getTaskApi"; -import route112 from "./routes/task/getTaskCategories"; -import route113 from "./routes/task/taskDetails"; -import route114 from "./routes/test/test"; +import route65 from "./routes/project/addVisual"; +import route66 from "./routes/project/deleteVisualManual"; +import route67 from "./routes/project/delProject"; +import route68 from "./routes/project/editProject"; +import route69 from "./routes/project/editVisualManual"; +import route70 from "./routes/project/getProject"; +import route71 from "./routes/project/getVisualManual"; +import route72 from "./routes/project/visualManual"; +import route73 from "./routes/script/addScript"; +import route74 from "./routes/script/delScript"; +import route75 from "./routes/script/exportScript"; +import route76 from "./routes/script/extractAssets"; +import route77 from "./routes/script/getScrptApi"; +import route78 from "./routes/script/pollScriptAssets"; +import route79 from "./routes/script/updateScript"; +import route80 from "./routes/scriptAgent/getPlanData"; +import route81 from "./routes/scriptAgent/setPlanData"; +import route82 from "./routes/setting/about/checkUpdate"; +import route83 from "./routes/setting/about/downloadApp"; +import route84 from "./routes/setting/agentDeploy/agentSetKey"; +import route85 from "./routes/setting/agentDeploy/deployAgentModel"; +import route86 from "./routes/setting/agentDeploy/getAgentDeploy"; +import route87 from "./routes/setting/dbConfig/clearData"; +import route88 from "./routes/setting/dev/getSwitchAiDevTool"; +import route89 from "./routes/setting/dev/updateSwitchAiDevTool"; +import route90 from "./routes/setting/fileManagement/openFolder"; +import route91 from "./routes/setting/getTextModel"; +import route92 from "./routes/setting/loginConfig/getUser"; +import route93 from "./routes/setting/loginConfig/updateUserPwd"; +import route94 from "./routes/setting/memoryConfig/delAllMemory"; +import route95 from "./routes/setting/memoryConfig/getMemory"; +import route96 from "./routes/setting/memoryConfig/sureMemory"; +import route97 from "./routes/setting/promptManage/getPrompt"; +import route98 from "./routes/setting/promptManage/updatePrompt"; +import route99 from "./routes/setting/skillManagement/backup/addSkill"; +import route100 from "./routes/setting/skillManagement/backup/deleteSkill"; +import route101 from "./routes/setting/skillManagement/backup/embeddingSkill"; +import route102 from "./routes/setting/skillManagement/backup/generateDescription"; +import route103 from "./routes/setting/skillManagement/backup/getSkillList"; +import route104 from "./routes/setting/skillManagement/backup/scanSkills"; +import route105 from "./routes/setting/skillManagement/backup/updateSkill"; +import route106 from "./routes/setting/skillManagement/getSkillList"; +import route107 from "./routes/setting/vendorConfig/addVendor"; +import route108 from "./routes/setting/vendorConfig/deleteVendor"; +import route109 from "./routes/setting/vendorConfig/getVendorList"; +import route110 from "./routes/setting/vendorConfig/modelTest"; +import route111 from "./routes/setting/vendorConfig/updateCode"; +import route112 from "./routes/setting/vendorConfig/updateVendor"; +import route113 from "./routes/task/getProject"; +import route114 from "./routes/task/getTaskApi"; +import route115 from "./routes/task/getTaskCategories"; +import route116 from "./routes/task/taskDetails"; +import route117 from "./routes/test/test"; export default async (app: Express) => { app.use("/api/agents/clearMemory", route1); @@ -181,54 +184,57 @@ export default async (app: Express) => { app.use("/api/production/workbench/getVideoModelDetail", route62); app.use("/api/production/workbench/videoPolling", route63); app.use("/api/project/addProject", route64); - app.use("/api/project/delProject", route65); - app.use("/api/project/editProject", route66); - app.use("/api/project/getProject", route67); - app.use("/api/project/getVisualManual", route68); - app.use("/api/project/visualManual", route69); - app.use("/api/script/addScript", route70); - app.use("/api/script/delScript", route71); - app.use("/api/script/exportScript", route72); - app.use("/api/script/extractAssets", route73); - app.use("/api/script/getScrptApi", route74); - app.use("/api/script/pollScriptAssets", route75); - app.use("/api/script/updateScript", route76); - app.use("/api/scriptAgent/getPlanData", route77); - app.use("/api/scriptAgent/setPlanData", route78); - app.use("/api/setting/about/checkUpdate", route79); - app.use("/api/setting/about/downloadApp", route80); - app.use("/api/setting/agentDeploy/agentSetKey", route81); - app.use("/api/setting/agentDeploy/deployAgentModel", route82); - app.use("/api/setting/agentDeploy/getAgentDeploy", route83); - app.use("/api/setting/dbConfig/clearData", route84); - app.use("/api/setting/dev/getSwitchAiDevTool", route85); - app.use("/api/setting/dev/updateSwitchAiDevTool", route86); - app.use("/api/setting/fileManagement/openFolder", route87); - app.use("/api/setting/getTextModel", route88); - app.use("/api/setting/loginConfig/getUser", route89); - app.use("/api/setting/loginConfig/updateUserPwd", route90); - app.use("/api/setting/memoryConfig/delAllMemory", route91); - app.use("/api/setting/memoryConfig/getMemory", route92); - app.use("/api/setting/memoryConfig/sureMemory", route93); - app.use("/api/setting/promptManage/getPrompt", route94); - app.use("/api/setting/promptManage/updatePrompt", route95); - app.use("/api/setting/skillManagement/backup/addSkill", route96); - app.use("/api/setting/skillManagement/backup/deleteSkill", route97); - app.use("/api/setting/skillManagement/backup/embeddingSkill", route98); - app.use("/api/setting/skillManagement/backup/generateDescription", route99); - app.use("/api/setting/skillManagement/backup/getSkillList", route100); - app.use("/api/setting/skillManagement/backup/scanSkills", route101); - app.use("/api/setting/skillManagement/backup/updateSkill", route102); - app.use("/api/setting/skillManagement/getSkillList", route103); - app.use("/api/setting/vendorConfig/addVendor", route104); - app.use("/api/setting/vendorConfig/deleteVendor", route105); - app.use("/api/setting/vendorConfig/getVendorList", route106); - app.use("/api/setting/vendorConfig/modelTest", route107); - app.use("/api/setting/vendorConfig/updateCode", route108); - app.use("/api/setting/vendorConfig/updateVendor", route109); - app.use("/api/task/getProject", route110); - app.use("/api/task/getTaskApi", route111); - app.use("/api/task/getTaskCategories", route112); - app.use("/api/task/taskDetails", route113); - app.use("/api/test/test", route114); + app.use("/api/project/addVisual", route65); + app.use("/api/project/deleteVisualManual", route66); + app.use("/api/project/delProject", route67); + app.use("/api/project/editProject", route68); + app.use("/api/project/editVisualManual", route69); + app.use("/api/project/getProject", route70); + app.use("/api/project/getVisualManual", route71); + app.use("/api/project/visualManual", route72); + app.use("/api/script/addScript", route73); + app.use("/api/script/delScript", route74); + app.use("/api/script/exportScript", route75); + app.use("/api/script/extractAssets", route76); + app.use("/api/script/getScrptApi", route77); + app.use("/api/script/pollScriptAssets", route78); + app.use("/api/script/updateScript", route79); + app.use("/api/scriptAgent/getPlanData", route80); + app.use("/api/scriptAgent/setPlanData", route81); + app.use("/api/setting/about/checkUpdate", route82); + app.use("/api/setting/about/downloadApp", route83); + app.use("/api/setting/agentDeploy/agentSetKey", route84); + app.use("/api/setting/agentDeploy/deployAgentModel", route85); + app.use("/api/setting/agentDeploy/getAgentDeploy", route86); + app.use("/api/setting/dbConfig/clearData", route87); + app.use("/api/setting/dev/getSwitchAiDevTool", route88); + app.use("/api/setting/dev/updateSwitchAiDevTool", route89); + app.use("/api/setting/fileManagement/openFolder", route90); + app.use("/api/setting/getTextModel", route91); + app.use("/api/setting/loginConfig/getUser", route92); + app.use("/api/setting/loginConfig/updateUserPwd", route93); + app.use("/api/setting/memoryConfig/delAllMemory", route94); + app.use("/api/setting/memoryConfig/getMemory", route95); + app.use("/api/setting/memoryConfig/sureMemory", route96); + app.use("/api/setting/promptManage/getPrompt", route97); + app.use("/api/setting/promptManage/updatePrompt", route98); + app.use("/api/setting/skillManagement/backup/addSkill", route99); + app.use("/api/setting/skillManagement/backup/deleteSkill", route100); + app.use("/api/setting/skillManagement/backup/embeddingSkill", route101); + app.use("/api/setting/skillManagement/backup/generateDescription", route102); + app.use("/api/setting/skillManagement/backup/getSkillList", route103); + app.use("/api/setting/skillManagement/backup/scanSkills", route104); + app.use("/api/setting/skillManagement/backup/updateSkill", route105); + app.use("/api/setting/skillManagement/getSkillList", route106); + app.use("/api/setting/vendorConfig/addVendor", route107); + app.use("/api/setting/vendorConfig/deleteVendor", route108); + app.use("/api/setting/vendorConfig/getVendorList", route109); + app.use("/api/setting/vendorConfig/modelTest", route110); + app.use("/api/setting/vendorConfig/updateCode", route111); + app.use("/api/setting/vendorConfig/updateVendor", route112); + app.use("/api/task/getProject", route113); + app.use("/api/task/getTaskApi", route114); + app.use("/api/task/getTaskCategories", route115); + app.use("/api/task/taskDetails", route116); + app.use("/api/test/test", route117); } diff --git a/src/routes/project/addVisual.ts b/src/routes/project/addVisual.ts new file mode 100644 index 0000000..958e15c --- /dev/null +++ b/src/routes/project/addVisual.ts @@ -0,0 +1,85 @@ +import express from "express"; +import u from "@/utils"; +import { success } from "@/lib/responseFormat"; +import fs from "fs"; +import path from "path"; +import { validateFields } from "@/middleware/middleware"; +import { z } from "zod"; +const router = express.Router(); + +// 新增视觉手册 +export default router.post( + "/", + validateFields({ + name: z.string(), + image: z.string(), + data: z.array( + z.object({ + label: z.string(), + value: z.string(), + data: z.string(), + }), + ), + }), + async (req, res) => { + try { + const { name, image, data } = req.body as { + name: string; + image: string; + data: { label: string; value: string; data: string }[]; + }; + + const mainPath = u.getPath(["skills", "art_prompts", name]); + + // 将 image 写入 mainPath/images/image 文件(无后缀) + if (image) { + const imagesDir = path.join(mainPath, "images"); + if (!fs.existsSync(imagesDir)) { + fs.mkdirSync(imagesDir, { recursive: true }); + } + fs.writeFileSync(path.join(imagesDir, "image"), image, "utf-8"); + } + + // 字段映射表(与 getVisualManual 保持一致) + const DATA_MAP: { label: string; value: string; subDir?: string }[] = [ + { label: "README", value: "README" }, + { label: "前缀", value: "prefix" }, + { label: "角色", value: "art_character", subDir: "art_prompt" }, + { label: "角色衍生", value: "art_character_derivative", subDir: "art_prompt" }, + { label: "道具", value: "art_prop", subDir: "art_prompt" }, + { label: "道具衍生", value: "art_prop_derivative", subDir: "art_prompt" }, + { label: "场景", value: "art_scene", subDir: "art_prompt" }, + { label: "场景衍生", value: "art_scene_derivative", subDir: "art_prompt" }, + { label: "分镜", value: "art_storyboard", subDir: "art_prompt" }, + { label: "分镜视频", value: "art_storyboard_video", subDir: "art_prompt" }, + { label: "技法-导演规划", value: "director_planning", subDir: "driector_skills" }, + { label: "技法-分镜表设计", value: "director_storyboard_table", subDir: "driector_skills" }, + ]; + + // 根据 DATA_MAP 构建 value -> subDir 的映射 + const SUB_DIR_MAP = new Map(DATA_MAP.map(({ value, subDir }) => [value, subDir ?? ""])); + + // 合法的 value 值集合,用于校验 + const VALID_KEYS = new Set(DATA_MAP.map(({ value }) => value)); + + for (const item of data) { + if (!VALID_KEYS.has(item.value)) continue; + + const subDir = SUB_DIR_MAP.get(item.value)!; + const dirArr = subDir ? [mainPath, subDir] : [mainPath]; + const filePath = u.getPath([...dirArr, `${item.value}.md`]); + + const fileDir = path.dirname(filePath); + // 目录不存在时递归创建 + if (!fs.existsSync(fileDir)) { + fs.mkdirSync(fileDir, { recursive: true }); + } + fs.writeFileSync(filePath, item.data, "utf-8"); + } + + res.status(200).send(success()); + } catch (err) { + res.status(500).send({ error: String(err) }); + } + }, +); diff --git a/src/routes/project/deleteVisualManual.ts b/src/routes/project/deleteVisualManual.ts new file mode 100644 index 0000000..eb9423b --- /dev/null +++ b/src/routes/project/deleteVisualManual.ts @@ -0,0 +1,43 @@ +import express from "express"; +import u from "@/utils"; +import fs from "fs"; +import { z } from "zod"; +import { success } from "@/lib/responseFormat"; +import { validateFields } from "@/middleware/middleware"; +const router = express.Router(); + +// 删除视觉手册 +export default router.post( + "/", + validateFields({ + name: z.string(), + }), + async (req, res) => { + try { + const { name } = req.body as { name: string }; + + // 安全校验:不允许包含路径分隔符、纯数字,防止越级删除或误删项目目录 + if (name.includes("/") || name.includes("\\") || name === "." || name === ".." || /^\d+$/.test(name)) { + res.status(400).send({ error: "非法的名称" }); + return; + } + + const artPromptsDir = u.getPath(["skills", "art_prompts", name]); + + // 1. 删除 skills/art_prompts 下的同名文件夹 + if (fs.existsSync(artPromptsDir)) { + fs.rmSync(artPromptsDir, { recursive: true, force: true }); + // 2. 删除 oss 下的同名文件夹(存放图片) + try { + await u.oss.deleteDirectory(name); + } catch { + // oss 下不存在该目录则忽略 + } + } + + res.status(200).send(success({ message: "删除成功" })); + } catch (err) { + res.status(500).send({ error: String(err) }); + } + }, +); diff --git a/src/routes/project/editVisualManual.ts b/src/routes/project/editVisualManual.ts new file mode 100644 index 0000000..7e90be4 --- /dev/null +++ b/src/routes/project/editVisualManual.ts @@ -0,0 +1,100 @@ +import express from "express"; +import u from "@/utils"; +import { success } from "@/lib/responseFormat"; +import fs from "fs"; +import path from "path"; +import { validateFields } from "@/middleware/middleware"; +import { z } from "zod"; +const router = express.Router(); + +// 编辑视觉手册 +export default router.post( + "/", + validateFields({ + name: z.string(), + images: z.array(z.string()), + data: z.array( + z.object({ + label: z.string(), + value: z.string(), + data: z.string(), + }), + ), + }), + async (req, res) => { + try { + const { name, images, data } = req.body as { + name: string; + images: string[]; + data: { label: string; value: string; data: string }[]; + }; + + if (/^\d+$/.test(name)) { + res.status(400).send({ error: "名称不能为纯数字" }); + return; + } + + const mainPath = u.getPath(["skills", "art_prompts", name]); + + // 字段映射表(与 getVisualManual 保持一致) + const DATA_MAP: { label: string; value: string; subDir?: string }[] = [ + { label: "README", value: "README" }, + { label: "前缀", value: "prefix" }, + { label: "角色", value: "art_character", subDir: "art_prompt" }, + { label: "角色衍生", value: "art_character_derivative", subDir: "art_prompt" }, + { label: "道具", value: "art_prop", subDir: "art_prompt" }, + { label: "道具衍生", value: "art_prop_derivative", subDir: "art_prompt" }, + { label: "场景", value: "art_scene", subDir: "art_prompt" }, + { label: "场景衍生", value: "art_scene_derivative", subDir: "art_prompt" }, + { label: "分镜", value: "art_storyboard", subDir: "art_prompt" }, + { label: "分镜视频", value: "art_storyboard_video", subDir: "art_prompt" }, + { label: "技法-导演规划", value: "director_planning", subDir: "driector_skills" }, + { label: "技法-分镜表设计", value: "director_storyboard_table", subDir: "driector_skills" }, + ]; + + // 根据 DATA_MAP 构建 value -> subDir 的映射 + const SUB_DIR_MAP = new Map(DATA_MAP.map(({ value, subDir }) => [value, subDir ?? ""])); + + // 合法的 value 值集合,用于校验 + const VALID_KEYS = new Set(DATA_MAP.map(({ value }) => value)); + + for (const item of data) { + if (!VALID_KEYS.has(item.value)) continue; + + const subDir = SUB_DIR_MAP.get(item.value)!; + const dirArr = subDir ? [mainPath, subDir] : [mainPath]; + const filePath = u.getPath([...dirArr, `${item.value}.md`]); + + const fileDir = path.dirname(filePath); + // 目录不存在时递归创建 + if (!fs.existsSync(fileDir)) { + fs.mkdirSync(fileDir, { recursive: true }); + } + fs.writeFileSync(filePath, item.data, "utf-8"); + } + const ossImagesDir = u.getPath(["oss", name]); + + let existingFiles: string[] = []; + try { + const allFiles = fs.readdirSync(ossImagesDir); + existingFiles = allFiles.filter((f) => /\.(png|jpe?g|gif|webp|svg)$/i.test(f)); + } catch {} + + const retainedFileNames = new Set(images.filter((item) => item.includes("http")).map((url) => path.basename(new URL(url).pathname))); + + for (const file of existingFiles) { + if (!retainedFileNames.has(file)) { + await u.oss.deleteFile(`${name}/${file}`); + } + } + + for (const item of images) { + if (!item.includes("http")) await u.oss.writeFile(`${name}/${u.uuid()}.jpg`, item); + } + + res.status(200).send(success()); + } catch (err) { + res.status(500).send({ error: String(err) }); + } + }, +); diff --git a/src/routes/project/getVisualManual.ts b/src/routes/project/getVisualManual.ts index 8c7e106..43a29f0 100644 --- a/src/routes/project/getVisualManual.ts +++ b/src/routes/project/getVisualManual.ts @@ -30,19 +30,19 @@ function readMd(filePath: string): string { } } -// 获取 images 文件夹第一张图片的 base64,无图片返回空字符串 -function readFirstImage(imagesDir: string): string { +// 获取 images 文件夹下所有图片文件路径列表 +async function readAllImages(imagesDir: string) { try { - const files = fs.readdirSync(imagesDir); - const imgFile = files.find((f) => /\.(png|jpe?g|gif|webp|svg)$/i.test(f)); - if (!imgFile) return ""; - const imgPath = path.join(imagesDir, imgFile); - const ext = path.extname(imgFile).slice(1).toLowerCase(); - const mimeType = ext === "jpg" ? "jpeg" : ext; - const base64 = fs.readFileSync(imgPath).toString("base64"); - return `data:image/${mimeType};base64,${base64}`; + const ossPath = u.getPath(["oss", imagesDir]); + const files = fs.readdirSync(ossPath); + const images = files.filter((f) => /\.(png|jpe?g|gif|webp|svg)$/i.test(f)).map((f) => path.join(imagesDir, f)); + if (images.length) { + return Promise.all(images.map(async (i) => await u.oss.getFileUrl(i))); + } else { + return []; + } } catch { - return ""; + return []; } } @@ -57,32 +57,34 @@ export default router.post("/", async (req, res) => { .filter((d) => d.isDirectory()) .map((d) => d.name); - const result = styleDirs.map((styleName) => { - const styleDir = path.join(artPromptsDir, styleName); - const imagesDir = path.join(styleDir, "images"); + const result = await Promise.all( + styleDirs.map(async (styleName) => { + const styleDir = path.join(artPromptsDir, styleName); + const imagesDir = path.join(styleDir, "images"); - const image = readFirstImage(imagesDir); + const images = await readAllImages(styleName); + + const data = DATA_MAP.map(({ label, value, subDir }) => { + let mdPath: string; + if (subDir) { + mdPath = path.join(styleDir, subDir, `${value}.md`); + } else { + mdPath = path.join(styleDir, `${value}.md`); + } + return { + label, + value, + data: readMd(mdPath), + }; + }); - const data = DATA_MAP.map(({ label, value, subDir }) => { - let mdPath: string; - if (subDir) { - mdPath = path.join(styleDir, subDir, `${value}.md`); - } else { - mdPath = path.join(styleDir, `${value}.md`); - } return { - label, - value, - data: readMd(mdPath), + name: styleName, + image: images, + data, }; - }); - - return { - name: styleName, - image, - data, - }; - }); + }), + ); res.status(200).send(success(result)); } catch (err) { res.status(500).send({ error: String(err) }); diff --git a/src/utils/oss.ts b/src/utils/oss.ts index ae65c9a..7dbd1b8 100644 --- a/src/utils/oss.ts +++ b/src/utils/oss.ts @@ -144,7 +144,11 @@ class OSS { const absPath = resolveSafeLocalPath(userRelPath, this.rootDir); await fs.mkdir(path.dirname(absPath), { recursive: true }); // 如果 data 是 string,则视为 base64 编码,先解码再写入 - const buffer = typeof data === "string" ? Buffer.from(data, "base64") : data; + // 自动去除可能存在的 Data URL 前缀(如 "data:image/png;base64,") + const buffer = + typeof data === "string" + ? Buffer.from(data.replace(/^data:[^;]+;base64,/, ""), "base64") + : data; await fs.writeFile(absPath, buffer); }