import { injectMindSpaceAnalytics, resolveAnalyticsOwnerLabel, resolveAnalyticsOwnerSegment, resolveAnalyticsPlan, } from '../mindspace-analytics.mjs'; import { injectMindSpaceRybbit, } from '../mindspace-rybbit.mjs'; import { buildPublishedHtmlViewContext, resolveMindSpacePageDataContext, } from '../mindspace-public-page-context.mjs'; import { preparePublicHtmlAssetDelivery, } from '../mindspace-public-asset-token.mjs'; import { decorateMindSpacePublishedHtml, } from '../mindspace-public-delivery.mjs'; import { injectPublicFileShareButton, } from '../mindspace-public-share-widget.mjs'; import { allowPlazaEmbedFrame, isPlazaEmbedRequest, preparePublicationHtmlForEmbed, } from '../plaza-embed.mjs'; import { publishedPageCsp, } from '../mindspace-published-page-csp.mjs'; import { rewriteKnownCdnScriptSources, } from '../mindspace-published-script-localize.mjs'; import { injectOgTags, injectWechatShareBridge, } from '../mindspace-og-tags.mjs'; import { isLongImageDownloadRequest, } from '../mindspace-long-image.mjs'; import { isWechatUserAgent } from '../wechat-oauth.mjs'; function deliveryNotFoundMessage(reason) { if (reason === 'missing_owner_dir' || reason === 'missing_owner') { return '用户不存在'; } if (reason === 'missing_directory_index') { return '目录中没有 index.html'; } return '文件不存在'; } export function createPortalWorkspacePublicationDelivery({ internalAgentSecret, analyticsConfig, rybbitConfig, getAuthPool = () => null, getUserAuth = () => null, getMindSpacePages = () => null, getWorkspacePublicationDelivery = () => null, resolveRequestOrigin, removeQueryParam, logger = console, resolvePageDataContext = resolveMindSpacePageDataContext, } = {}) { if ( typeof resolveRequestOrigin !== 'function' || typeof removeQueryParam !== 'function' ) { throw new Error( 'createPortalWorkspacePublicationDelivery requires delivery dependencies', ); } function requireDeliveryService() { const service = getWorkspacePublicationDelivery(); if ( !service || typeof service.resolveWorkspaceRequest !== 'function' ) { throw Object.assign( new Error('MindSpace 交付服务未启用'), { code: 'delivery_service_unavailable' }, ); } return service; } async function sendLongImageDownloadIfRequested( req, res, ) { if ( !isLongImageDownloadRequest(req.query) ) { return false; } const origin = resolveRequestOrigin(req); const currentUrl = origin ? `${origin}${ req.originalUrl || req.url || '' }` : null; const pageUrl = currentUrl ? removeQueryParam( removeQueryParam( currentUrl, 'download', ), 'export', ) : null; try { const rendered = await requireDeliveryService() .renderLongImage({ pageUrl }); const image = Buffer.from( rendered.bodyBase64, 'base64', ); res.set('Content-Type', 'image/png'); res.set( 'Content-Disposition', `attachment; filename="${ rendered.servedName || 'mindspace-public-page.long.png' }"`, ); res.set('Cache-Control', 'no-store'); res.send(image); } catch (error) { res .status(500) .type('text/plain; charset=utf-8') .send( `长图生成失败:${ error?.message || '未知错误' }`, ); } return true; } async function sendPublishDelivery( req, res, delivery, { isOwner = true } = {}, ) { if (delivery.kind !== 'html') { if (delivery.cacheControl) { res.set( 'Cache-Control', delivery.cacheControl, ); } res.type?.( delivery.servedName || 'application/octet-stream', ); return res.send( Buffer.from( delivery.bodyBase64 || '', 'base64', ), ); } if ( await sendLongImageDownloadIfRequested( req, res, ) ) { return; } let html = rewriteKnownCdnScriptSources( delivery.html, ).html; html = preparePublicHtmlAssetDelivery( html, internalAgentSecret, ); const embed = isPlazaEmbedRequest(req.query); if (embed) { html = preparePublicationHtmlForEmbed(html); allowPlazaEmbedFrame(res); res.set( 'Content-Security-Policy', publishedPageCsp(html, { embed }), ); } const context = buildPublishedHtmlViewContext({ origin: resolveRequestOrigin(req), requestPath: req.originalUrl || req.url || '', servedName: delivery.servedName, fallbackImageName: delivery.thumbnailName, }); const userAuth = getUserAuth(); const pageOwner = delivery.ownerId && userAuth ? await userAuth .getUserById(delivery.ownerId) .catch(() => null) : null; let pageDataContext = null; const mindSpacePages = getMindSpacePages(); if ( delivery.ownerId && delivery.relativePath ) { pageDataContext = await resolvePageDataContext({ pool: getAuthPool(), pageService: mindSpacePages, userId: delivery.ownerId, relativePath: delivery.relativePath, logger, }); } html = injectMindSpaceAnalytics(html, { ownerId: delivery.ownerId ?? '', ownerSegment: resolveAnalyticsOwnerSegment( pageOwner ?? {}, ), ownerLabel: resolveAnalyticsOwnerLabel( pageOwner ?? {}, ), planType: resolveAnalyticsPlan( pageOwner ?? {}, ), generatedAt: pageDataContext?.generatedAt ?? '', pageId: pageDataContext?.pageId ?? '', publicationId: pageDataContext?.publicationId ?? pageDataContext?.publication_id ?? '', config: analyticsConfig, }); html = injectMindSpaceRybbit(html, { ownerId: delivery.ownerId ?? '', ownerSegment: resolveAnalyticsOwnerSegment( pageOwner ?? {}, ), ownerLabel: resolveAnalyticsOwnerLabel( pageOwner ?? {}, ), pageId: pageDataContext?.pageId ?? '', publicationId: pageDataContext?.publicationId ?? pageDataContext?.publication_id ?? '', config: rybbitConfig, }); const decorated = decorateMindSpacePublishedHtml({ html, embed, isOwner, pageDataContext, context, userAgent: req.get('user-agent') || '', preparePublicationHtmlForEmbed, injectOgTags, injectWechatShareBridge, injectPublicFileShareButton, publishedPageCsp, isWechatUserAgent, }); html = decorated.html; if (decorated.allowEmbedFrame) { allowPlazaEmbedFrame(res); } res.set( 'Content-Security-Policy', decorated.csp, ); res.set( 'Content-Type', 'text/html; charset=utf-8', ); // REGRESSION GUARD: mindspace-public-owner-vs-visitor — this HTML varies by viewer. res.set( 'Cache-Control', 'private, no-store', ); return res.send(html); } async function serveUserPublishFile( req, res, next, ) { let result; try { result = await requireDeliveryService() .resolveWorkspaceRequest({ requestPath: req.path, }); } catch (error) { logger?.error?.( '[MindSpace] workspace delivery failed', error, ); return res .status(503) .json({ message: 'MindSpace 交付服务不可用', }); } if (result.action === 'redirect') { return res.redirect( result.status ?? 301, result.location, ); } if (result.action === 'forbidden') { return res .status(403) .json({ message: '禁止访问' }); } if (result.action === 'not_found') { return res .status(404) .json({ message: deliveryNotFoundMessage( result.reason, ), }); } if (result.action === 'not_ready') { return res .status(409) .type('text/plain; charset=utf-8') .send( '页面已生成,正在完成发布验证,请稍后重试。', ); } const userAuth = getUserAuth(); const viewer = req.userSession && userAuth ? await userAuth .getMe(req.userToken) .catch(() => null) : null; const isOwner = Boolean( viewer?.id && result.ownerId && String(viewer.id).toLowerCase() === String(result.ownerId).toLowerCase(), ); return sendPublishDelivery( req, res, result, { isOwner }, ); } return { sendPublishDelivery, serveUserPublishFile, }; }