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:
@@ -97,6 +97,7 @@ test('handleMindSpaceLongImageDownload falls back to local html path without pag
|
||||
test('decorateMindSpacePublishedHtml returns decorated html and csp', () => {
|
||||
const pageScript = "document.body.dataset.ready='1'";
|
||||
const shareScript = "document.body.dataset.share='1'";
|
||||
let sharedIsOwner;
|
||||
const result = decorateMindSpacePublishedHtml({
|
||||
html: `<html><body><script>${pageScript}</script>demo</body></html>`,
|
||||
embed: false,
|
||||
@@ -110,10 +111,13 @@ test('decorateMindSpacePublishedHtml returns decorated html and csp', () => {
|
||||
preparePublicationHtmlForEmbed: (value) => value,
|
||||
injectOgTags: (value, meta) => `${value}<!--og:${meta.pageUrl}-->`,
|
||||
injectWechatShareBridge: (value, meta) => `${value}<!--wechat:${meta.pageUrl}-->`,
|
||||
injectPublicFileShareButton: (value) => ({
|
||||
html: value.replace('</body>', `<script>${shareScript}</script></body>`),
|
||||
scriptHashes: ['ignored'],
|
||||
}),
|
||||
injectPublicFileShareButton: (value, options) => {
|
||||
sharedIsOwner = options?.isOwner;
|
||||
return {
|
||||
html: value.replace('</body>', `<script>${shareScript}</script></body>`),
|
||||
scriptHashes: ['ignored'],
|
||||
};
|
||||
},
|
||||
publishedPageCsp: (_value, options) => JSON.stringify(options),
|
||||
isWechatUserAgent: () => true,
|
||||
});
|
||||
@@ -121,12 +125,34 @@ test('decorateMindSpacePublishedHtml returns decorated html and csp', () => {
|
||||
assert.match(result.html, /og:/);
|
||||
assert.match(result.html, /wechat:/);
|
||||
assert.match(result.html, /dataset\.share/);
|
||||
assert.match(result.html, /data-mindspace-image-retry="1"/);
|
||||
assert.equal(result.allowEmbedFrame, false);
|
||||
assert.equal(sharedIsOwner, true);
|
||||
const options = JSON.parse(result.csp);
|
||||
assert.equal(options.embed, false);
|
||||
assert.equal(options.wechatShare, true);
|
||||
assert.deepEqual(options.scriptHashes, collectInlineScriptHashes(result.html));
|
||||
assert.equal(options.scriptHashes.length, 2);
|
||||
assert.equal(options.scriptHashes.length, 3);
|
||||
});
|
||||
|
||||
test('decorateMindSpacePublishedHtml forwards isOwner=false to the share button injector', () => {
|
||||
let sharedIsOwner;
|
||||
decorateMindSpacePublishedHtml({
|
||||
html: '<html><body>demo</body></html>',
|
||||
embed: false,
|
||||
isOwner: false,
|
||||
context: { origin: '', pageUrl: '', pageDirUrl: '', fallbackImageUrl: '' },
|
||||
preparePublicationHtmlForEmbed: (value) => value,
|
||||
injectOgTags: (value) => value,
|
||||
injectWechatShareBridge: (value) => value,
|
||||
injectPublicFileShareButton: (value, options) => {
|
||||
sharedIsOwner = options?.isOwner;
|
||||
return { html: value, scriptHashes: [] };
|
||||
},
|
||||
publishedPageCsp: (_value, options) => JSON.stringify(options),
|
||||
isWechatUserAgent: () => false,
|
||||
});
|
||||
assert.equal(sharedIsOwner, false);
|
||||
});
|
||||
|
||||
test('decorateMindSpacePublishedHtml supports embed mode without share injection', () => {
|
||||
|
||||
Reference in New Issue
Block a user