Record chat docx artifacts in packages

This commit is contained in:
john
2026-07-02 21:43:38 +08:00
parent 0e00ed52cd
commit b38dd5961f
5 changed files with 269 additions and 3 deletions
+75 -2
View File
@@ -43,7 +43,10 @@ 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 { createGetConversationPackageHandler } from './mindspace-conversation-package-routes.mjs';
import {
createDownloadConversationPackageArtifactHandler,
createGetConversationPackageHandler,
} from './mindspace-conversation-package-routes.mjs';
import { createConversationPackageStore } from './mindspace-conversation-package-store.mjs';
import { createMindSpaceServiceFacade } from './mindspace-service.mjs';
import { createLocalMindSpaceStorageAdapter } from './mindspace-storage-adapter.mjs';
@@ -2496,6 +2499,16 @@ api.get('/mindspace/v1/conversation-packages/:sessionId', createGetConversationP
mindSpaceError,
}));
api.get(
'/mindspace/v1/conversation-packages/:sessionId/artifacts/:artifactId/download',
createDownloadConversationPackageArtifactHandler({
getRegistry: () => mindSpaceConversationPackageRegistry,
getUserAuth: () => userAuth,
ensureMindSpaceEnabled,
mindSpaceError,
}),
);
api.post('/mindspace/v1/uploads', async (req, res) => {
if (!mindSpaceAssets || !ensureMindSpaceEnabled(res, req, { upload: true })) return;
try {
@@ -2874,6 +2887,7 @@ async function resolveExistingSavedPage(userId, { sessionId, messageId, relative
}
const SAVE_TARGET_CATEGORIES = new Set(['draft', 'oa', 'public']);
const DOCX_MIME_TYPE = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document';
const pageSyncInFlight = new Map();
@@ -2993,6 +3007,58 @@ async function resolveChatSaveBundle(user, h5Root, input = {}) {
};
}
async function registerChatDocxArtifactForConversation({
user,
bundle,
requestBody = {},
filename,
buffer,
}) {
if (!mindSpaceConversationPackageRegistry || !user?.id || !filename || !Buffer.isBuffer(buffer)) return;
const sessionId = String(requestBody.session_id ?? requestBody.sessionId ?? '').trim();
const messageId = String(requestBody.message_id ?? requestBody.messageId ?? '').trim();
if (!sessionId || !messageId) return;
try {
const selectedLinkIndex = Number(requestBody.selected_link_index ?? requestBody.selectedLinkIndex ?? 0);
const hash = crypto
.createHash('sha256')
.update(`${user.id}:${sessionId}:${messageId}:${selectedLinkIndex}:${filename}`)
.digest('hex')
.slice(0, 16);
const artifactId = `ca_docx_${hash}`;
const relativePath = `artifacts/${artifactId}/${filename}`;
const { packageRecord, writeResult } =
await mindSpaceConversationPackageRegistry.putObjectForSession({
userId: user.id,
sessionId,
title: bundle?.source?.session?.name ?? null,
relativePath,
body: buffer,
});
const canonicalUrl =
`/api/mindspace/v1/conversation-packages/${encodeURIComponent(sessionId)}` +
`/artifacts/${encodeURIComponent(artifactId)}/download`;
await mindSpaceConversationPackageRegistry.recordArtifact({
id: artifactId,
packageId: packageRecord.id,
artifactKind: 'docx',
messageId,
displayName: filename,
mimeType: DOCX_MIME_TYPE,
sizeBytes: buffer.length,
storageKey: writeResult.key,
canonicalUrl,
sortOrder: Date.now(),
});
await mindSpaceConversationPackageRegistry.writeManifestForSession({
userId: user.id,
sessionId,
});
} catch (error) {
console.warn('[MindSpace] failed to record chat docx artifact:', error?.message ?? error);
}
}
api.get('/mindspace/v1/pages/chat-save-preview', async (req, res) => {
if (!mindSpacePages) return res.status(503).json({ message: 'MindSpace 未启用' });
try {
@@ -3177,7 +3243,14 @@ async function handleChatSaveDocx(req, res) {
.replace(/^-+|-+$/g, '')
.slice(0, 60) || 'mindspace-document';
const filename = `${filenameBase}.docx`;
res.set('Content-Type', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document');
await registerChatDocxArtifactForConversation({
user: req.currentUser,
bundle,
requestBody: req.body,
filename,
buffer,
});
res.set('Content-Type', DOCX_MIME_TYPE);
res.set(
'Content-Disposition',
`attachment; filename="${encodeURIComponent(filename)}"; filename*=UTF-8''${encodeURIComponent(filename)}`,