e3063ea806
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>
152 lines
5.5 KiB
JavaScript
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,
|
|
};
|
|
}
|