From 880cd59ca5f613215ce42af85bda7b4b0b035f7c Mon Sep 17 00:00:00 2001 From: john Date: Fri, 10 Jul 2026 11:53:03 +0800 Subject: [PATCH] fix(mindspace): link inline public html paths --- tkmind-proxy.mjs | 19 ++++++++++++++++++- tkmind-proxy.test.mjs | 22 ++++++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/tkmind-proxy.mjs b/tkmind-proxy.mjs index c2d8f5c..c46fb52 100644 --- a/tkmind-proxy.mjs +++ b/tkmind-proxy.mjs @@ -446,6 +446,7 @@ const PUBLIC_HTML_MARKDOWN_LINK_PATTERN = /\[([^\]\n]*)\]\((https?:\/\/[^\s<>"')\]]+\/MindSpace\/([0-9a-f-]{36}|[a-z0-9._-]+)\/([^\s<>"')\]]+\.html))\)/gi; 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 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]*$/; @@ -547,6 +548,18 @@ function sanitizeMissingMindSpacePublicHtmlUrl(url, currentUser) { return result.ok ? result.url : result.notice; } +function sanitizeOwnPublicHtmlRelativePath(rawRelativePath, currentUser) { + const owner = currentUser?.id || currentUser?.username; + if (!owner) return null; + const normalizedRelativePath = normalizeStaticHtmlRelativePath(rawRelativePath); + if (!normalizedRelativePath.toLowerCase().endsWith('.html')) return null; + if (!publicHtmlExistsForUser(owner, normalizedRelativePath, currentUser)) return null; + return { + label: path.posix.basename(normalizedRelativePath), + url: buildPublicUrl(resolvePublicBaseUrl(), owner, normalizedRelativePath), + }; +} + export function sanitizePublicHtmlLinksInText(text, currentUser) { let next = String(text ?? '').replace( PUBLIC_HTML_MARKDOWN_LINK_PATTERN, @@ -565,9 +578,13 @@ export function sanitizePublicHtmlLinksInText(text, currentUser) { if (!sanitizedUrl) return match; return label ? `[${label}](${sanitizedUrl})` : sanitizedUrl; }); - return next.replace(PLAIN_HTML_URL_PATTERN, (match) => ( + next = next.replace(PLAIN_HTML_URL_PATTERN, (match) => ( sanitizeMissingMindSpacePublicHtmlUrl(match, currentUser) ?? match )); + return next.replace(INLINE_PUBLIC_HTML_PATH_PATTERN, (match, rawRelativePath) => { + const result = sanitizeOwnPublicHtmlRelativePath(rawRelativePath, currentUser); + return result ? `[${result.label}](${result.url})` : match; + }); } export function sanitizeUserVisibleMessageText(text, currentUser) { diff --git a/tkmind-proxy.test.mjs b/tkmind-proxy.test.mjs index cebb91e..463595f 100644 --- a/tkmind-proxy.test.mjs +++ b/tkmind-proxy.test.mjs @@ -247,6 +247,28 @@ test('sanitizePublicHtmlLinksInText leaves other users missing-MindSpace links u assert.equal(next, text); }); +test('sanitizePublicHtmlLinksInText links existing inline public html paths', () => { + const owner = `test-user-${Date.now()}-inline-public-path`; + const previousBase = process.env.H5_PUBLIC_BASE_URL; + process.env.H5_PUBLIC_BASE_URL = 'https://m.tkmind.cn'; + const htmlPath = path.join(process.cwd(), 'MindSpace', owner, 'public', 'thailand-travel-guide.html'); + try { + fs.mkdirSync(path.dirname(htmlPath), { recursive: true }); + fs.writeFileSync(htmlPath, 'Thailand'); + const text = '攻略页面已成功创建!文件位于 **`public/thailand-travel-guide.html`**(约 37KB)。'; + const next = sanitizePublicHtmlLinksInText(text, { id: owner, username: 'john2' }); + assert.doesNotMatch(next, /`public\/thailand-travel-guide\.html`/); + assert.match( + next, + new RegExp(`\\[thailand-travel-guide\\.html\\]\\(https://m\\.tkmind\\.cn/MindSpace/${owner}/public/thailand-travel-guide\\.html\\)`), + ); + } finally { + if (previousBase == null) delete process.env.H5_PUBLIC_BASE_URL; + else process.env.H5_PUBLIC_BASE_URL = previousBase; + fs.rmSync(path.join(process.cwd(), 'MindSpace', owner), { recursive: true, force: true }); + } +}); + test('sanitizeSessionConversationPublicHtmlLinks updates text items inside conversation messages', () => { const owner = `test-user-${Date.now()}-conversation`; const publicRoot = path.join(process.cwd(), 'MindSpace', owner, 'public');