fix: 修复 Persona Studio 「我要开发」任务提交失败

根因修复:
1. Nginx CORS 头缺失(跨域预检失败导致 fetch 抛出异常)
2. WebSocket /ws/preview 路由被 /ws 匹配到端口 8080(应路由到 3002)
3. 前端开发端口不匹配 (3721 → 3002)

增强:
- 添加重试机制(最多2次重试)
- 提供详细错误诊断信息
- 更新 nginx-api-proxy.conf 参考配置
- 修复 smoke test 默认端口

Co-authored-by: qinfendebingshuo <207279273+qinfendebingshuo@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot] 2026-03-13 07:09:19 +00:00
parent e13c5cd1e1
commit 2062878c9f
8 changed files with 111 additions and 31 deletions

View File

@ -67,6 +67,12 @@
}
},
"events": [
{
"date": "2026-03-13",
"type": "bug_fix",
"description": "修复「我要开发」任务提交失败 · 3个根因① Nginx缺少CORS头跨域预检失败② WebSocket路由冲突/ws/preview误路由到8080端口③ 前端开发端口不匹配3721→3002· 增加重试机制和详细错误诊断 · 冰朔人格体已启动",
"by": "铸渊(冰朔指令)"
},
{
"date": "2026-03-13",
"type": "brain_restore",

View File

@ -2,6 +2,12 @@
"persona_id": "ICE-GL-ZY001",
"persona_name": "铸渊",
"recent_events": [
{
"date": "2026-03-13",
"type": "bug_fix",
"description": "修复「我要开发」任务提交失败 · Nginx CORS+WebSocket路由+端口修复 · 冰朔人格体启动",
"by": "铸渊(冰朔指令)"
},
{
"date": "2026-03-13",
"type": "brain_restore",
@ -33,7 +39,7 @@
"by": "GitHub Actions"
}
],
"last_updated": "2026-03-11T10:24:00.000Z",
"last_updated": "2026-03-13T06:58:00.000Z",
"total_schemas_created": 3,
"total_routes_implemented": 4,
"hli_coverage": "3/17",

View File

@ -378,8 +378,14 @@ jobs:
' proxy_set_header X-Real-IP \$remote_addr;' \
'}' \
'' \
'# Persona Studio API → persona-studio 后端端口 3002' \
'# Persona Studio API → persona-studio 后端端口 3002(含 CORS 跨域支持)' \
'location /api/ps/ {' \
' add_header Access-Control-Allow-Origin * always;' \
' add_header Access-Control-Allow-Methods "GET, POST, OPTIONS" always;' \
' add_header Access-Control-Allow-Headers "Content-Type, Authorization" always;' \
' if (\$request_method = OPTIONS) {' \
' return 204;' \
' }' \
' proxy_pass http://127.0.0.1:3002/api/ps/;' \
' proxy_http_version 1.1;' \
' proxy_set_header Host \$host;' \
@ -408,6 +414,18 @@ jobs:
' proxy_cache_bypass \$http_upgrade;' \
'}' \
'' \
'# Persona Studio WebSocket → 预览进度推送端口 3002' \
'location /ws/preview {' \
' proxy_pass http://127.0.0.1:3002;' \
' proxy_http_version 1.1;' \
' proxy_set_header Upgrade \$http_upgrade;' \
' proxy_set_header Connection \"Upgrade\";' \
' proxy_set_header Host \$host;' \
' proxy_set_header X-Real-IP \$remote_addr;' \
' proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;' \
' proxy_read_timeout 86400;' \
'}' \
'' \
'# WebSocket 反向代理 → 看板实时推送端口 8080' \
'location /ws {' \
' proxy_pass http://127.0.0.1:8080;' \

View File

@ -5,7 +5,24 @@
#
# 前端GitHub Pages→ Nginx → API 代理Node.js :3721→ 模型 API
# API 代理转发
# Persona Studio API → persona-studio 后端端口 3002含 CORS 跨域支持)
location /api/ps/ {
add_header Access-Control-Allow-Origin * always;
add_header Access-Control-Allow-Methods "GET, POST, OPTIONS" always;
add_header Access-Control-Allow-Headers "Content-Type, Authorization" always;
if ($request_method = OPTIONS) {
return 204;
}
proxy_pass http://127.0.0.1:3002/api/ps/;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 90s;
}
# API 代理转发(通用 AI 聊天)
location /api/ {
proxy_pass http://127.0.0.1:3721;
proxy_http_version 1.1;
@ -24,3 +41,15 @@ location /api/ {
proxy_read_timeout 90s;
proxy_send_timeout 60s;
}
# Persona Studio WebSocket → 预览进度推送端口 3002
location /ws/preview {
proxy_pass http://127.0.0.1:3002;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_read_timeout 86400;
}

View File

@ -0,0 +1,7 @@
{
"dev_id": "EXP-000",
"conversations": [],
"last_topic": null,
"preferences": {},
"updated_at": null
}

View File

@ -13,7 +13,7 @@ const SELECTED_MODEL = sessionStorage.getItem('selected_model');
const API_BASE = (function () {
if (location.hostname === 'localhost' || location.hostname === '127.0.0.1') {
return 'http://localhost:3721';
return 'http://localhost:3002';
}
return 'https://guanghulab.com';
})();
@ -648,33 +648,47 @@ async function confirmBuild() {
// 先连接 WebSocket确保在 build 开始前建立连接,避免丢失进度消息)
connectPreviewWebSocket();
try {
var buildRes = await fetch(API_BASE + '/api/ps/build/start', {
method: 'POST',
headers: authHeaders({ 'Content-Type': 'application/json' }),
body: JSON.stringify({
dev_id: DEV_ID,
email: email,
contact: contact,
conversation: conversationHistory,
api_base: USER_API_BASE,
api_key: USER_API_KEY,
model: SELECTED_MODEL
})
});
// 提交开发任务(含重试机制)
var maxRetries = 2;
var retryCount = 0;
var submitted = false;
if (!buildRes.ok) {
var errMsg = 'HTTP ' + buildRes.status + ' ' + buildRes.statusText;
try {
var errData = await buildRes.json();
if (errData.message) errMsg = errData.message;
} catch (_e) { /* use status text fallback */ }
appendMessage('system', '⚠️ 铸渊代理启动失败: ' + errMsg);
updatePreviewStatus('error', '启动失败');
while (retryCount <= maxRetries && !submitted) {
try {
var buildRes = await fetch(API_BASE + '/api/ps/build/start', {
method: 'POST',
headers: authHeaders({ 'Content-Type': 'application/json' }),
body: JSON.stringify({
dev_id: DEV_ID,
email: email,
contact: contact,
conversation: conversationHistory,
api_base: USER_API_BASE,
api_key: USER_API_KEY,
model: SELECTED_MODEL
})
});
if (!buildRes.ok) {
var errMsg = 'HTTP ' + buildRes.status + ' ' + buildRes.statusText;
try {
var errData = await buildRes.json();
if (errData.message) errMsg = errData.message;
} catch (_e) { /* use status text fallback */ }
appendMessage('system', '⚠️ 铸渊代理启动失败: ' + errMsg);
updatePreviewStatus('error', '启动失败');
}
submitted = true;
} catch (_err) {
retryCount++;
if (retryCount <= maxRetries) {
appendMessage('system', '⏳ 连接后端服务中,正在重试(' + retryCount + '/' + maxRetries + '...');
await new Promise(function (r) { setTimeout(r, 1500); });
} else {
appendMessage('system', '⚠️ 任务提交失败:无法连接铸渊后端服务。请检查:\n1. 网络连接是否正常\n2. 后端服务是否已启动(端口 3002\n3. 如使用 GitHub Pages 访问,请确认 guanghulab.com 服务可用');
updatePreviewStatus('error', '连接失败');
}
}
} catch (_err) {
appendMessage('system', '⚠️ 任务提交失败,请检查网络连接后再试');
updatePreviewStatus('error', '网络错误');
}
}

View File

@ -150,7 +150,7 @@
function getApiBase() {
if (location.hostname === 'localhost' || location.hostname === '127.0.0.1') {
return 'http://localhost:3721';
return 'http://localhost:3002';
}
return 'https://guanghulab.com';
}

View File

@ -6,7 +6,7 @@
*/
const http = require('http');
const BASE = process.env.TEST_BASE || 'http://localhost:3721';
const BASE = process.env.TEST_BASE || 'http://localhost:3002';
function post(path, body) {
return new Promise((resolve, reject) => {