d3239ff292
Ensure page sync runs via pageSyncService in remote mode, fall back to workspace HTML when storage assets are missing, and prevent production Portal from linking to loopback Plaza URLs baked in at build time. Co-authored-by: Cursor <cursoragent@cursor.com>
4.0 KiB
4.0 KiB
MindSpace Remote 页面同步与缩略图回归守卫
日期:2026-07-05
事故症状
- 缩略图 500:
GET /api/mindspace/v1/pages/:pageId/thumbnail返回 500;日志/storage 为ENOENT,但MindSpace/<userId>/public/*.html仍存在。 - 页面列表缺新生成页:103 生产
MINDSPACE_SERVER_ADAPTER=remote时,Agent 新生成的public/*.html不出现在「最近页面 / 全部页面」。 - 误判:容易把缺页当成「MindSpace 路径解析错误」或「改回 local adapter 就能修缩略图」——两者根因不同。
根因
缩略图 500
h5_page_records/h5_asset_versions.storage_key指向data/mindspace/users/.../*.md。- 磁盘上该 storage 文件可能已丢失或未写入(历史 sync、清理、quota、迁移)。
- 页面 snapshot 仍保留
relative_path: public/index.html,工作区 HTML 是真实内容来源。 loadPageThumbnailContext/getPage若只读 storage,会在 remote 8082 上直接抛错 → Portal 返回 500。
Remote 下缺页
- Portal
GET /mindspace/v1/pages会调用syncUserGeneratedPages(),将public/*.html入库。 - 曾存在
if (adapterKind === 'remote') return;,导致 8082 独立部署后 sync 永不执行。 - 测试环境多为
local,sync 正常 → 生产/测试行为不一致。
必须保留的行为
1. Workspace HTML fallback(读内容)
在 mindspace-pages.mjs 中,读取页面 content / 缩略图 HTML 时:
- 优先读
storage_key对应文件; - 若
ENOENT且page_type === 'html'且 snapshot 有relative_path→ 回退读resolvePublishDir(h5Root, userId)下对应 HTML; getPage与renderThumbnail共用readPageContentWithWorkspaceFallback,禁止只修一处。
2. Remote 也执行 page sync
- 抽出
mindspace-page-sync-service.mjs→pageSyncService.syncUserGeneratedPages(userId); - 写入
mindspace-server-adapter-contract.mjs:pageSyncServicebinding; - Local:8082 / Portal 内直接跑
syncGeneratedPagesFromPublicAssets; - Remote:Portal 通过 RPC 调 8082 的
pageSyncService.syncUserGeneratedPages; server.mjs禁止在 remote 模式 early-return 跳过 sync。
触发点(保持不变):
GET /mindspace/v1/pages列表前- 聊天 Finish 后(
onAfterFinish)
3. 103 拓扑约束
- Portal:
8081,MINDSPACE_SERVER_ADAPTER=remote - MindSpace Service:
8082,跑 local adapter(含 pageSyncService + storage + h5Root) - 用户 HTML:
/Users/john/Project/Memind/MindSpace/<userId>/public/ - Storage:
data/mindspace/users/<userId>/...
禁止用「改回 Portal local adapter」作为生产长期方案;只可用于本地对比验证。
排查入口
# 1. 页面 DB 记录 vs storage vs workspace
mysql ... # h5_page_records + h5_page_versions.source_snapshot_json + h5_asset_versions.storage_key
# 2. storage 是否存在
ls -la data/mindspace/users/<userId>/pages/<assetId>/versions/
# 3. workspace HTML 是否存在
ls -la MindSpace/<userId>/public/
# 4. 8082 健康与 adapter
curl -s http://127.0.0.1:8082/health
# 5. 缩略图 RPC(需登录态;或直接在 8082 侧调 pageService.renderThumbnail)
curl -I 'https://m.tkmind.cn/api/mindspace/v1/pages/<pageId>/thumbnail'
改相关文件前必跑
npm run verify:mindspace-page-sync-guards
node --test mindspace-page-sync-service.test.mjs mindspace-pages.test.mjs
Checklist(发 103 前)
mindspace-pages.mjs含 workspace fallbackpageSyncService在 contract + local runtime + remote proxy 中齐全server.mjs无 remote sync skip- 8082 与 Portal 均发布(8082 承载 renderThumbnail + sync)
- 用真实 pageId 验证 thumbnail 200 + 列表含新页
相关单测
mindspace-page-sync-service.test.mjsmindspace-pages.test.mjs(storage 缺失时 thumbnail / getPage fallback)mindspace-page-sync.test.mjs(public dir → DB)
代码内标记
搜索 REGRESSION GUARD: mindspace-page-sync-thumbnail 定位内联说明。