const asyncHandler = require('express-async-handler'); const PromptGroup = require('../models/PromptGroup'); const logger = require('../utils/logger'); // @desc 获取所有提示词组 // @route GET /api/prompt-groups // @access Private const getPromptGroups = asyncHandler(async (req, res) => { const promptGroups = await PromptGroup.find({}) .populate('category', 'name color') .sort({ updatedAt: -1 }); res.json(promptGroups); }); // @desc 获取单个提示词组 // @route GET /api/prompt-groups/:id // @access Private const getPromptGroupById = asyncHandler(async (req, res) => { const promptGroup = await PromptGroup.findById(req.params.id) .populate('category', 'name color'); if (promptGroup) { res.json(promptGroup); } else { res.status(404); throw new Error('提示词组未找到'); } }); // @desc 创建提示词组 // @route POST /api/prompt-groups // @access Private const createPromptGroup = asyncHandler(async (req, res) => { const { name, description, category } = req.body; const promptGroup = await PromptGroup.create({ name, description, category, prompts: [], workflows: [], dslFiles: [], createdBy: req.user._id, }); if (promptGroup) { res.status(201).json(promptGroup); } else { res.status(400); throw new Error('无效的提示词组数据'); } }); // @desc 更新提示词组 // @route PUT /api/prompt-groups/:id // @access Private const updatePromptGroup = asyncHandler(async (req, res) => { const { name, description, category } = req.body; const promptGroup = await PromptGroup.findById(req.params.id); if (promptGroup) { promptGroup.name = name || promptGroup.name; promptGroup.description = description || promptGroup.description; promptGroup.category = category || promptGroup.category; const updatedPromptGroup = await promptGroup.save(); res.json(updatedPromptGroup); } else { res.status(404); throw new Error('提示词组未找到'); } }); // @desc 删除提示词组 // @route DELETE /api/prompt-groups/:id // @access Private const deletePromptGroup = asyncHandler(async (req, res) => { const promptGroup = await PromptGroup.findById(req.params.id); if (promptGroup) { await promptGroup.deleteOne(); res.json({ message: '提示词组已删除' }); } else { res.status(404); throw new Error('提示词组未找到'); } }); // @desc 添加提示词到提示词组 // @route POST /api/prompt-groups/:id/prompts // @access Private const addPromptToGroup = asyncHandler(async (req, res) => { const { title, content, tags } = req.body; const promptGroup = await PromptGroup.findById(req.params.id); if (promptGroup) { const newPrompt = { title, content, tags: tags || [], createdAt: new Date(), updatedAt: new Date(), }; promptGroup.prompts.push(newPrompt); promptGroup.updatedAt = new Date(); const updatedPromptGroup = await promptGroup.save(); res.status(201).json(updatedPromptGroup.prompts[updatedPromptGroup.prompts.length - 1]); } else { res.status(404); throw new Error('提示词组未找到'); } }); // @desc 更新提示词 // @route PUT /api/prompt-groups/:id/prompts/:promptId // @access Private const updatePrompt = asyncHandler(async (req, res) => { const { title, content, tags } = req.body; const promptGroup = await PromptGroup.findById(req.params.id); if (promptGroup) { const promptIndex = promptGroup.prompts.findIndex( (p) => p._id.toString() === req.params.promptId ); if (promptIndex !== -1) { promptGroup.prompts[promptIndex].title = title || promptGroup.prompts[promptIndex].title; promptGroup.prompts[promptIndex].content = content || promptGroup.prompts[promptIndex].content; promptGroup.prompts[promptIndex].tags = tags || promptGroup.prompts[promptIndex].tags; promptGroup.prompts[promptIndex].updatedAt = new Date(); promptGroup.updatedAt = new Date(); const updatedPromptGroup = await promptGroup.save(); res.json(updatedPromptGroup.prompts[promptIndex]); } else { res.status(404); throw new Error('提示词未找到'); } } else { res.status(404); throw new Error('提示词组未找到'); } }); // @desc 删除提示词 // @route DELETE /api/prompt-groups/:id/prompts/:promptId // @access Private const deletePrompt = asyncHandler(async (req, res) => { const promptGroup = await PromptGroup.findById(req.params.id); if (promptGroup) { const promptExists = promptGroup.prompts.some( (p) => p._id.toString() === req.params.promptId ); if (promptExists) { promptGroup.prompts = promptGroup.prompts.filter( (p) => p._id.toString() !== req.params.promptId ); promptGroup.updatedAt = new Date(); await promptGroup.save(); res.json({ message: '提示词已删除' }); } else { res.status(404); throw new Error('提示词未找到'); } } else { res.status(404); throw new Error('提示词组未找到'); } }); // @desc 添加DSL文件到提示词组 - 修改为支持文本内容 // @route POST /api/prompt-groups/:id/dsl-files // @access Private const addDslFileToGroup = asyncHandler(async (req, res) => { try { const { name, content, fileData, mimeType } = req.body; logger.info(`正在添加DSL文件: ${name}`); // 验证请求数据 if (!name) { res.status(400); throw new Error('文件名是必需的'); } // 确保至少提供了文本内容或文件数据中的一个 if (!content && !fileData) { res.status(400); throw new Error('请提供YAML内容或上传文件'); } const promptGroup = await PromptGroup.findById(req.params.id); if (!promptGroup) { res.status(404); throw new Error('提示词组未找到'); } // 确保文件名以 .yml 结尾 let fileName = name; if (!fileName.toLowerCase().endsWith('.yml')) { fileName = fileName.replace(/\.[^/.]+$/, '') + '.yml'; } // 处理内容: 优先使用提供的内容,如果没有则尝试解码文件数据 let yamlContent = content; // 如果提供了fileData但没有提供content,尝试将fileData转换为文本 if (!yamlContent && fileData) { try { // 尝试将Base64解码为文本 const buffer = Buffer.from(fileData, 'base64'); yamlContent = buffer.toString('utf-8'); logger.info(`成功从文件数据提取YAML内容`); } catch (error) { logger.error(`从文件数据提取内容失败: ${error.message}`); res.status(400); throw new Error('无法从上传的文件中提取内容'); } } const newDslFile = { name: fileName, content: yamlContent, mimeType: mimeType || 'application/x-yaml', uploadedAt: new Date(), }; promptGroup.dslFiles.push(newDslFile); promptGroup.updatedAt = new Date(); const updatedPromptGroup = await promptGroup.save(); logger.info(`DSL文件添加成功: ${fileName}`); res.status(201).json(updatedPromptGroup.dslFiles[updatedPromptGroup.dslFiles.length - 1]); } catch (error) { logger.error(`添加DSL文件时出错: ${error.message}`); if (!res.statusCode || res.statusCode === 200) { res.status(500); } throw error; } }); // @desc 更新DSL文件 // @route PUT /api/prompt-groups/:id/dsl-files/:fileId // @access Private const updateDslFile = asyncHandler(async (req, res) => { try { const { name, content } = req.body; if (!name && !content) { res.status(400); throw new Error('请提供要更新的文件名或内容'); } const promptGroup = await PromptGroup.findById(req.params.id); if (!promptGroup) { res.status(404); throw new Error('提示词组未找到'); } const fileIndex = promptGroup.dslFiles.findIndex( file => file._id.toString() === req.params.fileId ); if (fileIndex === -1) { res.status(404); throw new Error('DSL文件未找到'); } // 更新文件名 - 确保以.yml结尾 if (name) { let updatedName = name; if (!updatedName.toLowerCase().endsWith('.yml')) { updatedName = updatedName.replace(/\.[^/.]+$/, '') + '.yml'; } promptGroup.dslFiles[fileIndex].name = updatedName; } // 更新内容 if (content) { promptGroup.dslFiles[fileIndex].content = content; } // 更新时间戳 promptGroup.dslFiles[fileIndex].uploadedAt = new Date(); promptGroup.updatedAt = new Date(); const updatedPromptGroup = await promptGroup.save(); logger.info(`DSL文件更新成功: ${promptGroup.dslFiles[fileIndex].name}`); res.status(200).json(updatedPromptGroup.dslFiles[fileIndex]); } catch (error) { logger.error(`更新DSL文件时出错: ${error.message}`); if (!res.statusCode || res.statusCode === 200) { res.status(500); } throw error; } }); // @desc 删除DSL文件 // @route DELETE /api/prompt-groups/:id/dsl-files/:fileId // @access Private const deleteDslFile = asyncHandler(async (req, res) => { const promptGroup = await PromptGroup.findById(req.params.id); if (promptGroup) { const fileExists = promptGroup.dslFiles.some( (f) => f._id.toString() === req.params.fileId ); if (fileExists) { promptGroup.dslFiles = promptGroup.dslFiles.filter( (f) => f._id.toString() !== req.params.fileId ); promptGroup.updatedAt = new Date(); await promptGroup.save(); res.json({ message: 'DSL文件已删除' }); } else { res.status(404); throw new Error('DSL文件未找到'); } } else { res.status(404); throw new Error('提示词组未找到'); } }); module.exports = { getPromptGroups, getPromptGroupById, createPromptGroup, updatePromptGroup, deletePromptGroup, addPromptToGroup, updatePrompt, deletePrompt, addDslFileToGroup, updateDslFile, deleteDslFile, };