Guanghu Domestic Migration d1e47f4565
Some checks are pending
自动更新代码和重启 / update-and-restart (push) Waiting to run
CI检查 + 自动部署 / check (push) Waiting to run
CI检查 + 自动部署 / deploy (push) Blocked by required conditions
重启聊天服务 / restart (push) Waiting to run
chore: import sanitized domestic snapshot for REPO-002
Source snapshot: ca48d3ddf926d79aa138306164169baf764bb829
2026-07-17 15:54:41 +08:00

145 lines
4.2 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* persona-studio · 开发任务路由
* POST /api/ps/build/start 触发铸渊代理开发工作流
*
* 当用户点击「我要开发」时自动触发铸渊代理Agent
* 使用用户自带的 API Key 进行 5 步自动化开发。
*/
const express = require('express');
const router = express.Router();
const memoryManager = require('../brain/memory-manager');
const agentWorkflow = require('../brain/agent-workflow');
const emailSender = require('../utils/email-sender');
// 邮箱后端正则二次校验
const EMAIL_RE = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;
// 支持 EXP-XXX 和 GUEST 两种 dev_id 格式
function isValidDevId(devId) {
return devId === 'GUEST' || /^EXP-\d{3,}$/.test(devId);
}
// POST /api/ps/build/start
router.post('/start', async (req, res) => {
const { dev_id, email, contact, conversation, api_base, api_key, model } = req.body || {};
if (!dev_id || !isValidDevId(dev_id)) {
return res.status(400).json({
error: true,
code: 'INVALID_ID',
message: '无效的开发编号'
});
}
if (!email) {
return res.status(400).json({
error: true,
code: 'MISSING_EMAIL',
message: '请提供邮箱地址'
});
}
// 后端二次邮箱校验
if (!EMAIL_RE.test(email)) {
return res.status(400).json({
error: true,
code: 'INVALID_EMAIL',
message: '邮箱格式不正确'
});
}
// 防止 header injectionAPI Key 不得包含换行符
if (api_key && /[\r\n]/.test(api_key)) {
return res.status(400).json({
error: true,
code: 'INVALID_API_KEY',
message: 'API Key 格式无效'
});
}
// 联系方式输入清理(去除潜在 HTML/script 注入)
const safeContact = contact ? String(contact).replace(/[<>&"']/g, '').substring(0, 100) : null;
// 先立即响应,后台异步处理
res.json({
error: false,
message: '铸渊代理已启动,完成后将发送到 ' + email,
status: 'agent_started'
});
// 异步执行铸渊代理开发工作流
(async () => {
const broadcastRaw = req.app.locals.broadcastToClient || function () {};
const broadcast = function (data) { broadcastRaw(dev_id, data); };
try {
// 启动 Agent 工作流
broadcast({
type: 'progress',
message: '🌀 铸渊代理已唤醒,正在启动开发工作流...',
status: 'building',
status_text: '代理已启动'
});
const result = await agentWorkflow.runWorkflow({
dev_id: dev_id,
conversation: conversation || [],
api_base: api_base || '',
api_key: api_key || '',
model: model || '',
broadcast: broadcast
});
// 记录项目GUEST 也记录,方便追踪)
try {
memoryManager.addProject(dev_id, {
name: result.projectName || 'untitled',
email: email,
contact: safeContact,
status: 'completed',
created_at: new Date().toISOString(),
files: result.files || []
});
// 自动提取知识(仅注册开发者)
if (dev_id !== 'GUEST') {
memoryManager.autoExtractKnowledge(dev_id, conversation, result.projectName);
}
} catch (memErr) {
console.error('Memory save error (non-fatal):', memErr.message);
}
// 通知预览就绪
broadcast({
type: 'preview_ready',
project: result.projectName,
message: '✅ 预览已就绪'
});
broadcast({
type: 'complete',
message: '🎉 全部完成!邮件正在发送'
});
// 发邮件(异步,不阻塞主流程)
emailSender.sendCompletion({
to: email,
dev_id: dev_id,
projectName: result.projectName,
summary: result.summary,
files: result.files
}).catch(function (emailErr) {
console.error('Email send error (non-fatal):', emailErr.message);
});
} catch (err) {
console.error('Agent workflow error:', err.message);
broadcast({
type: 'error',
message: '铸渊代理遇到问题: ' + err.message
});
}
})();
});
module.exports = router;