Files
memind/server/portal-mindspace-chat-share-routes.mjs
2026-07-27 15:34:35 +08:00

269 lines
7.4 KiB
JavaScript

import {
getQuickPlazaFromPublicHtmlStatus,
quickPlazaFromChat,
quickPlazaFromPublicHtml,
} from '../mindspace-chat-plaza.mjs';
function assertRouter(api) {
if (
!api ||
typeof api.get !== 'function' ||
typeof api.post !== 'function'
) {
throw new Error(
'attachPortalMindSpaceChatShareRoutes requires an Express-compatible router',
);
}
}
export function attachPortalMindSpaceChatShareRoutes(
api,
{
h5Root = '',
getMindSpacePages = () => null,
getMindSpaceChatSave = () => null,
getMindSpacePublications = () => null,
getPlazaPosts = () => null,
resolveChatSaveBundle,
resolvePlazaPostUrl,
mapPlazaError = () => 500,
sendData = (res, _req, data, status = 200) =>
res.status(status).json({ data }),
sendError = (res, _req, status, code, message) =>
res.status(status).json({ error: { code, message } }),
handlePlazaError = (_res, _req, error) => {
throw error;
},
handleMindSpaceError = (_res, _req, error) => {
throw error;
},
quickPlazaFromChatFn = quickPlazaFromChat,
getQuickPlazaFromPublicHtmlStatusFn =
getQuickPlazaFromPublicHtmlStatus,
quickPlazaFromPublicHtmlFn = quickPlazaFromPublicHtml,
now = Date.now,
logger = console,
} = {},
) {
assertRouter(api);
if (
typeof resolveChatSaveBundle !== 'function' ||
typeof resolvePlazaPostUrl !== 'function'
) {
throw new Error(
'attachPortalMindSpaceChatShareRoutes requires chat and Plaza resolvers',
);
}
api.post('/mindspace/v1/pages/quick-share-from-chat', async (req, res) => {
const chatSave = getMindSpaceChatSave();
if (!getMindSpacePages() || !chatSave) {
return res.status(503).json({ message: 'MindSpace 未启用' });
}
try {
const bundle = await resolveChatSaveBundle(
req.currentUser,
h5Root,
req.body,
);
logger.log(
'[quick-share] bundle resolved, hasHtml:',
Boolean(bundle.resolvedHtml),
);
if (!bundle.resolvedHtml) {
throw Object.assign(new Error('无法读取链接页面内容'), {
code: 'static_page_not_found',
});
}
const shared = await chatSave.createSharedHtml({
userId: req.currentUser.id,
html: bundle.resolvedHtml.content,
sourceFilename: bundle.resolvedHtml.filename,
});
return res.status(201).json({
data: {
publicUrl: shared.publicUrl,
filename: shared.filename,
},
});
} catch (error) {
logger.error('[quick-share] error:', error);
return handleMindSpaceError(res, req, error);
}
});
api.post('/mindspace/v1/pages/quick-plaza-from-chat', async (req, res) => {
const pages = getMindSpacePages();
const publications = getMindSpacePublications();
const plazaPosts = getPlazaPosts();
if (!pages || !publications || !plazaPosts) {
return sendError(
res,
req,
503,
'plaza_unavailable',
'Plaza 或 MindSpace 未启用',
);
}
const startedAt = now();
try {
const bundle = await resolveChatSaveBundle(
req.currentUser,
h5Root,
req.body,
);
const result = await quickPlazaFromChatFn({
user: req.currentUser,
bundle,
body: req.body ?? {},
mindSpacePages: pages,
mindSpacePublications: publications,
plazaPosts,
});
logger.info('[quick-plaza] ok', {
ms: now() - startedAt,
pageId: result.pageId,
publicationId: result.publicationId,
postId: result.post?.id,
});
return sendData(res, req, result, 201);
} catch (error) {
logger.error('[quick-plaza] error:', {
ms: now() - startedAt,
error,
});
if (error?.code && mapPlazaError(error) !== 500) {
return handlePlazaError(res, req, error);
}
return handleMindSpaceError(res, req, error);
}
});
api.get(
'/mindspace/v1/pages/quick-plaza-from-public-html/status',
async (req, res) => {
const pages = getMindSpacePages();
const publications = getMindSpacePublications();
const plazaPosts = getPlazaPosts();
if (!pages || !publications || !plazaPosts) {
return sendError(
res,
req,
503,
'plaza_unavailable',
'Plaza 或 MindSpace 未启用',
);
}
try {
const relativePath = String(
req.query?.relative_path ??
req.query?.relativePath ??
'',
).trim();
const result =
await getQuickPlazaFromPublicHtmlStatusFn({
user: req.currentUser,
relativePath,
mindSpacePages: pages,
mindSpacePublications: publications,
plazaPosts,
});
const postId = result.post?.id;
return sendData(res, req, {
...result,
plaza_url: postId
? resolvePlazaPostUrl(postId, req)
: null,
});
} catch (error) {
if (error?.code && mapPlazaError(error) !== 500) {
return handlePlazaError(res, req, error);
}
return handleMindSpaceError(res, req, error);
}
},
);
api.post(
'/mindspace/v1/pages/quick-plaza-from-public-html',
async (req, res) => {
const pages = getMindSpacePages();
const chatSave = getMindSpaceChatSave();
const publications = getMindSpacePublications();
const plazaPosts = getPlazaPosts();
if (
!pages ||
!chatSave ||
!publications ||
!plazaPosts
) {
return sendError(
res,
req,
503,
'plaza_unavailable',
'Plaza 或 MindSpace 未启用',
);
}
const startedAt = now();
try {
const relativePath = String(
req.body?.relative_path ??
req.body?.relativePath ??
'',
).trim();
const result = await quickPlazaFromPublicHtmlFn({
user: req.currentUser,
relativePath,
mindSpacePages: pages,
mindSpacePublications: publications,
plazaPosts,
readWorkspaceHtml: (input) =>
chatSave.readWorkspaceHtml(input),
});
const plazaUrl = resolvePlazaPostUrl(
result.post.id,
req,
);
logger.info('[quick-plaza-public-html] ok', {
ms: now() - startedAt,
pageId: result.pageId,
publicationId: result.publicationId,
postId: result.post?.id,
relativePath,
});
return sendData(
res,
req,
{
...result,
plaza_url: plazaUrl,
},
201,
);
} catch (error) {
logger.error('[quick-plaza-public-html] error:', {
ms: now() - startedAt,
error,
});
if (error?.code === 'ALREADY_PUBLISHED') {
const postId =
error?.details?.post_id ?? error?.details?.postId;
if (postId) {
error.details = {
...(error.details ?? {}),
plaza_url: resolvePlazaPostUrl(postId, req),
};
}
}
if (error?.code && mapPlazaError(error) !== 500) {
return handlePlazaError(res, req, error);
}
return handleMindSpaceError(res, req, error);
}
},
);
}