mindspace: close authority boundaries

This commit is contained in:
john
2026-07-27 15:34:35 +08:00
parent dfab78c75a
commit e94052ff24
78 changed files with 11962 additions and 2162 deletions
+67 -118
View File
@@ -1,7 +1,5 @@
import express from 'express';
import crypto from 'node:crypto';
import fs from 'node:fs';
import fsPromises from 'node:fs/promises';
import { createProxyMiddleware } from 'http-proxy-middleware';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
@@ -73,13 +71,10 @@ import { attachPortalSessionRoutes } from './server/portal-session-routes.mjs';
import { attachPortalUserMemoryRoutes } from './server/portal-user-memory-routes.mjs';
import { assertMindSpaceRoute, mindspaceFlags } from './mindspace-flags.mjs';
import { normalizeWorkspaceRelativePath } from './mindspace-pages.mjs';
import { registerPublicHtmlArtifactsForConversationPackage } from './mindspace-conversation-package-public-html.mjs';
import {
buildMindSpacePublicUrlForUser,
resolveMindSpaceRuntimeConfig,
resolveMindSpacePublishRoot,
resolveMindSpaceServerRuntimeOptions,
resolveMindSpaceUserPublishDir,
resolvePortalH5Root,
} from './mindspace-runtime-config.mjs';
import {
@@ -92,15 +87,12 @@ import {
buildChatSaveThumbnailUrl,
buildWorkspaceAssetUrl,
buildWorkspaceThumbnailUrl,
materializePrivateAssetsInWorkspaceHtml,
repairMissingHtmlAssetReferences,
resolveChatSaveAnalysis,
} from './mindspace-chat-save.mjs';
import {
normalizePublicHtmlRelativePath,
} from './mindspace-public-finish-sync.mjs';
import { resolvePlazaPostPath, resolvePlazaPublicBase } from './src/utils/public-site-bases.mjs';
import { listRecentlyModifiedPublicHtmlRelativePaths } from './mindspace-run-public-html-scope.mjs';
import { DEFAULT_IMAGE_UPLOAD_MAX_BYTES } from './user-image-normalize.mjs';
import { loadWechatMpConfig } from './wechat-mp-config.mjs';
import {
@@ -290,6 +282,11 @@ let mindSpace = null;
let mindSpaceAssets = null;
let mindSpaceAudit = null;
let mindSpaceServiceFacade = null;
let mindSpaceChatSave = null;
let mindSpaceConversationArtifacts = null;
let mindSpacePublicFinish = null;
let mindSpaceWorkspacePublicationDelivery =
null;
let mindSpaceConversationPackageRegistry = null;
let mindSpacePages = null;
let mindSpacePageSync = null;
@@ -342,7 +339,6 @@ async function bootstrapUserAuth() {
onMindSearchSchemaReady: () => {
authPool = pool;
},
registerPublicHtmlArtifactsForConversation,
workspaceMaintenanceEnabled:
WORKSPACE_MAINTENANCE_ENABLED,
logger: console,
@@ -362,6 +358,15 @@ async function bootstrapUserAuth() {
mindSpace = domainServices.mindSpace;
mindSpaceServiceFacade =
domainServices.mindSpaceServiceFacade;
mindSpaceChatSave =
domainServices.mindSpaceChatSave;
mindSpaceConversationArtifacts =
domainServices.mindSpaceConversationArtifacts;
mindSpacePublicFinish =
domainServices.mindSpacePublicFinish;
mindSpaceWorkspacePublicationDelivery =
domainServices
.mindSpaceWorkspacePublicationDelivery;
mindSpaceConversationPackageRegistry =
domainServices.mindSpaceConversationPackageRegistry;
mindSpaceAssets = domainServices.mindSpaceAssets;
@@ -485,6 +490,8 @@ async function bootstrapUserAuth() {
memoryV2,
systemDisclosurePolicyService,
mindSpaceAssets,
getWorkspacePublicationDelivery: () =>
mindSpaceWorkspacePublicationDelivery,
directChatService,
chatIntentRouter,
syncUserGeneratedPages,
@@ -501,7 +508,7 @@ async function bootstrapUserAuth() {
env: process.env,
usersRoot: USERS_ROOT,
wechatMpConfig: WECHAT_MP_CONFIG,
authPool,
mindSpacePublicFinish,
userAuth,
sessionAccess,
tkmindProxy,
@@ -1115,12 +1122,18 @@ async function syncUserGeneratedPages(userId, { sessionId = null, sinceMs = null
// no conversation artifact. Discover only files written around this run;
// `null` would rescan every historical page and let stale Page Data pages
// block an unrelated delivery.
const recentWorkspaceRelativePaths = sessionId && !discoveredRelativePaths?.length
? listRecentlyModifiedPublicHtmlRelativePaths(
resolveMindSpaceUserPublishDir(H5_ROOT, { id: userId }),
{ sinceMs },
)
: [];
const recentWorkspaceRelativePaths =
sessionId &&
!discoveredRelativePaths?.length &&
mindSpaceWorkspacePublicationDelivery
? (
await mindSpaceWorkspacePublicationDelivery
.listRecentlyModifiedPublicHtml({
userId,
sinceMs,
})
)?.relativePaths ?? []
: [];
const pageDataRelativePaths = sessionId
? (discoveredRelativePaths?.length ? discoveredRelativePaths : recentWorkspaceRelativePaths)
: null;
@@ -1147,42 +1160,23 @@ async function resolveChatSaveBundle(user, h5Root, input = {}) {
selectedLinkIndex,
});
if (resolvedHtml?.content && resolvedHtml.relativePath && authPool) {
const { storageRoot } = resolveMindSpaceRuntimeConfig(h5Root, process.env);
let htmlContent = resolvedHtml.content;
const repaired = await repairMissingHtmlAssetReferences({
pool: authPool,
userId: user.id,
sourceContent: source.content,
html: htmlContent,
}).catch(() => ({ html: htmlContent, changed: false }));
if (repaired.changed) {
htmlContent = repaired.html;
}
const materialized = await materializePrivateAssetsInWorkspaceHtml({
pool: authPool,
storageRoot,
h5Root,
userId: user.id,
html: htmlContent,
htmlRelativePath: resolvedHtml.relativePath,
writeBack: false,
}).catch(() => ({ html: htmlContent, count: 0, changed: false }));
if (materialized.changed) {
htmlContent = materialized.html;
}
if (htmlContent !== resolvedHtml.content) {
const publishDir = resolveMindSpaceUserPublishDir(h5Root, { id: user.id });
await fsPromises.writeFile(
path.join(publishDir, resolvedHtml.relativePath),
htmlContent,
'utf8',
);
resolvedHtml = { ...resolvedHtml, content: htmlContent };
}
if (
resolvedHtml?.content &&
resolvedHtml.relativePath &&
mindSpaceChatSave
) {
const materialized =
await mindSpaceChatSave.materializeWorkspaceHtml({
userId: user.id,
sourceContent: source.content,
html: resolvedHtml.content,
relativePath: resolvedHtml.relativePath,
});
resolvedHtml = {
...resolvedHtml,
relativePath: materialized.relativePath,
content: materialized.html,
};
}
return {
@@ -1228,7 +1222,7 @@ async function registerPublishedLongImageArtifactForConversation({
const messageId = String(pageSource.sourceMessageId ?? '').trim();
const publicationId = String(result?.publication?.id ?? '').trim();
if (
!mindSpaceConversationPackageRegistry ||
!mindSpaceConversationArtifacts ||
!ownerId ||
!sessionId ||
!publicationId ||
@@ -1237,77 +1231,34 @@ async function registerPublishedLongImageArtifactForConversation({
return;
}
try {
const hash = crypto
.createHash('sha256')
.update(`${ownerId}:${sessionId}:${publicationId}:long-image`)
.digest('hex')
.slice(0, 16);
const artifactId = `ca_long_image_${hash}`;
const filename = `${publicationId || 'published-page'}.long.png`;
const relativePath = `artifacts/${artifactId}/${filename}`;
const now = Date.now();
const { packageRecord, writeResult } =
await mindSpaceConversationPackageRegistry.putObjectForSession({
userId: ownerId,
await mindSpaceConversationArtifacts
.registerPublishedLongImageArtifact({
ownerId,
sessionId,
messageId: messageId || null,
publicationId,
pageId:
pageSource.pageId ??
result?.publication?.pageId ??
null,
title: pageSource.title ?? null,
relativePath,
body: image,
canonicalUrl,
});
await mindSpaceConversationPackageRegistry.recordArtifact({
id: artifactId,
packageId: packageRecord.id,
artifactKind: 'long_image',
role: 'assistant',
pageId: pageSource.pageId ?? result?.publication?.pageId ?? null,
publicationId,
messageId: messageId || null,
displayName: filename,
mimeType: 'image/png',
sizeBytes: image.length,
storageKey: writeResult.key,
canonicalUrl,
sortOrder: now,
now,
});
await mindSpaceConversationPackageRegistry.writeManifestForSession({
userId: ownerId,
sessionId,
});
} catch (error) {
console.warn('[MindSpace] failed to record published long image artifact:', error?.message ?? error);
}
}
async function registerPublicHtmlArtifactsForConversation({
user,
publishDir,
sessionId,
title = null,
relativePaths = [],
artifactRefs = [],
}) {
return registerPublicHtmlArtifactsForConversationPackage({
conversationPackageRegistry: mindSpaceConversationPackageRegistry,
h5Root: H5_ROOT,
env: process.env,
user,
publishDir,
sessionId,
title,
relativePaths,
artifactRefs,
});
}
attachPortalMindSpaceChatSaveRoutes(api, {
h5Root: H5_ROOT,
env: process.env,
getMindSpacePages: () => mindSpacePages,
getMindSpaceAssets: () => mindSpaceAssets,
getMindSpaceChatSave: () => mindSpaceChatSave,
getConversationArtifactService: () =>
mindSpaceConversationArtifacts,
getAuthPool: () => authPool,
getConversationPackageRegistry: () =>
mindSpaceConversationPackageRegistry,
resolveChatSaveBundle,
resolveExistingSavedPage,
resolveOwnedAssistantMessage,
@@ -1317,11 +1268,10 @@ attachPortalMindSpaceChatSaveRoutes(api, {
attachPortalMindSpaceChatShareRoutes(api, {
h5Root: H5_ROOT,
env: process.env,
getMindSpacePages: () => mindSpacePages,
getMindSpaceChatSave: () => mindSpaceChatSave,
getMindSpacePublications: () => mindSpacePublications,
getPlazaPosts: () => plazaPosts,
getAuthPool: () => authPool,
resolveChatSaveBundle,
resolvePlazaPostUrl: resolvePlazaPostUrlForRequest,
mapPlazaError,
@@ -1406,25 +1356,21 @@ attachPortalAgentRuntimeRoutes(api, {
});
attachPortalSessionRoutes(api, {
h5Root: H5_ROOT,
env: process.env,
waitForUserAuthReady: () => userAuthReady,
getUserAuth: () => userAuth,
getTkmindProxy: () => tkmindProxy,
getSessionSnapshotService: () => sessionSnapshotService,
getAuthPool: () => authPool,
getMindSpaceAssets: () => mindSpaceAssets,
getMindSpacePublicFinish: () =>
mindSpacePublicFinish,
getMemoryV2: () => memoryV2,
getMindSpaceAnalyticsConfig: () => mindSpaceAnalyticsConfig,
getMindSpaceRybbitConfig: () => mindSpaceRybbitConfig,
isWorkspaceMaintenanceEnabled: () =>
WORKSPACE_MAINTENANCE_ENABLED,
ownsAgentSession,
unregisterAgentSessionForUser,
beginSessionPageDelivery,
endSessionPageDelivery,
syncUserGeneratedPages,
registerPublicHtmlArtifactsForConversation,
logger: console,
});
@@ -1517,7 +1463,6 @@ app.use('/rybbit', createProxyMiddleware({
}));
const { serveUserPublishFile } =
createPortalWorkspacePublicationDelivery({
h5Root: H5_ROOT,
internalAgentSecret:
INTERNAL_AGENT_SECRET,
analyticsConfig:
@@ -1527,6 +1472,8 @@ const { serveUserPublishFile } =
getAuthPool: () => authPool,
getUserAuth: () => userAuth,
getMindSpacePages: () => mindSpacePages,
getWorkspacePublicationDelivery: () =>
mindSpaceWorkspacePublicationDelivery,
resolveRequestOrigin,
removeQueryParam,
logger: console,
@@ -1557,6 +1504,8 @@ attachPortalPublicationRoutes({
getMindSpacePages: () => mindSpacePages,
getMindSpacePublications: () =>
mindSpacePublications,
getWorkspacePublicationDelivery: () =>
mindSpaceWorkspacePublicationDelivery,
getUserAuth: () => userAuth,
sendPublishedPage,
});