fix: MindSpace remote sync, thumbnail fallback, and Plaza URL guards

Ensure page sync runs via pageSyncService in remote mode, fall back to
workspace HTML when storage assets are missing, and prevent production
Portal from linking to loopback Plaza URLs baked in at build time.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
john
2026-07-05 07:57:04 +08:00
parent 4781bbc156
commit d3239ff292
20 changed files with 646 additions and 58 deletions
+52 -5
View File
@@ -309,6 +309,45 @@ export function createPageService(pool, options = {}) {
throw lastError ?? Object.assign(new Error('存储文件不存在'), { code: 'storage_not_found' });
};
// REGRESSION GUARD: mindspace-page-sync-thumbnail — storage ENOENT 时回退读 workspace HTML
const readPageContentWithWorkspaceFallback = async ({
userId,
storageKey,
sourceSnapshotJson,
pageType,
}) => {
let snapshot = {};
try {
snapshot = parseJsonColumn(sourceSnapshotJson);
} catch {
snapshot = {};
}
const workspaceHtmlRelativePath =
pageType === 'html' ? snapshot.relative_path ?? null : null;
const workspacePublishDir = workspaceHtmlRelativePath
? await resolveWorkspacePublishDir(userId)
: null;
try {
const storagePath = await resolveReadableStoragePath(storageKey);
return await fs.readFile(storagePath, 'utf8');
} catch (error) {
const storageMissing = error?.code === 'ENOENT' || error?.code === 'storage_not_found';
if (!storageMissing || !workspacePublishDir || !workspaceHtmlRelativePath) {
throw error;
}
const workspaceHtmlPath = path.join(workspacePublishDir, workspaceHtmlRelativePath);
try {
return await fs.readFile(workspaceHtmlPath, 'utf8');
} catch (workspaceError) {
if (workspaceError?.code === 'ENOENT') {
throw error;
}
throw workspaceError;
}
}
};
const writeVersionContent = async (userId, assetId, versionId, content) => {
const storageKey = path.posix.join(
'users',
@@ -668,7 +707,7 @@ export function createPageService(pool, options = {}) {
async function getPage(userId, pageId) {
const [rows] = await pool.query(
`SELECT p.*, c.category_code, pv.version_no, av.storage_key
`SELECT p.*, c.category_code, pv.version_no, av.storage_key, pv.source_snapshot_json
FROM h5_page_records p
JOIN h5_space_categories c ON c.id = p.category_id AND c.user_id = p.user_id
JOIN h5_page_versions pv ON pv.id = p.current_version_id
@@ -679,8 +718,12 @@ export function createPageService(pool, options = {}) {
);
const row = rows[0];
if (!row) throw pageError('页面不存在', 'page_not_found');
const storagePath = await resolveReadableStoragePath(row.storage_key);
const content = await fs.readFile(storagePath, 'utf8');
const content = await readPageContentWithWorkspaceFallback({
userId,
storageKey: row.storage_key,
sourceSnapshotJson: row.source_snapshot_json,
pageType: row.page_type,
});
return pageResponse({ ...row, content });
}
@@ -1265,8 +1308,12 @@ export function createPageService(pool, options = {}) {
if (row.page_type !== 'html') {
throw pageError('该页面不支持缩略图', 'thumbnail_not_supported');
}
const storagePath = await resolveReadableStoragePath(row.storage_key);
const content = await fs.readFile(storagePath, 'utf8');
const content = await readPageContentWithWorkspaceFallback({
userId,
storageKey: row.storage_key,
sourceSnapshotJson: row.source_snapshot_json,
pageType: row.page_type,
});
let snapshot = {};
try {
snapshot = parseJsonColumn(row.source_snapshot_json);