Record published pages in conversation packages
This commit is contained in:
@@ -296,6 +296,7 @@ export function createPublicationService(pool, options = {}) {
|
||||
const h5Root = options.h5Root ?? null;
|
||||
const idFactory = options.idFactory ?? (() => crypto.randomUUID());
|
||||
const publicPageLimitFallback = Number(options.publicPageLimit ?? 5);
|
||||
const conversationPackageRegistry = options.conversationPackageRegistry ?? null;
|
||||
|
||||
let imgproxySigner = null;
|
||||
if (process.env.IMGPROXY_BASE_URL && process.env.IMGPROXY_SIGNING_KEY && process.env.IMGPROXY_SIGNING_SALT) {
|
||||
@@ -362,6 +363,7 @@ export function createPublicationService(pool, options = {}) {
|
||||
const loadVersion = async (userId, pageId, pageVersionId) => {
|
||||
const [rows] = await pool.query(
|
||||
`SELECT p.id AS page_id, p.title, p.summary, p.page_type, p.template_id, p.current_version_id,
|
||||
p.source_session_id, p.source_message_id,
|
||||
p.user_id, p.space_id, pv.id AS page_version_id, pv.version_no,
|
||||
pv.bundle_asset_id, av.storage_key,
|
||||
JSON_UNQUOTE(JSON_EXTRACT(pv.source_snapshot_json, '$.relative_path')) AS source_relative_path
|
||||
@@ -729,7 +731,7 @@ export function createPublicationService(pool, options = {}) {
|
||||
}
|
||||
|
||||
await conn.commit();
|
||||
return {
|
||||
const publication = {
|
||||
...publicationResponse({
|
||||
id: publishId,
|
||||
page_id: pageId,
|
||||
@@ -745,6 +747,17 @@ export function createPublicationService(pool, options = {}) {
|
||||
}),
|
||||
findings: result.findings,
|
||||
};
|
||||
await registerPublicationArtifactForConversation({
|
||||
registry: conversationPackageRegistry,
|
||||
userId,
|
||||
page,
|
||||
publication,
|
||||
bundleAssetId,
|
||||
storageKey,
|
||||
htmlBytes,
|
||||
now,
|
||||
});
|
||||
return publication;
|
||||
} catch (error) {
|
||||
await conn.rollback();
|
||||
if (writtenPath) await fs.rm(writtenPath, { force: true }).catch(() => {});
|
||||
@@ -1046,6 +1059,53 @@ export function createPublicationService(pool, options = {}) {
|
||||
};
|
||||
}
|
||||
|
||||
async function registerPublicationArtifactForConversation({
|
||||
registry,
|
||||
userId,
|
||||
page,
|
||||
publication,
|
||||
bundleAssetId,
|
||||
storageKey,
|
||||
htmlBytes,
|
||||
now,
|
||||
}) {
|
||||
const sessionId = String(page?.source_session_id ?? '').trim();
|
||||
if (!registry || !sessionId || !publication?.id) return null;
|
||||
try {
|
||||
const packageRecord = await registry.ensurePackage({
|
||||
userId,
|
||||
sessionId,
|
||||
title: page.title,
|
||||
now,
|
||||
});
|
||||
const artifact = await registry.recordArtifact({
|
||||
id: `ca_${publication.id}`,
|
||||
packageId: packageRecord.id,
|
||||
artifactKind: 'public_html',
|
||||
role: 'assistant',
|
||||
assetId: bundleAssetId,
|
||||
pageId: page.page_id,
|
||||
publicationId: publication.id,
|
||||
messageId: page.source_message_id ?? null,
|
||||
displayName: `${page.title}.html`,
|
||||
mimeType: 'text/html',
|
||||
sizeBytes: htmlBytes,
|
||||
storageKey,
|
||||
canonicalUrl: publication.publicUrl,
|
||||
sortOrder: Number.isFinite(Number(publication.publishedAt)) ? Number(publication.publishedAt) : 0,
|
||||
now,
|
||||
});
|
||||
await registry.writeManifestForSession({ userId, sessionId });
|
||||
return artifact;
|
||||
} catch (error) {
|
||||
console.warn(
|
||||
'[MindSpace] conversation package publication artifact registration failed:',
|
||||
error instanceof Error ? error.message : error,
|
||||
);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export const publicationInternals = {
|
||||
normalizeSlug,
|
||||
normalizeAccessMode,
|
||||
@@ -1062,5 +1122,6 @@ export const publicationInternals = {
|
||||
rewriteWorkspacePublicAssetReferences,
|
||||
localizePrivateImageReferences,
|
||||
prepareHtmlPublishContent,
|
||||
registerPublicationArtifactForConversation,
|
||||
scanContent, // re-exported from mindspace-content-scan.mjs
|
||||
};
|
||||
|
||||
@@ -219,6 +219,74 @@ test('prepareHtmlPublishContent rewrites imgproxy local image urls to public sta
|
||||
assert.doesNotMatch(prepared, /plain\/local:\/\//);
|
||||
});
|
||||
|
||||
test('registerPublicationArtifactForConversation records public html artifact', 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 publicationInternals.registerPublicationArtifactForConversation({
|
||||
registry,
|
||||
userId: 'user-1',
|
||||
page: {
|
||||
page_id: 'page-1',
|
||||
title: '发布页',
|
||||
source_session_id: 'session-1',
|
||||
source_message_id: 'message-1',
|
||||
},
|
||||
publication: {
|
||||
id: 'pub-1',
|
||||
publicUrl: 'https://m.tkmind.cn/u/john/pages/published',
|
||||
publishedAt: 3000,
|
||||
},
|
||||
bundleAssetId: 'asset-pub',
|
||||
storageKey: 'users/user-1/publications/pub-1/index.html',
|
||||
htmlBytes: 256,
|
||||
now: 3000,
|
||||
});
|
||||
|
||||
assert.equal(result.id, 'ca_pub-1');
|
||||
assert.deepEqual(calls[0], [
|
||||
'ensurePackage',
|
||||
{
|
||||
userId: 'user-1',
|
||||
sessionId: 'session-1',
|
||||
title: '发布页',
|
||||
now: 3000,
|
||||
},
|
||||
]);
|
||||
assert.equal(calls[1][1].artifactKind, 'public_html');
|
||||
assert.equal(calls[1][1].publicationId, 'pub-1');
|
||||
assert.equal(calls[1][1].canonicalUrl, 'https://m.tkmind.cn/u/john/pages/published');
|
||||
assert.deepEqual(calls[2], [
|
||||
'writeManifestForSession',
|
||||
{ userId: 'user-1', sessionId: 'session-1' },
|
||||
]);
|
||||
});
|
||||
|
||||
test('registerPublicationArtifactForConversation skips pages without source session', async () => {
|
||||
assert.equal(
|
||||
await publicationInternals.registerPublicationArtifactForConversation({
|
||||
registry: { ensurePackage: async () => assert.fail('should not be called') },
|
||||
userId: 'user-1',
|
||||
page: { page_id: 'page-1', title: '发布页' },
|
||||
publication: { id: 'pub-1' },
|
||||
}),
|
||||
null,
|
||||
);
|
||||
});
|
||||
|
||||
test('prepareHtmlPublishContent rewrites imgproxy urls in srcset and css url contexts', async () => {
|
||||
const prepared = await publicationInternals.prepareHtmlPublishContent({
|
||||
pool: {
|
||||
|
||||
@@ -339,6 +339,7 @@ async function bootstrapUserAuth() {
|
||||
h5Root: __dirname,
|
||||
storageRoot: mindSpaceStorageRoot,
|
||||
publicPageLimit: Number(process.env.MINDSPACE_FREE_PUBLIC_PAGE_LIMIT ?? 5),
|
||||
conversationPackageRegistry,
|
||||
});
|
||||
setInterval(async () => {
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user