fix(mindspace-public): 公开页区分作者与访客视图 + 图片加载自动重试
工作区直链 /MindSpace/<userId>/public/*.html 此前对所有人(含匿名访客、 转发链接收到的其他登录用户)展示完全相同的悬浮操作按钮,导致非作者也能 看到"发布 Plaza"入口——而 Plaza 发布会把内容归属到操作者自己的账号下, 必须只对作者开放。 - server.mjs: serveUserPublishFile 用现有 session/cookie 鉴权判断访问者是 否等于 URL 中的 ownerKey,结果透传给 sendPublishFile;该响应内容因人 而异,显式加 Cache-Control: private, no-store(原来未设置任何缓存头) - mindspace-public-share-widget.mjs: injectPublicFileShareButton 新增 isOwner 参数(默认 true 保持兼容),非作者时隐藏"发布 Plaza"按钮与确认 弹窗,"保存长图"/"公开分享"对所有访客保留 - mindspace-public-delivery.mjs: 透传 isOwner,并注入图片重试脚本 - mindspace-public-image-retry.mjs(新增): 页面级 onerror 之前用捕获阶段 监听拦截 mindspace 资产图片的加载失败,带退避自动重试 3 次,避免刚生成 页面时的资产物化竞态被"永久占位符"放大成显示故障 本地起服务用 owner / 非 owner 已登录用户 / 匿名访客三种身份实测验证。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+17
-2
@@ -5756,7 +5756,7 @@ async function ensurePublicHtmlPrivateAssetsMaterialized(filePath, html) {
|
||||
return result.changed ? result.html : html;
|
||||
}
|
||||
|
||||
async function sendPublishFile(req, res, filePath) {
|
||||
async function sendPublishFile(req, res, filePath, { isOwner = true } = {}) {
|
||||
if (!filePath.toLowerCase().endsWith('.html')) {
|
||||
res.sendFile(filePath, (err) => {
|
||||
if (err && !res.headersSent) res.status(404).json({ message: '文件不存在' });
|
||||
@@ -5788,6 +5788,7 @@ async function sendPublishFile(req, res, filePath) {
|
||||
const decorated = decorateMindSpacePublishedHtml({
|
||||
html,
|
||||
embed,
|
||||
isOwner,
|
||||
context,
|
||||
htmlFilePath: filePath,
|
||||
userAgent: req.get('user-agent') || '',
|
||||
@@ -5804,6 +5805,10 @@ async function sendPublishFile(req, res, filePath) {
|
||||
}
|
||||
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 now varies by viewer
|
||||
// (owner sees the Plaza entry, visitors don't), so it must never be cached/shared across
|
||||
// sessions. There is no CDN/proxy_cache in front of this route today; keep it that way.
|
||||
res.set('Cache-Control', 'private, no-store');
|
||||
res.send(html);
|
||||
}
|
||||
|
||||
@@ -5861,7 +5866,17 @@ async function serveUserPublishFile(req, res, next) {
|
||||
}
|
||||
}
|
||||
|
||||
await sendPublishFile(req, res, resolvedPath);
|
||||
// REGRESSION GUARD: mindspace-public-owner-vs-visitor — this workspace URL is the one the
|
||||
// agent hands back in chat and is reachable by anyone (no login required). Only the logged-in
|
||||
// author (viewer.id === ownerKey) may see the "发布 Plaza" entry; everyone else is a visitor.
|
||||
const viewer = req.userSession && userAuth
|
||||
? await userAuth.getMe(req.userToken).catch(() => null)
|
||||
: null;
|
||||
const isOwner = Boolean(
|
||||
viewer?.id && result.ownerKey && String(viewer.id).toLowerCase() === String(result.ownerKey).toLowerCase(),
|
||||
);
|
||||
|
||||
await sendPublishFile(req, res, resolvedPath, { isOwner });
|
||||
}
|
||||
|
||||
app.use('/temp', (req, res) => {
|
||||
|
||||
Reference in New Issue
Block a user