Record page drafts in conversation packages
This commit is contained in:
+63
-1
@@ -257,6 +257,7 @@ export function createPageService(pool, options = {}) {
|
||||
const storageRoot = path.resolve(options.storageRoot ?? path.join(process.cwd(), 'data', 'mindspace'));
|
||||
const h5Root = options.h5Root ? path.resolve(options.h5Root) : null;
|
||||
const idFactory = options.idFactory ?? (() => crypto.randomUUID());
|
||||
const conversationPackageRegistry = options.conversationPackageRegistry ?? null;
|
||||
|
||||
const resolveWorkspacePublishDir = async (userId) => {
|
||||
if (!h5Root) return null;
|
||||
@@ -553,7 +554,20 @@ export function createPageService(pool, options = {}) {
|
||||
}).catch(() => {});
|
||||
});
|
||||
}
|
||||
return getPage(userId, pageId);
|
||||
const page = await getPage(userId, pageId);
|
||||
await registerPageArtifactForConversation({
|
||||
registry: conversationPackageRegistry,
|
||||
userId,
|
||||
source,
|
||||
page,
|
||||
contentAssetId,
|
||||
contentMimeType,
|
||||
contentBytes: normalized.contentBytes,
|
||||
storageKey: stored.storageKey,
|
||||
versionNo: nextVersionNo,
|
||||
now,
|
||||
});
|
||||
return page;
|
||||
} catch (error) {
|
||||
await conn.rollback();
|
||||
if (writtenPath) await fs.rm(writtenPath, { force: true }).catch(() => {});
|
||||
@@ -1438,6 +1452,53 @@ export function createPageService(pool, options = {}) {
|
||||
};
|
||||
}
|
||||
|
||||
async function registerPageArtifactForConversation({
|
||||
registry,
|
||||
userId,
|
||||
source,
|
||||
page,
|
||||
contentAssetId,
|
||||
contentMimeType,
|
||||
contentBytes,
|
||||
storageKey,
|
||||
versionNo,
|
||||
now,
|
||||
}) {
|
||||
const sessionId = String(source?.sessionId ?? '').trim();
|
||||
if (!registry || !sessionId || !page?.id) return null;
|
||||
try {
|
||||
const packageRecord = await registry.ensurePackage({
|
||||
userId,
|
||||
sessionId,
|
||||
title: page.title,
|
||||
now,
|
||||
});
|
||||
const artifact = await registry.recordArtifact({
|
||||
id: `ca_${page.id}_${page.currentVersionId}`,
|
||||
packageId: packageRecord.id,
|
||||
artifactKind: 'page',
|
||||
role: 'assistant',
|
||||
assetId: contentAssetId,
|
||||
pageId: page.id,
|
||||
messageId: source.messageId ?? page.sourceMessageId ?? null,
|
||||
displayName: page.title,
|
||||
mimeType: contentMimeType,
|
||||
sizeBytes: contentBytes,
|
||||
storageKey,
|
||||
sortOrder: Number.isFinite(Number(versionNo)) ? Number(versionNo) : 0,
|
||||
now,
|
||||
});
|
||||
await registry.writeManifestForSession({ userId, sessionId });
|
||||
return artifact;
|
||||
} catch (error) {
|
||||
console.warn(
|
||||
'[MindSpace] conversation package page artifact registration failed:',
|
||||
error instanceof Error ? error.message : error,
|
||||
);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export const pageInternals = {
|
||||
escapeHtml,
|
||||
normalizePageInput,
|
||||
@@ -1449,6 +1510,7 @@ export const pageInternals = {
|
||||
previewContentSecurityPolicy,
|
||||
isHtmlPage,
|
||||
renderPublicationHtml,
|
||||
registerPageArtifactForConversation,
|
||||
redactContent,
|
||||
buildPageDeleteSummary,
|
||||
};
|
||||
|
||||
@@ -132,3 +132,78 @@ test('normalizeWorkspaceRelativePath unifies bare html filenames under public/',
|
||||
assert.equal(normalizeWorkspaceRelativePath('public/ai-robot-report.html'), 'public/ai-robot-report.html');
|
||||
assert.equal(normalizeWorkspaceRelativePath('/public/demo.html'), 'public/demo.html');
|
||||
});
|
||||
|
||||
test('registerPageArtifactForConversation records page artifact and refreshes manifest', async () => {
|
||||
const calls = [];
|
||||
const registry = {
|
||||
async ensurePackage(input) {
|
||||
calls.push(['ensurePackage', input]);
|
||||
return { id: 'cp_session-1' };
|
||||
},
|
||||
async recordArtifact(input) {
|
||||
calls.push(['recordArtifact', input]);
|
||||
return input;
|
||||
},
|
||||
async writeManifestForSession(input) {
|
||||
calls.push(['writeManifestForSession', input]);
|
||||
return { storageKey: 'users/user-1/conversations/session-1/manifest.json' };
|
||||
},
|
||||
};
|
||||
|
||||
const result = await pageInternals.registerPageArtifactForConversation({
|
||||
registry,
|
||||
userId: 'user-1',
|
||||
source: { sessionId: 'session-1', messageId: 'message-1' },
|
||||
page: {
|
||||
id: 'page-1',
|
||||
currentVersionId: 'version-1',
|
||||
title: '旅行计划',
|
||||
sourceMessageId: 'message-fallback',
|
||||
},
|
||||
contentAssetId: 'asset-1',
|
||||
contentMimeType: 'text/html',
|
||||
contentBytes: 128,
|
||||
storageKey: 'users/user-1/pages/page-1/versions/v1.html',
|
||||
versionNo: 1,
|
||||
now: 1000,
|
||||
});
|
||||
|
||||
assert.equal(result.id, 'ca_page-1_version-1');
|
||||
assert.deepEqual(calls[0], [
|
||||
'ensurePackage',
|
||||
{
|
||||
userId: 'user-1',
|
||||
sessionId: 'session-1',
|
||||
title: '旅行计划',
|
||||
now: 1000,
|
||||
},
|
||||
]);
|
||||
assert.equal(calls[1][1].artifactKind, 'page');
|
||||
assert.equal(calls[1][1].messageId, 'message-1');
|
||||
assert.equal(calls[1][1].storageKey, 'users/user-1/pages/page-1/versions/v1.html');
|
||||
assert.deepEqual(calls[2], [
|
||||
'writeManifestForSession',
|
||||
{ userId: 'user-1', sessionId: 'session-1' },
|
||||
]);
|
||||
});
|
||||
|
||||
test('registerPageArtifactForConversation skips missing registry or session safely', async () => {
|
||||
assert.equal(
|
||||
await pageInternals.registerPageArtifactForConversation({
|
||||
registry: null,
|
||||
userId: 'user-1',
|
||||
source: { sessionId: 'session-1' },
|
||||
page: { id: 'page-1' },
|
||||
}),
|
||||
null,
|
||||
);
|
||||
assert.equal(
|
||||
await pageInternals.registerPageArtifactForConversation({
|
||||
registry: { ensurePackage: async () => assert.fail('should not be called') },
|
||||
userId: 'user-1',
|
||||
source: {},
|
||||
page: { id: 'page-1' },
|
||||
}),
|
||||
null,
|
||||
);
|
||||
});
|
||||
|
||||
+17
-6
@@ -42,6 +42,10 @@ import { createMindSpaceService, DEFAULT_MAX_FILE_BYTES } from './mindspace.mjs'
|
||||
import { ensureMindSpaceConfig } from './mindspace-config.mjs';
|
||||
import { createAssetService } from './mindspace-assets.mjs';
|
||||
import { createPageService, pageInternals, inlinePrivateAssetsInHtml, normalizeWorkspaceRelativePath } from './mindspace-pages.mjs';
|
||||
import { createConversationPackageRegistry } from './mindspace-conversation-package-registry.mjs';
|
||||
import { createConversationPackageStore } from './mindspace-conversation-package-store.mjs';
|
||||
import { createMindSpaceServiceFacade } from './mindspace-service.mjs';
|
||||
import { createLocalMindSpaceStorageAdapter } from './mindspace-storage-adapter.mjs';
|
||||
import { createPageLiveEditService } from './mindspace-page-live-edit.mjs';
|
||||
import { createAssetAgentService } from './mindspace-asset-agent.mjs';
|
||||
import { createPageEditSessionService } from './mindspace-page-edit-session.mjs';
|
||||
@@ -297,16 +301,24 @@ async function bootstrapUserAuth() {
|
||||
monthlyViewLimit: Number(process.env.MINDSPACE_FREE_MONTHLY_VIEW_LIMIT ?? 1000),
|
||||
scheduleService,
|
||||
});
|
||||
const mindSpaceStorageRoot =
|
||||
process.env.MINDSPACE_STORAGE_ROOT ?? path.join(__dirname, 'data', 'mindspace');
|
||||
const conversationPackageRegistry = createConversationPackageRegistry({
|
||||
store: createConversationPackageStore(pool),
|
||||
service: createMindSpaceServiceFacade({
|
||||
storageAdapter: createLocalMindSpaceStorageAdapter(mindSpaceStorageRoot),
|
||||
publicBaseUrl: resolvePublicBaseUrl(),
|
||||
}),
|
||||
});
|
||||
mindSpaceAssets = createAssetService(pool, {
|
||||
h5Root: __dirname,
|
||||
storageRoot:
|
||||
process.env.MINDSPACE_STORAGE_ROOT ?? path.join(__dirname, 'data', 'mindspace'),
|
||||
storageRoot: mindSpaceStorageRoot,
|
||||
maxFileBytes: Number(process.env.MINDSPACE_MAX_FILE_BYTES ?? DEFAULT_MAX_FILE_BYTES),
|
||||
});
|
||||
mindSpacePages = createPageService(pool, {
|
||||
h5Root: __dirname,
|
||||
storageRoot:
|
||||
process.env.MINDSPACE_STORAGE_ROOT ?? path.join(__dirname, 'data', 'mindspace'),
|
||||
storageRoot: mindSpaceStorageRoot,
|
||||
conversationPackageRegistry,
|
||||
});
|
||||
const resolveUserIdForAgentSession = async (sessionId) => {
|
||||
const [rows] = await pool.query(
|
||||
@@ -325,8 +337,7 @@ async function bootstrapUserAuth() {
|
||||
});
|
||||
mindSpacePublications = createPublicationService(pool, {
|
||||
h5Root: __dirname,
|
||||
storageRoot:
|
||||
process.env.MINDSPACE_STORAGE_ROOT ?? path.join(__dirname, 'data', 'mindspace'),
|
||||
storageRoot: mindSpaceStorageRoot,
|
||||
publicPageLimit: Number(process.env.MINDSPACE_FREE_PUBLIC_PAGE_LIMIT ?? 5),
|
||||
});
|
||||
setInterval(async () => {
|
||||
|
||||
Reference in New Issue
Block a user