diff --git a/data/skills/art_prompts/chinese_sweet_romance/README.md b/data/skills/art_skills/chinese_sweet_romance/README.md similarity index 100% rename from data/skills/art_prompts/chinese_sweet_romance/README.md rename to data/skills/art_skills/chinese_sweet_romance/README.md diff --git a/data/skills/art_prompts/chinese_sweet_romance/art_prompt/art_character.md b/data/skills/art_skills/chinese_sweet_romance/art_prompt/art_character.md similarity index 100% rename from data/skills/art_prompts/chinese_sweet_romance/art_prompt/art_character.md rename to data/skills/art_skills/chinese_sweet_romance/art_prompt/art_character.md diff --git a/data/skills/art_prompts/chinese_sweet_romance/art_prompt/art_character_derivative.md b/data/skills/art_skills/chinese_sweet_romance/art_prompt/art_character_derivative.md similarity index 100% rename from data/skills/art_prompts/chinese_sweet_romance/art_prompt/art_character_derivative.md rename to data/skills/art_skills/chinese_sweet_romance/art_prompt/art_character_derivative.md diff --git a/data/skills/art_prompts/chinese_sweet_romance/art_prompt/art_prop.md b/data/skills/art_skills/chinese_sweet_romance/art_prompt/art_prop.md similarity index 100% rename from data/skills/art_prompts/chinese_sweet_romance/art_prompt/art_prop.md rename to data/skills/art_skills/chinese_sweet_romance/art_prompt/art_prop.md diff --git a/data/skills/art_prompts/chinese_sweet_romance/art_prompt/art_prop_derivative.md b/data/skills/art_skills/chinese_sweet_romance/art_prompt/art_prop_derivative.md similarity index 100% rename from data/skills/art_prompts/chinese_sweet_romance/art_prompt/art_prop_derivative.md rename to data/skills/art_skills/chinese_sweet_romance/art_prompt/art_prop_derivative.md diff --git a/data/skills/art_prompts/chinese_sweet_romance/art_prompt/art_scene.md b/data/skills/art_skills/chinese_sweet_romance/art_prompt/art_scene.md similarity index 100% rename from data/skills/art_prompts/chinese_sweet_romance/art_prompt/art_scene.md rename to data/skills/art_skills/chinese_sweet_romance/art_prompt/art_scene.md diff --git a/data/skills/art_prompts/chinese_sweet_romance/art_prompt/art_scene_derivative.md b/data/skills/art_skills/chinese_sweet_romance/art_prompt/art_scene_derivative.md similarity index 100% rename from data/skills/art_prompts/chinese_sweet_romance/art_prompt/art_scene_derivative.md rename to data/skills/art_skills/chinese_sweet_romance/art_prompt/art_scene_derivative.md diff --git a/data/skills/art_prompts/chinese_sweet_romance/art_prompt/art_storyboard.md b/data/skills/art_skills/chinese_sweet_romance/art_prompt/art_storyboard.md similarity index 100% rename from data/skills/art_prompts/chinese_sweet_romance/art_prompt/art_storyboard.md rename to data/skills/art_skills/chinese_sweet_romance/art_prompt/art_storyboard.md diff --git a/data/skills/art_prompts/chinese_sweet_romance/art_prompt/art_storyboard_video.md b/data/skills/art_skills/chinese_sweet_romance/art_prompt/art_storyboard_video.md similarity index 100% rename from data/skills/art_prompts/chinese_sweet_romance/art_prompt/art_storyboard_video.md rename to data/skills/art_skills/chinese_sweet_romance/art_prompt/art_storyboard_video.md diff --git a/data/skills/art_prompts/chinese_sweet_romance/driector_skills/director_planning.md b/data/skills/art_skills/chinese_sweet_romance/driector_skills/director_planning.md similarity index 100% rename from data/skills/art_prompts/chinese_sweet_romance/driector_skills/director_planning.md rename to data/skills/art_skills/chinese_sweet_romance/driector_skills/director_planning.md diff --git a/data/skills/art_prompts/chinese_sweet_romance/driector_skills/director_storyboard_table.md b/data/skills/art_skills/chinese_sweet_romance/driector_skills/director_storyboard_table.md similarity index 100% rename from data/skills/art_prompts/chinese_sweet_romance/driector_skills/director_storyboard_table.md rename to data/skills/art_skills/chinese_sweet_romance/driector_skills/director_storyboard_table.md diff --git a/data/skills/art_prompts/chinese_sweet_romance/images/2b1cb9c6-54f9-4e19-bf15-b546c80147bc.jpg b/data/skills/art_skills/chinese_sweet_romance/images/2b1cb9c6-54f9-4e19-bf15-b546c80147bc.jpg similarity index 100% rename from data/skills/art_prompts/chinese_sweet_romance/images/2b1cb9c6-54f9-4e19-bf15-b546c80147bc.jpg rename to data/skills/art_skills/chinese_sweet_romance/images/2b1cb9c6-54f9-4e19-bf15-b546c80147bc.jpg diff --git a/data/skills/art_prompts/chinese_sweet_romance/images/b5bd58ee-c304-477f-9c18-b40f814a5901.jpg b/data/skills/art_skills/chinese_sweet_romance/images/b5bd58ee-c304-477f-9c18-b40f814a5901.jpg similarity index 100% rename from data/skills/art_prompts/chinese_sweet_romance/images/b5bd58ee-c304-477f-9c18-b40f814a5901.jpg rename to data/skills/art_skills/chinese_sweet_romance/images/b5bd58ee-c304-477f-9c18-b40f814a5901.jpg diff --git a/data/skills/art_prompts/chinese_sweet_romance/images/e4b20248-54f1-4e63-ab2d-cb72cd1886b8.jpg b/data/skills/art_skills/chinese_sweet_romance/images/e4b20248-54f1-4e63-ab2d-cb72cd1886b8.jpg similarity index 100% rename from data/skills/art_prompts/chinese_sweet_romance/images/e4b20248-54f1-4e63-ab2d-cb72cd1886b8.jpg rename to data/skills/art_skills/chinese_sweet_romance/images/e4b20248-54f1-4e63-ab2d-cb72cd1886b8.jpg diff --git a/data/skills/art_prompts/chinese_sweet_romance/images/f0f89ce4-b197-4bd6-9794-3d14d1c3ab1e.jpg b/data/skills/art_skills/chinese_sweet_romance/images/f0f89ce4-b197-4bd6-9794-3d14d1c3ab1e.jpg similarity index 100% rename from data/skills/art_prompts/chinese_sweet_romance/images/f0f89ce4-b197-4bd6-9794-3d14d1c3ab1e.jpg rename to data/skills/art_skills/chinese_sweet_romance/images/f0f89ce4-b197-4bd6-9794-3d14d1c3ab1e.jpg diff --git a/data/skills/art_prompts/chinese_sweet_romance/prefix.md b/data/skills/art_skills/chinese_sweet_romance/prefix.md similarity index 100% rename from data/skills/art_prompts/chinese_sweet_romance/prefix.md rename to data/skills/art_skills/chinese_sweet_romance/prefix.md diff --git a/data/skills/story_skills/第三方/README.md b/data/skills/story_skills/第三方/README.md new file mode 100644 index 0000000..a840090 --- /dev/null +++ b/data/skills/story_skills/第三方/README.md @@ -0,0 +1,2 @@ +12第三方第三方 +12是多少 \ No newline at end of file diff --git a/data/skills/story_skills/第三方/art_prompt/art_character.md b/data/skills/story_skills/第三方/art_prompt/art_character.md new file mode 100644 index 0000000..3cacc0b --- /dev/null +++ b/data/skills/story_skills/第三方/art_prompt/art_character.md @@ -0,0 +1 @@ +12 \ No newline at end of file diff --git a/data/skills/story_skills/第三方/art_prompt/art_character_derivative.md b/data/skills/story_skills/第三方/art_prompt/art_character_derivative.md new file mode 100644 index 0000000..3cacc0b --- /dev/null +++ b/data/skills/story_skills/第三方/art_prompt/art_character_derivative.md @@ -0,0 +1 @@ +12 \ No newline at end of file diff --git a/data/skills/story_skills/第三方/images/0aaad2b5-926b-46a7-b36e-f0cb47ef5803.jpg b/data/skills/story_skills/第三方/images/0aaad2b5-926b-46a7-b36e-f0cb47ef5803.jpg new file mode 100644 index 0000000..1c355c8 Binary files /dev/null and b/data/skills/story_skills/第三方/images/0aaad2b5-926b-46a7-b36e-f0cb47ef5803.jpg differ diff --git a/data/skills/story_skills/第三方/images/f55a2376-1281-44f9-a486-f5b4a7c2cfdc.jpg b/data/skills/story_skills/第三方/images/f55a2376-1281-44f9-a486-f5b4a7c2cfdc.jpg new file mode 100644 index 0000000..1d0cd85 Binary files /dev/null and b/data/skills/story_skills/第三方/images/f55a2376-1281-44f9-a486-f5b4a7c2cfdc.jpg differ diff --git a/src/lib/initDB.ts b/src/lib/initDB.ts index d1c44d3..939284a 100644 --- a/src/lib/initDB.ts +++ b/src/lib/initDB.ts @@ -37,6 +37,7 @@ export default async (knex: Knex, forceInit: boolean = false): Promise => table.text("intro"); table.text("type"); table.text("artStyle"); + table.text("directorManual"); table.text("mode"); table.text("videoRatio"); table.integer("createTime"); diff --git a/src/router.ts b/src/router.ts index 8469a27..4ce6b21 100644 --- a/src/router.ts +++ b/src/router.ts @@ -1,4 +1,4 @@ -// @routes-hash 6b77c26005a9993d80cda7ab95d26702 +// @routes-hash 0bd9383c5e26c63f175c07ee431ea8a9 import { Express } from "express"; import route1 from "./routes/agents/clearMemory"; @@ -78,59 +78,62 @@ import route74 from "./routes/production/workbench/getGenerateData"; import route75 from "./routes/production/workbench/getVideoList"; import route76 from "./routes/production/workbench/getVideoModelDetail"; import route77 from "./routes/production/workbench/selectVideo"; -import route78 from "./routes/project/addProject"; -import route79 from "./routes/project/addVisual"; +import route78 from "./routes/project/addDirectorManual"; +import route79 from "./routes/project/addProject"; import route80 from "./routes/project/addVisualManual"; -import route81 from "./routes/project/deleteVisualManual"; -import route82 from "./routes/project/delProject"; -import route83 from "./routes/project/editProject"; -import route84 from "./routes/project/editVisualManual"; -import route85 from "./routes/project/getProject"; -import route86 from "./routes/project/getVisualManual"; -import route87 from "./routes/project/visualManual"; -import route88 from "./routes/script/addScript"; -import route89 from "./routes/script/delScript"; -import route90 from "./routes/script/exportScript"; -import route91 from "./routes/script/extractAssets"; -import route92 from "./routes/script/getScrptApi"; -import route93 from "./routes/script/pollScriptAssets"; -import route94 from "./routes/script/updateScript"; -import route95 from "./routes/scriptAgent/getPlanData"; -import route96 from "./routes/scriptAgent/setPlanData"; -import route97 from "./routes/scriptAgent/updateData"; -import route98 from "./routes/setting/about/checkUpdate"; -import route99 from "./routes/setting/about/downloadApp"; -import route100 from "./routes/setting/agentDeploy/agentSetKey"; -import route101 from "./routes/setting/agentDeploy/deployAgentModel"; -import route102 from "./routes/setting/agentDeploy/getAgentDeploy"; -import route103 from "./routes/setting/dbConfig/clearData"; -import route104 from "./routes/setting/dev/getSwitchAiDevTool"; -import route105 from "./routes/setting/dev/updateSwitchAiDevTool"; -import route106 from "./routes/setting/fileManagement/openFolder"; -import route107 from "./routes/setting/getTextModel"; -import route108 from "./routes/setting/loginConfig/getUser"; -import route109 from "./routes/setting/loginConfig/updateUserPwd"; -import route110 from "./routes/setting/memoryConfig/delAllMemory"; -import route111 from "./routes/setting/memoryConfig/getMemory"; -import route112 from "./routes/setting/memoryConfig/sureMemory"; -import route113 from "./routes/setting/promptManage/getPrompt"; -import route114 from "./routes/setting/promptManage/updatePrompt"; -import route115 from "./routes/setting/skillManagement/getSkillContent"; -import route116 from "./routes/setting/skillManagement/getSkillList"; -import route117 from "./routes/setting/skillManagement/saveSkillContent"; -import route118 from "./routes/setting/vendorConfig/addVendor"; -import route119 from "./routes/setting/vendorConfig/deleteVendor"; -import route120 from "./routes/setting/vendorConfig/enableEnglishVendor"; -import route121 from "./routes/setting/vendorConfig/getCodeByLink"; -import route122 from "./routes/setting/vendorConfig/getVendorList"; -import route123 from "./routes/setting/vendorConfig/modelTest"; -import route124 from "./routes/setting/vendorConfig/updateCode"; -import route125 from "./routes/setting/vendorConfig/updateVendor"; -import route126 from "./routes/task/getProject"; -import route127 from "./routes/task/getTaskApi"; -import route128 from "./routes/task/getTaskCategories"; -import route129 from "./routes/task/taskDetails"; -import route130 from "./routes/test/test"; +import route81 from "./routes/project/deleteDirectorManual"; +import route82 from "./routes/project/deleteVisualManual"; +import route83 from "./routes/project/delProject"; +import route84 from "./routes/project/editDirectorlManual"; +import route85 from "./routes/project/editProject"; +import route86 from "./routes/project/editVisualManual"; +import route87 from "./routes/project/getProject"; +import route88 from "./routes/project/getVisualManual"; +import route89 from "./routes/project/queryDirectorManual"; +import route90 from "./routes/project/visualManual"; +import route91 from "./routes/script/addScript"; +import route92 from "./routes/script/delScript"; +import route93 from "./routes/script/exportScript"; +import route94 from "./routes/script/extractAssets"; +import route95 from "./routes/script/getScrptApi"; +import route96 from "./routes/script/pollScriptAssets"; +import route97 from "./routes/script/updateScript"; +import route98 from "./routes/scriptAgent/getPlanData"; +import route99 from "./routes/scriptAgent/setPlanData"; +import route100 from "./routes/scriptAgent/updateData"; +import route101 from "./routes/setting/about/checkUpdate"; +import route102 from "./routes/setting/about/downloadApp"; +import route103 from "./routes/setting/agentDeploy/agentSetKey"; +import route104 from "./routes/setting/agentDeploy/deployAgentModel"; +import route105 from "./routes/setting/agentDeploy/getAgentDeploy"; +import route106 from "./routes/setting/dbConfig/clearData"; +import route107 from "./routes/setting/dev/getSwitchAiDevTool"; +import route108 from "./routes/setting/dev/updateSwitchAiDevTool"; +import route109 from "./routes/setting/fileManagement/openFolder"; +import route110 from "./routes/setting/getTextModel"; +import route111 from "./routes/setting/loginConfig/getUser"; +import route112 from "./routes/setting/loginConfig/updateUserPwd"; +import route113 from "./routes/setting/memoryConfig/delAllMemory"; +import route114 from "./routes/setting/memoryConfig/getMemory"; +import route115 from "./routes/setting/memoryConfig/sureMemory"; +import route116 from "./routes/setting/promptManage/getPrompt"; +import route117 from "./routes/setting/promptManage/updatePrompt"; +import route118 from "./routes/setting/skillManagement/getSkillContent"; +import route119 from "./routes/setting/skillManagement/getSkillList"; +import route120 from "./routes/setting/skillManagement/saveSkillContent"; +import route121 from "./routes/setting/vendorConfig/addVendor"; +import route122 from "./routes/setting/vendorConfig/deleteVendor"; +import route123 from "./routes/setting/vendorConfig/enableEnglishVendor"; +import route124 from "./routes/setting/vendorConfig/getCodeByLink"; +import route125 from "./routes/setting/vendorConfig/getVendorList"; +import route126 from "./routes/setting/vendorConfig/modelTest"; +import route127 from "./routes/setting/vendorConfig/updateCode"; +import route128 from "./routes/setting/vendorConfig/updateVendor"; +import route129 from "./routes/task/getProject"; +import route130 from "./routes/task/getTaskApi"; +import route131 from "./routes/task/getTaskCategories"; +import route132 from "./routes/task/taskDetails"; +import route133 from "./routes/test/test"; export default async (app: Express) => { app.use("/api/agents/clearMemory", route1); @@ -210,57 +213,60 @@ export default async (app: Express) => { app.use("/api/production/workbench/getVideoList", route75); app.use("/api/production/workbench/getVideoModelDetail", route76); app.use("/api/production/workbench/selectVideo", route77); - app.use("/api/project/addProject", route78); - app.use("/api/project/addVisual", route79); + app.use("/api/project/addDirectorManual", route78); + app.use("/api/project/addProject", route79); app.use("/api/project/addVisualManual", route80); - app.use("/api/project/deleteVisualManual", route81); - app.use("/api/project/delProject", route82); - app.use("/api/project/editProject", route83); - app.use("/api/project/editVisualManual", route84); - app.use("/api/project/getProject", route85); - app.use("/api/project/getVisualManual", route86); - app.use("/api/project/visualManual", route87); - app.use("/api/script/addScript", route88); - app.use("/api/script/delScript", route89); - app.use("/api/script/exportScript", route90); - app.use("/api/script/extractAssets", route91); - app.use("/api/script/getScrptApi", route92); - app.use("/api/script/pollScriptAssets", route93); - app.use("/api/script/updateScript", route94); - app.use("/api/scriptAgent/getPlanData", route95); - app.use("/api/scriptAgent/setPlanData", route96); - app.use("/api/scriptAgent/updateData", route97); - app.use("/api/setting/about/checkUpdate", route98); - app.use("/api/setting/about/downloadApp", route99); - app.use("/api/setting/agentDeploy/agentSetKey", route100); - app.use("/api/setting/agentDeploy/deployAgentModel", route101); - app.use("/api/setting/agentDeploy/getAgentDeploy", route102); - app.use("/api/setting/dbConfig/clearData", route103); - app.use("/api/setting/dev/getSwitchAiDevTool", route104); - app.use("/api/setting/dev/updateSwitchAiDevTool", route105); - app.use("/api/setting/fileManagement/openFolder", route106); - app.use("/api/setting/getTextModel", route107); - app.use("/api/setting/loginConfig/getUser", route108); - app.use("/api/setting/loginConfig/updateUserPwd", route109); - app.use("/api/setting/memoryConfig/delAllMemory", route110); - app.use("/api/setting/memoryConfig/getMemory", route111); - app.use("/api/setting/memoryConfig/sureMemory", route112); - app.use("/api/setting/promptManage/getPrompt", route113); - app.use("/api/setting/promptManage/updatePrompt", route114); - app.use("/api/setting/skillManagement/getSkillContent", route115); - app.use("/api/setting/skillManagement/getSkillList", route116); - app.use("/api/setting/skillManagement/saveSkillContent", route117); - app.use("/api/setting/vendorConfig/addVendor", route118); - app.use("/api/setting/vendorConfig/deleteVendor", route119); - app.use("/api/setting/vendorConfig/enableEnglishVendor", route120); - app.use("/api/setting/vendorConfig/getCodeByLink", route121); - app.use("/api/setting/vendorConfig/getVendorList", route122); - app.use("/api/setting/vendorConfig/modelTest", route123); - app.use("/api/setting/vendorConfig/updateCode", route124); - app.use("/api/setting/vendorConfig/updateVendor", route125); - app.use("/api/task/getProject", route126); - app.use("/api/task/getTaskApi", route127); - app.use("/api/task/getTaskCategories", route128); - app.use("/api/task/taskDetails", route129); - app.use("/api/test/test", route130); + app.use("/api/project/deleteDirectorManual", route81); + app.use("/api/project/deleteVisualManual", route82); + app.use("/api/project/delProject", route83); + app.use("/api/project/editDirectorlManual", route84); + app.use("/api/project/editProject", route85); + app.use("/api/project/editVisualManual", route86); + app.use("/api/project/getProject", route87); + app.use("/api/project/getVisualManual", route88); + app.use("/api/project/queryDirectorManual", route89); + app.use("/api/project/visualManual", route90); + app.use("/api/script/addScript", route91); + app.use("/api/script/delScript", route92); + app.use("/api/script/exportScript", route93); + app.use("/api/script/extractAssets", route94); + app.use("/api/script/getScrptApi", route95); + app.use("/api/script/pollScriptAssets", route96); + app.use("/api/script/updateScript", route97); + app.use("/api/scriptAgent/getPlanData", route98); + app.use("/api/scriptAgent/setPlanData", route99); + app.use("/api/scriptAgent/updateData", route100); + app.use("/api/setting/about/checkUpdate", route101); + app.use("/api/setting/about/downloadApp", route102); + app.use("/api/setting/agentDeploy/agentSetKey", route103); + app.use("/api/setting/agentDeploy/deployAgentModel", route104); + app.use("/api/setting/agentDeploy/getAgentDeploy", route105); + app.use("/api/setting/dbConfig/clearData", route106); + app.use("/api/setting/dev/getSwitchAiDevTool", route107); + app.use("/api/setting/dev/updateSwitchAiDevTool", route108); + app.use("/api/setting/fileManagement/openFolder", route109); + app.use("/api/setting/getTextModel", route110); + app.use("/api/setting/loginConfig/getUser", route111); + app.use("/api/setting/loginConfig/updateUserPwd", route112); + app.use("/api/setting/memoryConfig/delAllMemory", route113); + app.use("/api/setting/memoryConfig/getMemory", route114); + app.use("/api/setting/memoryConfig/sureMemory", route115); + app.use("/api/setting/promptManage/getPrompt", route116); + app.use("/api/setting/promptManage/updatePrompt", route117); + app.use("/api/setting/skillManagement/getSkillContent", route118); + app.use("/api/setting/skillManagement/getSkillList", route119); + app.use("/api/setting/skillManagement/saveSkillContent", route120); + app.use("/api/setting/vendorConfig/addVendor", route121); + app.use("/api/setting/vendorConfig/deleteVendor", route122); + app.use("/api/setting/vendorConfig/enableEnglishVendor", route123); + app.use("/api/setting/vendorConfig/getCodeByLink", route124); + app.use("/api/setting/vendorConfig/getVendorList", route125); + app.use("/api/setting/vendorConfig/modelTest", route126); + app.use("/api/setting/vendorConfig/updateCode", route127); + app.use("/api/setting/vendorConfig/updateVendor", route128); + app.use("/api/task/getProject", route129); + app.use("/api/task/getTaskApi", route130); + app.use("/api/task/getTaskCategories", route131); + app.use("/api/task/taskDetails", route132); + app.use("/api/test/test", route133); } diff --git a/src/routes/project/addDirectorManual.ts b/src/routes/project/addDirectorManual.ts new file mode 100644 index 0000000..6d50ebd --- /dev/null +++ b/src/routes/project/addDirectorManual.ts @@ -0,0 +1,104 @@ +import express from "express"; +import u from "@/utils"; +import { error, 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()), + directorManual: z.string(), + data: z.array( + z.object({ + label: z.string(), + value: z.string(), + data: z.string(), + }), + ), + }), + async (req, res) => { + try { + const { name, images, data, directorManual } = req.body as { + name: string; + images: string[]; + data: { label: string; value: string; data: string }[]; + directorManual: string; + }; + + if (/^\d+$/.test(directorManual)) { + res.status(400).send(error("文件名称不能为纯数字")); + return; + } + + const mainPath = u.getPath(["skills", "story_skills", directorManual]); + if (fs.existsSync(mainPath)) { + return res.status(400).send(error("请勿填写重复名称的视觉手册")); + } + // 字段映射表(与 getVisualManual 保持一致) + const DATA_MAP: { value: string; subDir?: string }[] = [ + { value: "README" }, + { value: "art_character", subDir: "art_prompt" }, + { value: "art_character_derivative", subDir: "art_prompt" }, + ]; + // 根据 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 imagesDir = path.join(mainPath, "images"); + + let existingFiles: string[] = []; + try { + const allFiles = fs.readdirSync(imagesDir); + existingFiles = allFiles.filter((f) => /\.(png|jpe?g|gif|webp|svg)$/i.test(f)); + } catch {} + + const retainedFileNames = new Set(images.filter((item) => item.startsWith("http")).map((url) => path.basename(new URL(url).pathname))); + + for (const file of existingFiles) { + if (!retainedFileNames.has(file)) { + const filePath = path.join(imagesDir, file); + if (fs.existsSync(filePath)) fs.unlinkSync(filePath); + } + } + + if (!fs.existsSync(imagesDir)) { + fs.mkdirSync(imagesDir, { recursive: true }); + } + + for (const item of images) { + if (!item.startsWith("http")) { + const fileName = `${u.uuid()}.jpg`; + const targetPath = path.join(imagesDir, fileName); + const buffer = Buffer.from(item.replace(/^data:[^;]+;base64,/, ""), "base64"); + fs.writeFileSync(targetPath, buffer); + } + } + + res.status(200).send(success()); + } catch (err) { + res.status(500).send({ error: String(err) }); + } + }, +); diff --git a/src/routes/project/addVisual.ts b/src/routes/project/addVisual.ts deleted file mode 100644 index 958e15c..0000000 --- a/src/routes/project/addVisual.ts +++ /dev/null @@ -1,85 +0,0 @@ -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/addVisualManual.ts b/src/routes/project/addVisualManual.ts index e8550fd..ead3b97 100644 --- a/src/routes/project/addVisualManual.ts +++ b/src/routes/project/addVisualManual.ts @@ -7,7 +7,7 @@ import { validateFields } from "@/middleware/middleware"; import { z } from "zod"; const router = express.Router(); -// 编辑视觉手册 +// 新增视觉手册 export default router.post( "/", validateFields({ @@ -36,7 +36,7 @@ export default router.post( return; } - const mainPath = u.getPath(["skills", "art_prompts", stylePath]); + const mainPath = u.getPath(["skills", "art_skills", stylePath]); if (fs.existsSync(mainPath)) { return res.status(400).send(error("请勿填写重复名称的视觉手册")); } diff --git a/src/routes/project/deleteDirectorManual.ts b/src/routes/project/deleteDirectorManual.ts new file mode 100644 index 0000000..3fb77b1 --- /dev/null +++ b/src/routes/project/deleteDirectorManual.ts @@ -0,0 +1,41 @@ +import express from "express"; +import u from "@/utils"; +import fs from "node:fs/promises"; +import { z } from "zod"; +import { error, 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", "story_skills", name]); + + try { + const stat = await fs.stat(artPromptsDir); + if (!stat.isDirectory()) { + throw new Error(`${artPromptsDir} 不是文件夹`); + } + await fs.rm(artPromptsDir, { recursive: true, force: true }); + } catch (e) { + console.error("[删除视觉手册] 删除失败:", artPromptsDir, e); + } + res.status(200).send(success({ message: "删除成功" })); + } catch (err) { + res.status(500).send(error(u.error(err).message || "删除失败")); + } + }, +); diff --git a/src/routes/project/deleteVisualManual.ts b/src/routes/project/deleteVisualManual.ts index b0a1244..430c29d 100644 --- a/src/routes/project/deleteVisualManual.ts +++ b/src/routes/project/deleteVisualManual.ts @@ -22,7 +22,7 @@ export default router.post( return; } - const artPromptsDir = u.getPath(["skills", "art_prompts", name]); + const artPromptsDir = u.getPath(["skills", "art_skills", name]); try { const stat = await fs.stat(artPromptsDir); diff --git a/src/routes/project/editDirectorlManual.ts b/src/routes/project/editDirectorlManual.ts new file mode 100644 index 0000000..27cafa4 --- /dev/null +++ b/src/routes/project/editDirectorlManual.ts @@ -0,0 +1,105 @@ +import express from "express"; +import u from "@/utils"; +import { error, 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(), + directorManual: 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, directorManual, images, data } = req.body as { + name: string; + directorManual: string; + images: string[]; + data: { label: string; value: string; data: string }[]; + }; + + if (/^\d+$/.test(directorManual)) { + res.status(400).send(error("名称不能为纯数字")); + return; + } + + const mainPath = u.getPath(["skills", "story_skills", directorManual]); + if (!fs.existsSync(mainPath)) { + return res.status(400).send(error("导演手册不存在")); + } + // 字段映射表(与 getVisualManual 保持一致) + const DATA_MAP: { value: string; subDir?: string }[] = [ + { value: "README" }, + { value: "art_character", subDir: "art_prompt" }, + { value: "art_character_derivative", subDir: "art_prompt" }, + ]; + // 根据 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 }); + } + const content = item.value === "README" ? `${name}\n${item.data}` : item.data; + fs.writeFileSync(filePath, content, "utf-8"); + } + const imagesDir = path.join(mainPath, "images"); + + let existingFiles: string[] = []; + try { + const allFiles = fs.readdirSync(imagesDir); + existingFiles = allFiles.filter((f) => /\.(png|jpe?g|gif|webp|svg)$/i.test(f)); + } catch {} + + const retainedFileNames = new Set(images.filter((item) => item.startsWith("http")).map((url) => path.basename(new URL(url).pathname))); + + for (const file of existingFiles) { + if (!retainedFileNames.has(file)) { + const filePath = path.join(imagesDir, file); + if (fs.existsSync(filePath)) fs.unlinkSync(filePath); + } + } + + if (!fs.existsSync(imagesDir)) { + fs.mkdirSync(imagesDir, { recursive: true }); + } + + for (const item of images) { + if (!item.startsWith("http")) { + const fileName = `${u.uuid()}.jpg`; + const targetPath = path.join(imagesDir, fileName); + const buffer = Buffer.from(item.replace(/^data:[^;]+;base64,/, ""), "base64"); + fs.writeFileSync(targetPath, buffer); + } + } + + res.status(200).send(success()); + } catch (err) { + res.status(500).send({ error: String(err) }); + } + }, +); diff --git a/src/routes/project/editVisualManual.ts b/src/routes/project/editVisualManual.ts index 69997b0..589599f 100644 --- a/src/routes/project/editVisualManual.ts +++ b/src/routes/project/editVisualManual.ts @@ -36,7 +36,7 @@ export default router.post( return; } - const mainPath = u.getPath(["skills", "art_prompts", stylePath]); + const mainPath = u.getPath(["skills", "art_skills", stylePath]); if (!fs.existsSync(mainPath)) { return res.status(400).send(error("视觉手册不存在")); } diff --git a/src/routes/project/getVisualManual.ts b/src/routes/project/getVisualManual.ts index b997b96..9d13410 100644 --- a/src/routes/project/getVisualManual.ts +++ b/src/routes/project/getVisualManual.ts @@ -33,9 +33,9 @@ function readMd(filePath: string): string { // 获取 images 文件夹下所有图片文件路径列表 async function readAllImages(imagesDir: string) { try { - const ossPath = u.getPath(path.join("skills", "art_prompts", imagesDir, "images")); + const ossPath = u.getPath(path.join("skills", "art_skills", imagesDir, "images")); const files = fs.readdirSync(ossPath); - const images = files.filter((f) => /\.(png|jpe?g|gif|webp|svg)$/i.test(f)).map((f) => path.join("art_prompts", imagesDir, "images", f)); + const images = files.filter((f) => /\.(png|jpe?g|gif|webp|svg)$/i.test(f)).map((f) => path.join("art_skills", imagesDir, "images", f)); if (images.length) { return Promise.all(images.map(async (i) => await u.oss.getFileUrl(i, "skills"))); } else { @@ -49,7 +49,7 @@ async function readAllImages(imagesDir: string) { // 获取视觉手册 export default router.post("/", async (req, res) => { try { - const artPromptsDir = u.getPath(["skills", "art_prompts"]); + const artPromptsDir = u.getPath(["skills", "art_skills"]); // 读取所有风格文件夹 const styleDirs = fs diff --git a/src/routes/project/queryDirectorManual.ts b/src/routes/project/queryDirectorManual.ts new file mode 100644 index 0000000..2e3f481 --- /dev/null +++ b/src/routes/project/queryDirectorManual.ts @@ -0,0 +1,84 @@ +import express from "express"; +import u from "@/utils"; +import { success } from "@/lib/responseFormat"; +import fs from "fs"; +import path from "path"; +const router = express.Router(); + +// 字段映射表 +const DATA_MAP: { label: string; value: string; subDir?: string }[] = [ + { label: "README", value: "README" }, + { label: "前缀", value: "art_character", subDir: "art_prompt" }, + { label: "角色", value: "art_character_derivative", subDir: "art_prompt" }, +]; + +// 读取 md 文件内容,文件不存在时返回空字符串 +function readMd(filePath: string): string { + try { + return fs.readFileSync(filePath, "utf-8"); + } catch { + return ""; + } +} + +// 获取 images 文件夹下所有图片文件路径列表 +async function readAllImages(imagesDir: string) { + try { + const ossPath = u.getPath(path.join("skills", "story_skills", imagesDir, "images")); + const files = fs.readdirSync(ossPath); + const images = files.filter((f) => /\.(png|jpe?g|gif|webp|svg)$/i.test(f)).map((f) => path.join("story_skills", imagesDir, "images", f)); + if (images.length) { + return Promise.all(images.map(async (i) => await u.oss.getFileUrl(i, "skills"))); + } else { + return []; + } + } catch { + return []; + } +} + +// 获取导演手册 +export default router.post("/", async (req, res) => { + try { + const artPromptsDir = u.getPath(["skills", "story_skills"]); + + // 读取所有风格文件夹 + const styleDirs = fs + .readdirSync(artPromptsDir, { withFileTypes: true }) + .filter((d) => d.isDirectory()) + .map((d) => d.name); + + const result = await Promise.all( + styleDirs.map(async (directorManual) => { + const styleDir = path.join(artPromptsDir, directorManual); + const images = await readAllImages(directorManual); + const readmePath = path.join(styleDir, "README.md"); + const readmeContent = fs.readFileSync(readmePath, "utf-8"); + const firstLine = readmeContent.split("\n")[0].replace(/--/g, ""); + 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), + }; + }); + + return { + name: firstLine, + image: images, + directorManual: directorManual, + data, + }; + }), + ); + res.status(200).send(success(result)); + } catch (err) { + res.status(500).send({ error: String(err) }); + } +}); diff --git a/src/types/database.d.ts b/src/types/database.d.ts index a8c2754..62a13d9 100644 --- a/src/types/database.d.ts +++ b/src/types/database.d.ts @@ -1,6 +1,21 @@ -// @db-hash 7af86e2bafe5cab7d175eb68cf76ed7a +// @db-hash 35cf00f711e9d4df398703de70511684 //该文件由脚本自动生成,请勿手动修改 +export interface _o_project_old_20260402 { + 'artStyle'?: string | null; + 'createTime'?: number | null; + 'id'?: number | null; + 'imageModel'?: string | null; + 'imageQuality'?: string | null; + 'intro'?: string | null; + 'mode'?: string | null; + 'name'?: string | null; + 'projectType'?: string | null; + 'type'?: string | null; + 'userId'?: number | null; + 'videoModel'?: string | null; + 'videoRatio'?: string | null; +} export interface _o_storyboard_old_20260402 { 'createTime'?: number | null; 'duration'?: string | null; @@ -165,6 +180,7 @@ export interface o_outlineNovel { export interface o_project { 'artStyle'?: string | null; 'createTime'?: number | null; + 'directorManual'?: string | null; 'id'?: number | null; 'imageModel'?: string | null; 'imageQuality'?: string | null; @@ -285,6 +301,7 @@ export interface o_videoTrack { } export interface DB { + "_o_project_old_20260402": _o_project_old_20260402; "_o_storyboard_old_20260402": _o_storyboard_old_20260402; "_o_storyboard_old_20260402_1": _o_storyboard_old_20260402_1; "_o_vendorConfig_old_20260401": _o_vendorConfig_old_20260401;