Files
memind/mindspace-public-page-context.mjs
T

108 lines
4.1 KiB
JavaScript

import fs from 'node:fs';
import path from 'node:path';
import { PUBLISH_KEY_UUID, PUBLISH_ROOT_DIR } from './user-publish.mjs';
import { normalizeWorkspaceRelativePath } from './mindspace-pages.mjs';
const LOCAL_HOST_PATTERN = /^(localhost|127\.0\.0\.1|\[::1\]|192\.168\.|10\.|100\.)/i;
export function resolvePublicRequestOrigin({ hostHeader = '', forwardedProto = '', protocol = 'http' } = {}) {
const host = String(hostHeader ?? '').split(',')[0].trim();
if (!host) return '';
const fwdProto = String(forwardedProto ?? '').split(',')[0].trim();
const isLocalHost = LOCAL_HOST_PATTERN.test(host);
const resolvedProtocol = isLocalHost ? fwdProto || protocol || 'http' : 'https';
return `${resolvedProtocol}://${host}`;
}
export function buildPublishedHtmlViewContext({
origin = '',
requestPath = '',
filePath = '',
thumbnailPngPathForSvg = (value) => value,
fileExists = fs.existsSync,
} = {}) {
const cleanPath = String(requestPath ?? '').split('?')[0].split('#')[0];
const servedName = path.basename(String(filePath ?? ''));
const urlLast = decodeURIComponent(cleanPath.split('/').filter(Boolean).pop() ?? '');
const isImplicitIndex = urlLast.toLowerCase() !== servedName.toLowerCase();
const pageUrl = !origin
? ''
: `${origin}${isImplicitIndex && !cleanPath.endsWith('/') ? `${cleanPath}/` : cleanPath}`;
const pageDirUrl = !origin
? ''
: isImplicitIndex
? `${origin}${cleanPath.endsWith('/') ? cleanPath : `${cleanPath}/`}`
: `${origin}${cleanPath.slice(0, cleanPath.lastIndexOf('/') + 1)}`;
let fallbackImageUrl = '';
const svgSibling = String(filePath ?? '').replace(/\.[^./]+$/, '.thumbnail.svg');
if (pageDirUrl && fileExists(svgSibling)) {
const pngName = path.basename(thumbnailPngPathForSvg(svgSibling));
fallbackImageUrl = `${pageDirUrl}${pngName}`;
}
return {
origin,
cleanPath,
pageUrl,
pageDirUrl,
fallbackImageUrl,
};
}
export function parseMindSpacePublishFilePath(absoluteFilePath, h5Root) {
const resolved = path.resolve(String(absoluteFilePath ?? ''));
const publishRoot = path.resolve(h5Root, PUBLISH_ROOT_DIR);
if (resolved !== publishRoot && !resolved.startsWith(`${publishRoot}${path.sep}`)) {
return null;
}
const rest = resolved.slice(publishRoot.length + 1).split(path.sep).filter(Boolean);
if (rest.length < 2) return null;
const ownerKey = String(rest[0] ?? '').trim().toLowerCase();
if (!PUBLISH_KEY_UUID.test(ownerKey)) return null;
const relativePath = normalizeWorkspaceRelativePath(rest.slice(1).join('/'));
if (!relativePath) return null;
return { userId: ownerKey, relativePath };
}
export function buildMindSpacePageDataPayload({ pageId, accessMode = null } = {}) {
const id = String(pageId ?? '').trim();
if (!id) return null;
const payload = { pageId: id };
const mode = String(accessMode ?? '').trim();
if (mode) payload.accessMode = mode;
return payload;
}
export function injectMindSpacePageDataContext(html, context = {}) {
const payload = buildMindSpacePageDataPayload(context);
if (!payload) return String(html ?? '');
let nextHtml = String(html ?? '');
const serialized = JSON.stringify(payload).replace(/</g, '\\u003c');
const escapedPageId = String(payload.pageId).replace(/"/g, '&quot;');
const injection = [
`<meta name="mindspace-page-data-page-id" content="${escapedPageId}">`,
`<script>window.__MINDSPACE_PAGE_DATA__=${serialized};</script>`,
].join('\n');
if (/<\/head>/i.test(nextHtml)) {
return nextHtml.replace(/<\/head>/i, `${injection}\n</head>`);
}
if (/<body\b/i.test(nextHtml)) {
return nextHtml.replace(/<body\b/i, `${injection}\n<body`);
}
return `${injection}\n${nextHtml}`;
}
export function injectPublishedPageDataContext(html, { pageSource = null, publication = null } = {}) {
const pageId = String(pageSource?.pageId ?? publication?.pageId ?? '').trim();
if (!pageId) return String(html ?? '');
return injectMindSpacePageDataContext(html, {
pageId,
accessMode: publication?.accessMode ?? null,
});
}
export const mindspacePublicPageContextInternals = {
LOCAL_HOST_PATTERN,
};