${title}
${summary ? `${summary}
` : ''} ${previewNote}import { appendPlazaEmbedQuery } from './plaza-embed.mjs'; import { buildMindspaceGeoPayload } from './mindspace-geo-backfill.mjs'; import { decoratePlatformSeoGeoHtml } from './platform-seo-html.mjs'; import { resolvePlazaCoverUrl } from './plaza-posts.mjs'; function plazaError(message, code) { return Object.assign(new Error(message), { code }); } export function escapePublicHtml(value) { return String(value ?? '') .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"'); } export async function loadPlazaPublicPost(pool, postId, { devPreview = false } = {}) { const [rows] = await pool.query( `SELECT pp.id, pp.title, pp.summary, pp.status, pp.cover_url, pr.public_url, pr.status AS publication_status FROM plaza_posts pp JOIN h5_publish_records pr ON pr.id = pp.publication_id WHERE pp.id = ? AND pp.status != 'hidden' LIMIT 1`, [postId], ); const row = rows[0]; if (!row) throw plazaError('帖子不存在或已隐藏', 'POST_NOT_FOUND'); if (row.publication_status !== 'online') { throw plazaError('关联发布未上线', 'PUBLICATION_NOT_ONLINE'); } if (row.status !== 'published') { const allowPreview = devPreview && row.status === 'pending_review'; if (!allowPreview) throw plazaError('帖子不存在或已隐藏', 'POST_NOT_FOUND'); } return row; } export function buildPublicationUrl(publicUrl, portalBase) { const value = String(publicUrl ?? '').trim(); if (!value) return null; if (/^https?:\/\//i.test(value)) return value; const base = String(portalBase ?? '').replace(/\/$/, ''); if (value.startsWith('/')) return `${base}${value}`; return `${base}/${value.replace(/^\/+/, '')}`; } export function renderPlazaPostRedirectHtml( post, targetUrl, { preview = false, plazaBaseUrl = process.env.PLAZA_PUBLIC_BASE ?? 'https://plaza.tkmind.cn' } = {}, ) { const title = escapePublicHtml(post.title); const summary = escapePublicHtml(post.summary); const iframeUrl = appendPlazaEmbedQuery(targetUrl); const safeTarget = escapePublicHtml(targetUrl); const safeIframeUrl = escapePublicHtml(iframeUrl); const previewNote = preview ? '
开发预览:该帖子尚未审核通过,仅本地可见。
' : ''; const pageUrl = `${String(plazaBaseUrl).replace(/\/$/, '')}/plaza/p/${encodeURIComponent(post.id)}`; const coverUrl = resolvePlazaCoverUrl(post.cover_url, post.public_url); const html = `${summary}
` : ''} ${previewNote}${post.summary ?? ''}
${post.summary ?? post.title}
点击卡片进入详情页,或在 TKMind 发现广场浏览更多公开作品。
`, ), }, }); } export function attachPlazaPublicRoutes(app, { pool, portalBase, devPreview = false, plazaBaseUrl = process.env.PLAZA_PUBLIC_BASE ?? 'https://plaza.tkmind.cn' }) { app.get('/plaza/p/:postId', async (req, res) => { if (!pool) return res.status(503).send('Plaza 未启用'); try { const post = await loadPlazaPublicPost(pool, req.params.postId, { devPreview }); const targetUrl = buildPublicationUrl(post.public_url, portalBase); if (!targetUrl) return res.status(404).send('内容链接不存在'); res.set('Content-Type', 'text/html; charset=utf-8'); res.set('Cache-Control', post.status === 'published' ? 'public, max-age=60' : 'private, no-store'); return res.send( renderPlazaPostRedirectHtml(post, targetUrl, { preview: devPreview && post.status === 'pending_review', plazaBaseUrl, }), ); } catch (error) { if (error?.code === 'POST_NOT_FOUND') return res.status(404).send('帖子不存在或已隐藏'); if (error?.code === 'PUBLICATION_NOT_ONLINE') return res.status(404).send('关联发布未上线'); return res.status(500).send('页面加载失败'); } }); app.get(['/plaza', '/plaza/'], async (_req, res) => { if (!pool) return res.status(503).send('Plaza 未启用'); try { const [categoryRows] = await pool.query( `SELECT slug, name, icon FROM plaza_categories WHERE is_active = 1 ORDER BY sort_order ASC`, ); const [postRows] = await pool.query( `SELECT pp.id, pp.title, pp.summary, pp.cover_url, pp.published_at, pr.public_url AS public_url, c.name AS category_name, c.slug AS category_slug, c.icon AS category_icon, u.slug AS user_slug, u.display_name AS user_display_name FROM plaza_posts pp JOIN plaza_categories c ON c.id = pp.category_id JOIN h5_publish_records pr ON pr.id = pp.publication_id JOIN h5_users u ON u.id = pp.user_id WHERE pp.status = 'published' ORDER BY pp.hot_score DESC, pp.id DESC LIMIT 24`, ); const cards = postRows .map((post) => { const title = escapePublicHtml(post.title); const summary = escapePublicHtml(post.summary); const author = escapePublicHtml(post.user_display_name || post.user_slug); const category = escapePublicHtml(`${post.category_icon ?? ''} ${post.category_name ?? ''}`.trim()); const coverUrl = escapePublicHtml(resolvePlazaCoverUrl(post.cover_url, post.public_url)); const href = `/plaza/p/${encodeURIComponent(post.id)}`; return `${summary}
` : ''} `; }) .join(''); const categoryLinks = categoryRows .map( (category) => `${escapePublicHtml(`${category.icon ?? ''} ${category.name}`.trim())}`, ) .join(''); const plazaOrigin = String(plazaBaseUrl).replace(/\/$/, ''); const plazaHomeUrl = `${plazaOrigin}/plaza`; const homepageHtml = `TKMind Plaza
浏览公开发布的 MindSpace 作品。点击卡片进入帖子详情页。
${categoryLinks ? `浏览公开发布的 MindSpace 页面与创意内容。
按旅行、美食、活动、报告等分类发现更多作品。
', ), }, }), ); } catch { return res.status(500).send('广场首页加载失败'); } }); }