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, '