Files
memind/mindspace-workspace-page-deliver.mjs
T
john e3063ea806 fix(mindspace): refresh published HTML after workspace sync
Keep online publication snapshots aligned when public/*.html changes, default static page binds to public access with MindSpace delivery URLs, and split browser-safe workspace helpers out of the Vite import chain.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-10 20:44:04 +08:00

152 lines
5.5 KiB
JavaScript

import { slugFromPageTitle } from './mindspace-chat-plaza.mjs';
import { normalizeWorkspaceRelativePath } from './mindspace-pages.mjs';
function parseJsonColumn(value, fallback = {}) {
if (value == null || value === '') return { ...fallback };
if (typeof value === 'object' && !Buffer.isBuffer(value)) return value;
try {
return JSON.parse(String(value));
} catch {
return { ...fallback };
}
}
function isPublicWorkspaceHtmlPath(relativePath) {
const normalized = normalizeWorkspaceRelativePath(relativePath);
return Boolean(normalized?.startsWith('public/') && normalized.toLowerCase().endsWith('.html'));
}
export function createWorkspacePageDeliverService({
pool,
pageService,
publicationService,
pageSyncService,
logger = console,
} = {}) {
async function listUnpublishedAutoSyncedWorkspacePages(userId) {
if (!pool || !userId) return [];
const [rows] = await pool.query(
`SELECT p.id AS page_id, p.title, p.current_version_id, p.workspace_relative_path,
pv.source_snapshot_json
FROM h5_page_records p
JOIN h5_page_versions pv ON pv.id = p.current_version_id
LEFT JOIN h5_publish_records pr
ON pr.page_id = p.id AND pr.user_id = p.user_id AND pr.status = 'online'
WHERE p.user_id = ?
AND p.status <> 'deleted'
AND pr.id IS NULL`,
[userId],
);
return (rows ?? []).filter((row) => {
const snapshot = parseJsonColumn(row.source_snapshot_json);
const relativePath = row.workspace_relative_path ?? snapshot.relative_path ?? null;
if (!isPublicWorkspaceHtmlPath(relativePath)) return false;
return snapshot.auto_synced === true || snapshot.content_mode === 'static_html';
});
}
async function listOnlineAutoSyncedWorkspacePages(userId) {
if (!pool || !userId) return [];
const [rows] = await pool.query(
`SELECT p.id AS page_id, p.title, p.current_version_id, p.workspace_relative_path,
pv.source_snapshot_json
FROM h5_page_records p
JOIN h5_page_versions pv ON pv.id = p.current_version_id
JOIN h5_publish_records pr
ON pr.page_id = p.id AND pr.user_id = p.user_id AND pr.status = 'online'
WHERE p.user_id = ?
AND p.status <> 'deleted'`,
[userId],
);
return (rows ?? []).filter((row) => {
const snapshot = parseJsonColumn(row.source_snapshot_json);
const relativePath = row.workspace_relative_path ?? snapshot.relative_path ?? null;
if (!isPublicWorkspaceHtmlPath(relativePath)) return false;
return snapshot.auto_synced === true || snapshot.content_mode === 'static_html';
});
}
async function refreshOnlineWorkspacePublications(userId) {
if (!publicationService?.refreshOnlinePublicationHtml || !userId) {
return { refreshed: 0, skipped: 0, errors: [] };
}
const candidates = await listOnlineAutoSyncedWorkspacePages(userId);
let refreshed = 0;
let skipped = 0;
const errors = [];
for (const candidate of candidates) {
try {
const result = await publicationService.refreshOnlinePublicationHtml(
userId,
candidate.page_id,
);
if (result) refreshed += 1;
else skipped += 1;
} catch (error) {
errors.push({
pageId: candidate.page_id,
message: error instanceof Error ? error.message : String(error),
});
logger?.warn?.(
`[MindSpace] refresh publication failed for page ${candidate.page_id}:`,
error instanceof Error ? error.message : error,
);
}
}
return { refreshed, skipped, errors };
}
async function ensureWorkspaceHtmlPublications(userId) {
if (!publicationService?.publish || !pageService?.getPage || !userId) {
return { published: 0, skipped: 0, errors: [] };
}
const candidates = await listUnpublishedAutoSyncedWorkspacePages(userId);
let published = 0;
let skipped = 0;
const errors = [];
for (const candidate of candidates) {
try {
const page = await pageService.getPage(userId, candidate.page_id);
const current = await publicationService.getCurrent?.(userId, candidate.page_id);
if (current?.status === 'online') {
skipped += 1;
continue;
}
await publicationService.publish(userId, candidate.page_id, {
pageVersionId: page.currentVersionId,
accessMode: 'public',
urlSlug: slugFromPageTitle(page.title, candidate.page_id),
autoAcknowledgeFindings: true,
});
published += 1;
} catch (error) {
errors.push({
pageId: candidate.page_id,
message: error instanceof Error ? error.message : String(error),
});
logger?.warn?.(
`[MindSpace] auto publish failed for page ${candidate.page_id}:`,
error instanceof Error ? error.message : error,
);
}
}
return { published, skipped, errors };
}
async function syncAndDeliver(userId) {
let syncResult = { created: 0, updated: 0, skipped: 0 };
if (pageSyncService?.syncUserGeneratedPages) {
syncResult = await pageSyncService.syncUserGeneratedPages(userId);
}
const publishResult = await ensureWorkspaceHtmlPublications(userId);
const refreshResult = await refreshOnlineWorkspacePublications(userId);
return { sync: syncResult, publish: publishResult, refresh: refreshResult };
}
return {
syncAndDeliver,
ensureWorkspaceHtmlPublications,
refreshOnlineWorkspacePublications,
};
}