Files
memind/server/portal-static-delivery-routes.mjs
T
john f281f15788
Memind CI / Test, build, and release guards (pull_request) Failing after 3m21s
feat(seo): 固化 m.tkmind.cn 百度验证、推送与 sitemap 去重
让 Portal 首页稳定注入站长验证 meta,并提供 sitemap 批量推送脚本与 LaunchAgent 安装入口;sitemap/llms 对同页优先保留 /u/ canonical URL。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-16 16:28:46 +08:00

343 lines
7.8 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import fs from 'node:fs';
import path from 'node:path';
import express from 'express';
import {
extractSharePreviewMeta,
injectOgTags,
renderWechatSharePreviewHtml,
} from '../mindspace-og-tags.mjs';
import { injectBaiduSiteVerification } from '../mindspace-baidu-site-verification.mjs';
export function attachPortalStaticDeliveryRoutes({
app,
h5Root,
host,
port,
publishRootDir,
usersRoot,
userAuthReady = Promise.resolve(),
resolveRequestOrigin,
fetchFn = fetch,
fsApi = fs,
pathApi = path,
staticMiddleware = express.static,
injectOgTagsFn = injectOgTags,
extractSharePreviewMetaFn =
extractSharePreviewMeta,
renderWechatSharePreviewHtmlFn =
renderWechatSharePreviewHtml,
} = {}) {
if (
!app ||
!h5Root ||
!publishRootDir ||
!usersRoot ||
typeof resolveRequestOrigin !== 'function' ||
typeof fetchFn !== 'function' ||
typeof staticMiddleware !== 'function'
) {
throw new Error(
'attachPortalStaticDeliveryRoutes requires route dependencies',
);
}
app.use('/temp', (req, res) => {
res.redirect(
301,
`/${publishRootDir}${req.url}`,
);
});
app.use('/user', async (req, res, next) => {
await userAuthReady;
const parts = req.path
.split('/')
.filter(Boolean);
if (parts.length < 1) return next();
const [username, ...rest] = parts;
const targetDir = pathApi.join(
usersRoot,
username,
);
const filePath = pathApi.join(
targetDir,
...rest,
);
const resolvedRoot =
pathApi.resolve(targetDir);
const resolvedPath =
pathApi.resolve(filePath);
if (
!resolvedPath.startsWith(
resolvedRoot + pathApi.sep,
) &&
resolvedPath !== resolvedRoot
) {
return res
.status(403)
.json({ message: '禁止访问' });
}
if (!fsApi.existsSync(targetDir)) {
return res
.status(404)
.json({ message: '用户不存在' });
}
if (!fsApi.existsSync(resolvedPath)) {
return res
.status(404)
.json({ message: '文件不存在' });
}
if (fsApi.statSync(resolvedPath).isDirectory()) {
const indexPath = pathApi.join(
resolvedPath,
'index.html',
);
if (fsApi.existsSync(indexPath)) {
return res.sendFile(indexPath);
}
return res
.status(404)
.json({
message: '目录中没有 index.html',
});
}
res.sendFile(resolvedPath, (error) => {
if (error) {
res
.status(404)
.json({ message: '文件不存在' });
}
});
});
app.get(
`/${publishRootDir}/wiki/*`,
(_req, res) => {
res.sendFile(
pathApi.join(
h5Root,
publishRootDir,
'wiki',
'index.html',
),
);
},
);
app.get('/temp/wiki/*', (req, res) => {
res.redirect(
301,
`/${publishRootDir}/wiki${req.url.slice(
'/temp/wiki'.length,
)}`,
);
});
app.use(
'/plaza-covers',
staticMiddleware(
pathApi.join(
h5Root,
'public/plaza-covers',
),
{ maxAge: '7d' },
),
);
app.use(
'/brand',
staticMiddleware(
pathApi.join(h5Root, 'public/brand'),
{ maxAge: '7d' },
),
);
app.use(
'/assets',
staticMiddleware(
pathApi.join(h5Root, 'public/assets'),
{ maxAge: '1d' },
),
);
app.use(
'/learn',
staticMiddleware(
pathApi.join(h5Root, 'public/learn'),
{ maxAge: '5m', index: 'index.html' },
),
);
app.get(
'/dev/wechat-share-preview',
async (req, res) => {
try {
const rawTarget = String(
req.query.url ?? req.query.path ?? '',
).trim();
if (!rawTarget) {
return res
.status(400)
.type('text/plain; charset=utf-8')
.send(
'缺少 url 参数,例如 ?url=/MindSpace/john/public/demo.html',
);
}
const origin =
resolveRequestOrigin(req) ||
`http://${host}:${port}`;
const targetUrl = rawTarget.startsWith(
'http',
)
? new URL(rawTarget)
: new URL(
rawTarget.startsWith('/')
? rawTarget
: `/${rawTarget}`,
origin,
);
const upstream = await fetchFn(
targetUrl.toString(),
{
headers: {
'user-agent':
req.get('user-agent') ||
'tkmind-wechat-share-preview',
accept: 'text/html,*/*',
},
redirect: 'follow',
},
);
if (!upstream.ok) {
return res
.status(upstream.status)
.type('text/plain; charset=utf-8')
.send(
`页面请求失败:HTTP ${upstream.status}`,
);
}
let html = await upstream.text();
const pageUrl =
targetUrl.toString().split('#')[0];
const pageDirUrl = pageUrl.slice(
0,
pageUrl.lastIndexOf('/') + 1,
);
html = injectOgTagsFn(html, {
origin: targetUrl.origin,
pageUrl,
pageDirUrl,
});
const preview =
extractSharePreviewMetaFn(html, {
origin: targetUrl.origin,
pageUrl,
pageDirUrl,
});
res.set(
'Content-Type',
'text/html; charset=utf-8',
);
res.set('Cache-Control', 'no-store');
return res.send(
renderWechatSharePreviewHtmlFn(
preview,
{
note: '此预览读取页面最终 HTML 中的 Open Graph 标签,可用来对照微信里粘贴链接后的卡片效果。',
},
),
);
} catch (error) {
return res
.status(500)
.type('text/plain; charset=utf-8')
.send(
String(error?.message ?? error),
);
}
},
);
app.use(
'/dev',
staticMiddleware(
pathApi.join(h5Root, 'public/dev'),
{ maxAge: 0 },
),
);
app.get(
/^\/MP_verify_[A-Za-z0-9]+\.txt$/,
(req, res) => {
const fileName = pathApi.basename(req.path);
const filePath = pathApi.join(
h5Root,
'public',
fileName,
);
if (!fsApi.existsSync(filePath)) {
return res.status(404).end();
}
res
.type('text/plain')
.sendFile(filePath);
},
);
const sendPortalIndexHtml = (res) => {
const indexPath = pathApi.join(h5Root, 'dist', 'index.html');
const html = fsApi.readFileSync(indexPath, 'utf8');
res
.type('text/html; charset=utf-8')
.set('Cache-Control', 'public, max-age=0')
.send(injectBaiduSiteVerification(html));
};
app.get(
/^\/baidu_verify_[A-Za-z0-9-]+\.html$/,
(req, res) => {
const fileName = pathApi.basename(req.path);
const filePath = pathApi.join(
h5Root,
'public',
fileName,
);
if (!fsApi.existsSync(filePath)) {
return res.status(404).end();
}
return res
.type('text/html; charset=utf-8')
.sendFile(filePath);
},
);
app.get('/', (_req, res) => {
sendPortalIndexHtml(res);
});
app.use('/auth', (_req, res) => {
res.status(404).json({
message:
'接口不存在,请重启后端服务(node server.mjs 或 pnpm dev)',
});
});
app.use('/admin-api', (_req, res) => {
res.status(404).json({
message:
'接口不存在,请重启后端服务(node server.mjs 或 pnpm dev)',
});
});
app.use(
staticMiddleware(
pathApi.join(h5Root, 'dist'),
{ index: 'index.html' },
),
);
app.get('*', (_req, res) => {
sendPortalIndexHtml(res);
});
}