Files
memind/server/portal-mindspace-page-core-routes.mjs

419 lines
12 KiB
JavaScript

function assertRouter(api) {
if (
!api ||
typeof api.get !== 'function' ||
typeof api.post !== 'function' ||
typeof api.put !== 'function' ||
typeof api.delete !== 'function'
) {
throw new Error(
'attachPortalMindSpacePageCoreRoutes requires an Express-compatible router',
);
}
}
export function attachPortalMindSpacePageCoreRoutes(
api,
{
getMindSpacePages = () => null,
getMindSpacePublications = () => null,
getMindSpace = () => null,
getMindSpaceAudit = () => null,
getMindSpacePageLiveEdit = () => null,
getMindSpacePageEditSession = () => null,
getUserAuth = () => null,
syncUserGeneratedPages,
ownsAgentSession,
ensureMindSpaceEnabled,
sendData,
sendError,
handleMindSpaceError,
} = {},
) {
assertRouter(api);
if (
typeof syncUserGeneratedPages !== 'function' ||
typeof ownsAgentSession !== 'function' ||
typeof ensureMindSpaceEnabled !== 'function' ||
typeof sendData !== 'function' ||
typeof sendError !== 'function' ||
typeof handleMindSpaceError !== 'function'
) {
throw new Error(
'attachPortalMindSpacePageCoreRoutes requires page route dependencies',
);
}
api.post('/mindspace/v1/pages', async (req, res) => {
const pages = getMindSpacePages();
if (!pages) {
return res.status(503).json({ message: 'MindSpace 未启用' });
}
try {
const page = await pages.createPage(req.currentUser.id, {
title: req.body?.title,
summary: req.body?.summary,
content: req.body?.content,
contentFormat:
req.body?.content_format === 'html' ? 'html' : undefined,
templateId: req.body?.template_id,
pageType: req.body?.page_type,
categoryCode: req.body?.category_code,
});
return res.status(201).json({ data: page });
} catch (error) {
return handleMindSpaceError(res, req, error);
}
});
api.get('/mindspace/v1/pages', async (req, res) => {
const pages = getMindSpacePages();
if (!pages) {
return res.status(503).json({ message: 'MindSpace 未启用' });
}
try {
await syncUserGeneratedPages(req.currentUser.id);
const limit = Number.parseInt(String(req.query.limit ?? ''), 10);
const offset = Number.parseInt(String(req.query.offset ?? ''), 10);
const result = await pages.listPages(req.currentUser.id, {
status:
typeof req.query.status === 'string'
? req.query.status
: undefined,
categoryCode:
typeof req.query.category_code === 'string'
? req.query.category_code
: undefined,
...(Number.isFinite(limit) ? { limit } : {}),
...(Number.isFinite(offset) ? { offset } : {}),
});
return res.json({
data: result.items,
page: {
total: result.total,
limit: result.limit,
offset: result.offset,
has_more: result.hasMore,
next_cursor: null,
},
});
} catch (error) {
return handleMindSpaceError(res, req, error);
}
});
api.get('/mindspace/v1/pages/:pageId', async (req, res) => {
const pages = getMindSpacePages();
if (!pages) {
return res.status(503).json({ message: 'MindSpace 未启用' });
}
try {
const [page, versions, publication] = await Promise.all([
pages.getPage(req.currentUser.id, req.params.pageId),
pages.listVersions(req.currentUser.id, req.params.pageId),
getMindSpacePublications()?.getCurrent(
req.currentUser.id,
req.params.pageId,
) ?? null,
]);
return res.json({
data: {
...page,
versions,
publication,
},
});
} catch (error) {
return handleMindSpaceError(res, req, error);
}
});
api.delete('/mindspace/v1/pages/:pageId', async (req, res) => {
const pages = getMindSpacePages();
if (!pages || !ensureMindSpaceEnabled(res, req)) return;
try {
const removeFromPlaza =
String(
req.query?.remove_from_plaza ??
req.body?.remove_from_plaza ??
'',
).toLowerCase() === 'true' ||
req.query?.remove_from_plaza === '1' ||
req.body?.remove_from_plaza === true;
const result = await pages.deletePage(
req.currentUser.id,
req.params.pageId,
{ removeFromPlaza },
);
const quota = await getMindSpace().getQuota(req.currentUser.id);
await getMindSpaceAudit()?.write({
userId: req.currentUser.id,
action: 'page.delete',
objectType: 'page',
objectId: req.params.pageId,
ip: req.ip,
detail: {
offlinedPublicationCount:
result.offlinedPublicationCount,
hiddenPlazaPostCount: result.hiddenPlazaPostCount,
deletedAssetCount: result.deletedAssetCount,
freedBytes: result.freedBytes,
},
});
return sendData(res, req, { ...result, quota });
} catch (error) {
return handleMindSpaceError(res, req, error);
}
});
api.get(
'/mindspace/v1/pages/:pageId/delete-preview',
async (req, res) => {
const pages = getMindSpacePages();
if (!pages || !ensureMindSpaceEnabled(res, req)) return;
try {
return sendData(
res,
req,
await pages.getDeletePreview(
req.currentUser.id,
req.params.pageId,
),
);
} catch (error) {
return handleMindSpaceError(res, req, error);
}
},
);
api.put('/mindspace/v1/pages/:pageId', async (req, res) => {
const pages = getMindSpacePages();
if (!pages) {
return res.status(503).json({ message: 'MindSpace 未启用' });
}
try {
const page = await pages.updatePage(
req.currentUser.id,
req.params.pageId,
{
expectedVersion: req.body?.expected_version,
title: req.body?.title,
summary: req.body?.summary,
content: req.body?.content,
templateId: req.body?.template_id,
pageType: req.body?.page_type,
changeNote: req.body?.change_note,
},
);
return res.json({ data: page });
} catch (error) {
return handleMindSpaceError(res, req, error);
}
});
api.post(
'/mindspace/v1/pages/:pageId/live-edit/bind',
async (req, res) => {
const pageLiveEdit = getMindSpacePageLiveEdit();
const pages = getMindSpacePages();
if (!pageLiveEdit || !pages) {
return res.status(503).json({ message: 'MindSpace 未启用' });
}
try {
const sessionId = String(req.body?.session_id ?? '').trim();
if (!sessionId) {
return sendError(
res,
req,
400,
'invalid_request',
'缺少 session_id',
);
}
const owns = await ownsAgentSession(
req.currentUser.id,
sessionId,
);
if (!owns) {
return sendError(
res,
req,
403,
'forbidden',
'无权绑定该 Agent 会话',
);
}
await pages.getPage(
req.currentUser.id,
req.params.pageId,
);
const parentSessionId =
String(req.body?.parent_session_id ?? '').trim() || null;
return sendData(
res,
req,
pageLiveEdit.bindSession({
userId: req.currentUser.id,
sessionId,
pageId: req.params.pageId,
parentSessionId,
}),
);
} catch (error) {
return handleMindSpaceError(res, req, error);
}
},
);
api.post(
'/mindspace/v1/pages/:pageId/live-edit/fork-session',
async (req, res) => {
const pageEditSession = getMindSpacePageEditSession();
if (!pageEditSession || !getMindSpacePages()) {
return res.status(503).json({ message: 'MindSpace 未启用' });
}
try {
const parentSessionId = String(
req.body?.parent_session_id ?? '',
).trim();
if (!parentSessionId) {
return sendError(
res,
req,
400,
'invalid_request',
'缺少 parent_session_id',
);
}
const gate = await getUserAuth().canUseChat(
req.currentUser.id,
);
if (!gate.ok) {
return sendError(
res,
req,
402,
gate.code ?? 'insufficient_balance',
gate.message,
);
}
return sendData(
res,
req,
await pageEditSession.forkSession({
userId: req.currentUser.id,
pageId: req.params.pageId,
parentSessionId,
h5ApiBase:
String(
req.body?.h5_api_base ??
req.body?.h5ApiBase ??
'',
).trim() || null,
}),
);
} catch (error) {
if (error?.code === 'forbidden') {
return sendError(
res,
req,
403,
error.code,
error.message,
);
}
if (error?.code === 'invalid_request') {
return sendError(
res,
req,
400,
error.code,
error.message,
);
}
return handleMindSpaceError(res, req, error);
}
},
);
api.post(
'/mindspace/v1/pages/:pageId/live-edit/close-session',
async (req, res) => {
const pageEditSession = getMindSpacePageEditSession();
if (!pageEditSession || !getMindSpacePages()) {
return res.status(503).json({ message: 'MindSpace 未启用' });
}
try {
const sessionId = String(req.body?.session_id ?? '').trim();
if (!sessionId) {
return sendError(
res,
req,
400,
'invalid_request',
'缺少 session_id',
);
}
return sendData(
res,
req,
await pageEditSession.closeSession({
userId: req.currentUser.id,
pageId: req.params.pageId,
sessionId,
parentSessionId:
String(req.body?.parent_session_id ?? '').trim() ||
null,
summary: String(req.body?.summary ?? ''),
}),
);
} catch (error) {
if (
error?.code === 'forbidden' ||
error?.code === 'page_binding_mismatch'
) {
return sendError(
res,
req,
403,
error.code,
error.message,
);
}
if (error?.code === 'invalid_request') {
return sendError(
res,
req,
400,
error.code,
error.message,
);
}
return handleMindSpaceError(res, req, error);
}
},
);
api.get(
'/mindspace/v1/pages/:pageId/live-edit/revision',
async (req, res) => {
const pageLiveEdit = getMindSpacePageLiveEdit();
if (!pageLiveEdit || !getMindSpacePages()) {
return res.status(503).json({ message: 'MindSpace 未启用' });
}
try {
return sendData(
res,
req,
await pageLiveEdit.getRevisionSnapshot(
req.currentUser.id,
req.params.pageId,
),
);
} catch (error) {
return handleMindSpaceError(res, req, error);
}
},
);
}