Skip to content

Commit 05881cb

Browse files
nieaoclaude
andcommitted
fix(canvas): 孤儿警告刷屏 + 截图上传 405 — useMemo 重算 + Caddy 代理路径
孤儿警告: useMemo 因 yjs sync 频繁重算, 每次都 console.warn 把 console + CPU 炸. 改成 window.__orphanWarned 全局 flag, 一次/页面. 截图 405: /canvas/screenshot 走 Caddy 静态服务直接 405 Method Not Allowed. 改路径到 /canvas/api/screenshot 走 source-proxy 反代规则. 源端兼容两条路径 (公开 .png/.svg GET 仍保留旧 /canvas/screenshot/). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 4679a8c commit 05881cb

3 files changed

Lines changed: 13 additions & 6 deletions

File tree

server/source-proxy.js

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -790,10 +790,11 @@ const server = http.createServer(async (req, res) => {
790790
return
791791
}
792792

793-
// ── /canvas/screenshot (POST) ── 元认知完成后前端上传截图 + 元数据
793+
// ── /canvas/api/screenshot (POST) ── 元认知完成后前端上传截图 + 元数据
794794
// body: { id, room, rootId, prompt, decision, score, pngDataUrl, svgDataUrl?, meta? }
795+
// 走 /canvas/api/* 路径, Caddy 反代到 source-proxy (其他 /canvas/* 走静态)
795796
// 写到本地 disk, bot daemon 同 VPS 直接 fs 读, 不走 HTTP
796-
if (method === 'POST' && url.pathname === '/canvas/screenshot') {
797+
if (method === 'POST' && (url.pathname === '/canvas/api/screenshot' || url.pathname === '/canvas/screenshot')) {
797798
const body = await readJsonBody(req, 12 * 1024 * 1024) // 12MB 上限 — 高分辨率 PNG 可能 2-5MB
798799
const id = String(body.id || '').replace(/[^a-zA-Z0-9_-]/g, '').slice(0, 80)
799800
if (!id) { sendJson(res, 400, { ok: false, error: 'id 必填' }); return }
@@ -843,9 +844,10 @@ const server = http.createServer(async (req, res) => {
843844
}
844845

845846
// ── /canvas/screenshot/:id.(png|svg|json) (GET) ── 公开访问截图 + 元数据
846-
const screenshotMatch = url.pathname.match(/^\/canvas\/screenshot\/([a-zA-Z0-9_-]+)\.(png|svg|json)$/)
847+
// 两条路径同时支持: /canvas/screenshot/{id}.png (公开) + /canvas/api/screenshot/{id}.png (api 通道)
848+
const screenshotMatch = url.pathname.match(/^\/canvas\/(api\/)?screenshot\/([a-zA-Z0-9_-]+)\.(png|svg|json)$/)
847849
if (method === 'GET' && screenshotMatch) {
848-
const [, id, ext] = screenshotMatch
850+
const [, , id, ext] = screenshotMatch
849851
const filePath = path.join(SCREENSHOTS_DIR, `${id}.${ext}`)
850852
try {
851853
if (!fs.existsSync(filePath)) { sendJson(res, 404, { ok: false, error: '截图不存在' }); return }

src/components/canvas/KnowledgeCanvas.jsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -392,7 +392,11 @@ function KnowledgeCanvasInner({
392392
},
393393
data: { isOrphanFallback: true },
394394
})
395-
console.warn('[KnowledgeCanvas] 检测到孤儿子节点, 已挂 fallback root')
395+
// 只 warn 一次/页面 — useMemo 会因 yjs sync 频繁重算, 不限速会炸 console + CPU
396+
if (typeof window !== 'undefined' && !window.__orphanWarned) {
397+
window.__orphanWarned = true
398+
console.warn('[KnowledgeCanvas] 检测到孤儿子节点, 已挂 fallback root (后续静默)')
399+
}
396400
}
397401
// 最终交给 React Flow 的数组: 把 hidden 节点直接剔除.
398402
// 单靠 hidden=true 字段不够 — React Flow 11 在 child layout 时仍可能 throw

src/utils/projectScreenshot.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,8 @@ export async function captureAndUploadProjectScreenshot({ rootId, conclusionId,
8787
// POST 到 source-proxy
8888
const ssId = conclusionId || rootId
8989
try {
90-
const r = await fetch('/canvas/screenshot', {
90+
// 走 /canvas/api/* 路径 — Caddy 反代到 source-proxy:17090; /canvas/screenshot 走静态服务会 405
91+
const r = await fetch('/canvas/api/screenshot', {
9192
method: 'POST',
9293
headers: { 'Content-Type': 'application/json; charset=utf-8' },
9394
body: JSON.stringify({

0 commit comments

Comments
 (0)