import { resolveSessionAccess } from './session-broker.mjs'; function defaultSendData(res, _req, data) { return res.json({ data }); } function defaultRouteError(res, _req, error) { return res.status(500).json({ message: error?.message || 'MindSpace 服务异常' }); } function resolveRegistry(input) { return typeof input === 'function' ? input() : input; } function resolveActiveSessionAccess({ sessionAccess, getSessionAccess, userAuth, getUserAuth }) { const explicit = resolveRegistry(getSessionAccess ?? sessionAccess); if (explicit) return explicit; const activeUserAuth = resolveRegistry(getUserAuth ?? userAuth); if (!activeUserAuth) return null; return resolveSessionAccess({ userAuth: activeUserAuth, enabled: false }); } async function assertConversationPackageSessionOwned(req, res, options) { const sessionId = String(req.params?.sessionId ?? '').trim(); if (!sessionId) { res.status(400).json({ message: '缺少 sessionId' }); return null; } const access = resolveActiveSessionAccess(options); if (!access) { res.status(503).json({ message: 'MindSpace 对话包未启用' }); return null; } if (!(await access.validateOwnership(req.currentUser.id, sessionId))) { res.status(403).json({ message: '无权访问该会话' }); return null; } return sessionId; } function encodeDownloadFilename(filename) { const safe = String(filename ?? '').trim() || 'mindspace-artifact'; return `attachment; filename="${encodeURIComponent(safe)}"; filename*=UTF-8''${encodeURIComponent(safe)}`; } function sanitizeDownloadFilenameBase(value, fallback = 'mindspace-conversation-package') { const normalized = String(value ?? '') .replace(/[\\/:*?"<>|]+/g, ' ') .replace(/\s+/g, ' ') .trim(); return normalized || fallback; } export function createGetConversationPackageHandler({ registry, getRegistry, userAuth, getUserAuth, sessionAccess, getSessionAccess, ensureMindSpaceEnabled = () => true, sendData = defaultSendData, mindSpaceError = defaultRouteError, beforeReadManifest, } = {}) { return async function getConversationPackage(req, res) { const activeRegistry = resolveRegistry(getRegistry ?? registry); if (!activeRegistry || !ensureMindSpaceEnabled(res, req)) { if (res.headersSent) return undefined; return res.status(503).json({ message: 'MindSpace 对话包未启用' }); } const sessionId = await assertConversationPackageSessionOwned(req, res, { sessionAccess, getSessionAccess, userAuth, getUserAuth, }); if (!sessionId) return undefined; try { if (typeof beforeReadManifest === 'function') { await beforeReadManifest({ req, userId: req.currentUser.id, sessionId }); } const manifest = await activeRegistry.readManifestForSession({ userId: req.currentUser.id, sessionId, }); if (!manifest) return res.status(404).json({ message: '对话包不存在' }); return sendData(res, req, manifest); } catch (error) { return mindSpaceError(res, req, error); } }; } export function createDownloadConversationPackageManifestHandler({ registry, getRegistry, userAuth, getUserAuth, sessionAccess, getSessionAccess, ensureMindSpaceEnabled = () => true, mindSpaceError = defaultRouteError, beforeReadManifest, } = {}) { return async function downloadConversationPackageManifest(req, res) { const activeRegistry = resolveRegistry(getRegistry ?? registry); if (!activeRegistry || !ensureMindSpaceEnabled(res, req)) { if (res.headersSent) return undefined; return res.status(503).json({ message: 'MindSpace 对话包未启用' }); } const sessionId = await assertConversationPackageSessionOwned(req, res, { sessionAccess, getSessionAccess, userAuth, getUserAuth, }); if (!sessionId) return undefined; try { if (typeof beforeReadManifest === 'function') { await beforeReadManifest({ req, userId: req.currentUser.id, sessionId }); } const manifest = await activeRegistry.readManifestForSession({ userId: req.currentUser.id, sessionId, }); if (!manifest) return res.status(404).json({ message: '对话包不存在' }); const filenameBase = sanitizeDownloadFilenameBase( manifest.displayFolderName ?? manifest.title, 'mindspace-conversation-package', ); const filename = `${filenameBase}-manifest.json`; res.set('Content-Type', 'application/json; charset=utf-8'); res.set('Content-Disposition', encodeDownloadFilename(filename)); res.set('Cache-Control', 'private, no-store'); if (req.requestId) res.setHeader('X-Request-Id', req.requestId); return res.send(`${JSON.stringify(manifest, null, 2)}\n`); } catch (error) { return mindSpaceError(res, req, error); } }; } export function createDownloadConversationPackageArtifactHandler({ registry, getRegistry, userAuth, getUserAuth, sessionAccess, getSessionAccess, ensureMindSpaceEnabled = () => true, mindSpaceError = defaultRouteError, } = {}) { return async function downloadConversationPackageArtifact(req, res) { const activeRegistry = resolveRegistry(getRegistry ?? registry); if (!activeRegistry || !ensureMindSpaceEnabled(res, req)) { if (res.headersSent) return undefined; return res.status(503).json({ message: 'MindSpace 对话包未启用' }); } const sessionId = await assertConversationPackageSessionOwned(req, res, { sessionAccess, getSessionAccess, userAuth, getUserAuth, }); if (!sessionId) return undefined; const artifactId = String(req.params?.artifactId ?? '').trim(); if (!artifactId) return res.status(400).json({ message: '缺少 artifactId' }); try { const result = await activeRegistry.readArtifactObject({ userId: req.currentUser.id, sessionId, artifactId, }); if (!result) return res.status(404).json({ message: '文件不存在' }); const filename = result.artifact.displayName || result.artifact.id; res.set('Content-Type', result.artifact.mimeType || 'application/octet-stream'); res.set('Content-Disposition', encodeDownloadFilename(filename)); res.set('Cache-Control', 'private, no-store'); if (req.requestId) res.setHeader('X-Request-Id', req.requestId); return res.send(result.body); } catch (error) { return mindSpaceError(res, req, error); } }; } export const conversationPackageRouteInternals = { encodeDownloadFilename, sanitizeDownloadFilenameBase, };