From 1d291fd5288908ce942f69ad565bdd46e344801d Mon Sep 17 00:00:00 2001 From: john Date: Fri, 24 Jul 2026 17:58:52 +0800 Subject: [PATCH] fix: restore page links after delayed materialization --- .../mindspace-publish-and-chat-finish.md | 1 + tkmind-proxy.mjs | 9 +++++++++ tkmind-proxy.test.mjs | 15 +++++++++++++++ 3 files changed, 25 insertions(+) diff --git a/docs/regression-guards/mindspace-publish-and-chat-finish.md b/docs/regression-guards/mindspace-publish-and-chat-finish.md index 894843c..8239d4e 100644 --- a/docs/regression-guards/mindspace-publish-and-chat-finish.md +++ b/docs/regression-guards/mindspace-publish-and-chat-finish.md @@ -94,6 +94,7 @@ - `H5_USERS_ROOT` 同级的共享 `MindSpace/` - 显式 `MEMIND_SHARED_PUBLISH_ROOT` / `GOOSED_SANDBOX_PUBLISH_ROOT` - 不得因为 Portal 运行在临时 worktree,而误删共享 session 工作区中的有效页面链接 +- 历史消息若已被替换成「页面生成未完成」占位文案,后续读取发现同名 HTML 已存在时必须恢复为可点击链接 ### 守卫 diff --git a/tkmind-proxy.mjs b/tkmind-proxy.mjs index 395ec9b..5cef979 100644 --- a/tkmind-proxy.mjs +++ b/tkmind-proxy.mjs @@ -461,6 +461,8 @@ const PUBLIC_HTML_MARKDOWN_LINK_PATTERN = const MARKDOWN_LINK_PATTERN = /\[([^\]\n]*)\]\((https?:\/\/[^\s<>"')\]]+)\)/g; const PLAIN_HTML_URL_PATTERN = /https?:\/\/[^\s<>"')\]]+\.html/gi; const INLINE_PUBLIC_HTML_PATH_PATTERN = /`((?:\.\/)?public\/[^`\s<>"')\]]+\.html)`/gi; +const MISSING_PUBLIC_HTML_NOTICE_PATTERN = + /(页面生成未完成,已阻止显示失效链接:([^()\n]+\.html)。)/g; const USER_IDENTITY_BLOCK_PATTERN = /^\[用户身份\][\s\S]*?(?:\n{2,}|$)/; const IMAGE_URL_LINES_PATTERN = /\n*\[图片\d+]: [^\n]+/g; const TKMIND_VISION_NOTE_PATTERN = /\n*【TKMind 图片分析结果[\s\S]*$/; @@ -606,6 +608,13 @@ function rewriteOwnPublicationRouteLinks(text, currentUser) { export function sanitizePublicHtmlLinksInText(text, currentUser) { let next = String(text ?? '').replace( + MISSING_PUBLIC_HTML_NOTICE_PATTERN, + (match, filename) => { + const result = sanitizeOwnPublicHtmlRelativePath(`public/${path.posix.basename(filename)}`, currentUser); + return result ? `[${result.label}](${result.url})` : match; + }, + ); + next = next.replace( PUBLIC_HTML_MARKDOWN_LINK_PATTERN, (match, label, url, owner, rawRelativePath) => { const result = sanitizeOwnPublicHtmlUrl(url, owner, rawRelativePath, currentUser); diff --git a/tkmind-proxy.test.mjs b/tkmind-proxy.test.mjs index 1e6ce01..f864da9 100644 --- a/tkmind-proxy.test.mjs +++ b/tkmind-proxy.test.mjs @@ -219,6 +219,21 @@ test('sanitizePublicHtmlLinksInText finds pages in the workspace beside H5_USERS } }); +test('sanitizePublicHtmlLinksInText restores a previously suppressed link after the page appears', () => { + const owner = `test-user-${Date.now()}-restored-link`; + const htmlPath = path.join(process.cwd(), 'MindSpace', owner, 'public', 'poem.html'); + try { + fs.mkdirSync(path.dirname(htmlPath), { recursive: true }); + fs.writeFileSync(htmlPath, 'Poem'); + const text = '页面链接:\n(页面生成未完成,已阻止显示失效链接:poem.html。)'; + const next = sanitizePublicHtmlLinksInText(text, { id: owner, username: 'john' }); + assert.match(next, new RegExp(`\\[poem\\.html\\]\\([^)]*/MindSpace/${owner}/public/poem\\.html\\)`)); + assert.doesNotMatch(next, /页面生成未完成/); + } finally { + fs.rmSync(path.join(process.cwd(), 'MindSpace', owner), { recursive: true, force: true }); + } +}); + test('sanitizePublicHtmlLinksInText canonicalizes wrong MindSpace public hosts', () => { const owner = `test-user-${Date.now()}-canonical-host`; const previousBase = process.env.H5_PUBLIC_BASE_URL;