fix(plaza): delegate thumbnail regen to chatSave authority boundary
Memind CI / Test, build, and release guards (push) Successful in 3m19s

Portal chat-share routes must not resolve workspace publish dirs directly;
quick-plaza flows now call mindSpaceChatSave.ensurePreviewThumbnail instead.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
john
2026-08-29 16:18:06 +08:00
parent ed910a68ca
commit ac2c87be43
3 changed files with 26 additions and 36 deletions
+21 -21
View File
@@ -255,8 +255,7 @@ export async function quickPlazaFromChat({
mindSpacePages, mindSpacePages,
mindSpacePublications, mindSpacePublications,
plazaPosts, plazaPosts,
ensureWorkspaceHtmlThumbnail, mindSpaceChatSave,
publishDir,
}) { }) {
if (!mindSpacePages || !mindSpacePublications || !plazaPosts) { if (!mindSpacePages || !mindSpacePublications || !plazaPosts) {
throw Object.assign(new Error('Plaza 或 MindSpace 未启用'), { code: 'plaza_unavailable' }); throw Object.assign(new Error('Plaza 或 MindSpace 未启用'), { code: 'plaza_unavailable' });
@@ -267,29 +266,26 @@ export async function quickPlazaFromChat({
userId: user.id, userId: user.id,
bundle, bundle,
mindSpacePages, mindSpacePages,
ensureWorkspaceHtmlThumbnail,
publishDir,
body, body,
skipThumbnail: !ensureWorkspaceHtmlThumbnail || !publishDir, skipThumbnail: true,
}); });
if ( if (
analysis.contentMode === 'static_html' && analysis.contentMode === 'static_html' &&
analysis.relativePath && analysis.relativePath &&
resolvedHtml?.content && resolvedHtml?.content &&
ensureWorkspaceHtmlThumbnail && mindSpaceChatSave?.ensurePreviewThumbnail
publishDir
) { ) {
await ensureWorkspaceHtmlThumbnail( await mindSpaceChatSave
publishDir, .ensurePreviewThumbnail({
analysis.relativePath, userId: user.id,
resolvedHtml.content, relativePath: analysis.relativePath,
{ html: resolvedHtml.content,
title: page.title, title: page.title,
subtitle: page.summary, subtitle: page.summary,
force: true, force: true,
}, })
).catch(() => {}); .catch(() => {});
} }
if (analysis.contentMode === 'static_html' && analysis.relativePath) { if (analysis.contentMode === 'static_html' && analysis.relativePath) {
@@ -394,8 +390,7 @@ export async function quickPlazaFromPublicHtml({
mindSpacePublications, mindSpacePublications,
plazaPosts, plazaPosts,
readWorkspaceHtml, readWorkspaceHtml,
ensureWorkspaceHtmlThumbnail, mindSpaceChatSave,
publishDir,
}) { }) {
if (!mindSpacePages || !mindSpacePublications || !plazaPosts) { if (!mindSpacePages || !mindSpacePublications || !plazaPosts) {
throw Object.assign(new Error('Plaza 或 MindSpace 未启用'), { code: 'plaza_unavailable' }); throw Object.assign(new Error('Plaza 或 MindSpace 未启用'), { code: 'plaza_unavailable' });
@@ -421,11 +416,16 @@ export async function quickPlazaFromPublicHtml({
}); });
const title = titleFromPublicHtml(content, normalized); const title = titleFromPublicHtml(content, normalized);
if (ensureWorkspaceHtmlThumbnail && publishDir) { if (mindSpaceChatSave?.ensurePreviewThumbnail) {
await ensureWorkspaceHtmlThumbnail(publishDir, normalized, content, { await mindSpaceChatSave
title, .ensurePreviewThumbnail({
force: true, userId: user.id,
}).catch(() => {}); relativePath: normalized,
html: content,
title,
force: true,
})
.catch(() => {});
} }
const page = await ensureWorkspaceHtmlPageForPlaza({ const page = await ensureWorkspaceHtmlPageForPlaza({
+4 -11
View File
@@ -3,8 +3,6 @@ import {
quickPlazaFromChat, quickPlazaFromChat,
quickPlazaFromPublicHtml, quickPlazaFromPublicHtml,
} from '../mindspace-chat-plaza.mjs'; } from '../mindspace-chat-plaza.mjs';
import { resolveMindSpaceUserPublishDir } from '../mindspace-runtime-config.mjs';
import { ensureWorkspaceHtmlThumbnail } from '../mindspace-workspace-thumbnails.mjs';
function assertRouter(api) { function assertRouter(api) {
if ( if (
@@ -43,8 +41,6 @@ export function attachPortalMindSpaceChatShareRoutes(
getQuickPlazaFromPublicHtmlStatusFn = getQuickPlazaFromPublicHtmlStatusFn =
getQuickPlazaFromPublicHtmlStatus, getQuickPlazaFromPublicHtmlStatus,
quickPlazaFromPublicHtmlFn = quickPlazaFromPublicHtml, quickPlazaFromPublicHtmlFn = quickPlazaFromPublicHtml,
ensureWorkspaceHtmlThumbnailFn = ensureWorkspaceHtmlThumbnail,
resolveMindSpaceUserPublishDirFn = resolveMindSpaceUserPublishDir,
now = Date.now, now = Date.now,
logger = console, logger = console,
} = {}, } = {},
@@ -100,9 +96,10 @@ export function attachPortalMindSpaceChatShareRoutes(
api.post('/mindspace/v1/pages/quick-plaza-from-chat', async (req, res) => { api.post('/mindspace/v1/pages/quick-plaza-from-chat', async (req, res) => {
const pages = getMindSpacePages(); const pages = getMindSpacePages();
const chatSave = getMindSpaceChatSave();
const publications = getMindSpacePublications(); const publications = getMindSpacePublications();
const plazaPosts = getPlazaPosts(); const plazaPosts = getPlazaPosts();
if (!pages || !publications || !plazaPosts) { if (!pages || !chatSave || !publications || !plazaPosts) {
return sendError( return sendError(
res, res,
req, req,
@@ -118,7 +115,6 @@ export function attachPortalMindSpaceChatShareRoutes(
h5Root, h5Root,
req.body, req.body,
); );
const publishDir = resolveMindSpaceUserPublishDirFn(h5Root, req.currentUser);
const result = await quickPlazaFromChatFn({ const result = await quickPlazaFromChatFn({
user: req.currentUser, user: req.currentUser,
bundle, bundle,
@@ -126,8 +122,7 @@ export function attachPortalMindSpaceChatShareRoutes(
mindSpacePages: pages, mindSpacePages: pages,
mindSpacePublications: publications, mindSpacePublications: publications,
plazaPosts, plazaPosts,
ensureWorkspaceHtmlThumbnail: ensureWorkspaceHtmlThumbnailFn, mindSpaceChatSave: chatSave,
publishDir,
}); });
logger.info('[quick-plaza] ok', { logger.info('[quick-plaza] ok', {
ms: now() - startedAt, ms: now() - startedAt,
@@ -221,7 +216,6 @@ export function attachPortalMindSpaceChatShareRoutes(
req.body?.relativePath ?? req.body?.relativePath ??
'', '',
).trim(); ).trim();
const publishDir = resolveMindSpaceUserPublishDirFn(h5Root, req.currentUser);
const result = await quickPlazaFromPublicHtmlFn({ const result = await quickPlazaFromPublicHtmlFn({
user: req.currentUser, user: req.currentUser,
relativePath, relativePath,
@@ -230,8 +224,7 @@ export function attachPortalMindSpaceChatShareRoutes(
plazaPosts, plazaPosts,
readWorkspaceHtml: (input) => readWorkspaceHtml: (input) =>
chatSave.readWorkspaceHtml(input), chatSave.readWorkspaceHtml(input),
ensureWorkspaceHtmlThumbnail: ensureWorkspaceHtmlThumbnailFn, mindSpaceChatSave: chatSave,
publishDir,
}); });
const plazaUrl = resolvePlazaPostUrl( const plazaUrl = resolvePlazaPostUrl(
result.post.id, result.post.id,
@@ -1,8 +1,6 @@
import assert from 'node:assert/strict'; import assert from 'node:assert/strict';
import test from 'node:test'; import test from 'node:test';
import { attachPortalMindSpaceChatShareRoutes } from './portal-mindspace-chat-share-routes.mjs'; import { attachPortalMindSpaceChatShareRoutes } from './portal-mindspace-chat-share-routes.mjs';
import { resolveMindSpaceUserPublishDir } from '../mindspace-runtime-config.mjs';
import { ensureWorkspaceHtmlThumbnail } from '../mindspace-workspace-thumbnails.mjs';
function createRouterRecorder() { function createRouterRecorder() {
const routes = new Map(); const routes = new Map();
@@ -196,8 +194,7 @@ test('quick Plaza from chat preserves dependencies, timing, result, and errors',
mindSpacePages: dependencies.services.pages, mindSpacePages: dependencies.services.pages,
mindSpacePublications: dependencies.services.publications, mindSpacePublications: dependencies.services.publications,
plazaPosts: dependencies.services.plazaPosts, plazaPosts: dependencies.services.plazaPosts,
ensureWorkspaceHtmlThumbnail, mindSpaceChatSave: dependencies.services.chatSave,
publishDir: resolveMindSpaceUserPublishDir('/workspace', req.currentUser),
}); });
const failureApi = createRouterRecorder(); const failureApi = createRouterRecorder();