function assertRouter(api) { if ( !api || typeof api.get !== 'function' || typeof api.post !== 'function' ) { throw new Error( 'attachPortalMindSpaceSpaceRoutes requires an Express-compatible router', ); } } export function attachPortalMindSpaceSpaceRoutes( api, { getMindSpace = () => null, getScheduleService = () => null, getMindSpaceCleanup = () => null, getMindSpaceAudit = () => null, ensureMindSpaceEnabled = () => false, sendData = (res, _req, data) => res.json({ data }), sendError = (res, _req, status, code, message) => res.status(status).json({ error: { code, message } }), handleMindSpaceError = (_res, _req, error) => { throw error; }, } = {}, ) { assertRouter(api); api.get('/mindspace/v1/space', async (req, res) => { const mindSpace = getMindSpace(); if (!mindSpace || !ensureMindSpaceEnabled(res, req)) return; const space = await mindSpace.getSpace(req.currentUser.id); if (!space) { return sendError(res, req, 404, 'resource_not_found', '用户空间不存在'); } return sendData(res, req, space); }); api.post('/mindspace/v1/schedule/reminders/:reminderId/ignore', async (req, res) => { const scheduleService = getScheduleService(); if (!ensureMindSpaceEnabled(res, req) || !scheduleService) { return sendError(res, req, 503, 'feature_disabled', '日程服务未启用'); } try { const reminder = await scheduleService.cancelReminder({ userId: req.currentUser.id, reminderId: req.params.reminderId, }); return sendData(res, req, reminder); } catch (err) { const message = err instanceof Error ? err.message : '忽略提醒失败'; return sendError(res, req, 400, 'invalid_schedule_input', message); } }); api.post('/mindspace/v1/schedule/reminders/bulk-delete', async (req, res) => { const scheduleService = getScheduleService(); if (!ensureMindSpaceEnabled(res, req) || !scheduleService) { return sendError(res, req, 503, 'feature_disabled', '日程服务未启用'); } try { const ids = Array.isArray(req.body?.ids) ? req.body.ids.map(String) : []; const deleted = await scheduleService.deleteReminders({ userId: req.currentUser.id, reminderIds: ids, }); return sendData(res, req, { deleted }); } catch (err) { const message = err instanceof Error ? err.message : '删除提醒失败'; return sendError(res, req, 400, 'invalid_schedule_input', message); } }); api.get('/mindspace/v1/space/quota', async (req, res) => { const mindSpace = getMindSpace(); if (!mindSpace) return res.status(503).json({ message: 'MindSpace 未启用' }); const quota = await mindSpace.getQuota(req.currentUser.id); if (!quota) return res.status(404).json({ message: '用户空间不存在' }); return res.json({ data: quota }); }); api.get('/mindspace/v1/space/categories', async (req, res) => { const mindSpace = getMindSpace(); if (!mindSpace) return res.status(503).json({ message: 'MindSpace 未启用' }); const categories = await mindSpace.listCategories(req.currentUser.id); if (!categories) return res.status(404).json({ message: '用户空间不存在' }); return res.json({ data: categories }); }); api.get('/mindspace/v1/space/cleanup', async (req, res) => { const mindSpaceCleanup = getMindSpaceCleanup(); if (!mindSpaceCleanup || !ensureMindSpaceEnabled(res, req)) return; try { const username = req.currentUser.username ?? req.currentUser.slug; const items = await mindSpaceCleanup.listCandidates(req.currentUser.id, username); const totalBytes = items.reduce((sum, item) => sum + item.sizeBytes, 0); return sendData(res, req, { items, totalBytes }); } catch (error) { return handleMindSpaceError(res, req, error); } }); api.post('/mindspace/v1/space/cleanup', async (req, res) => { const mindSpaceCleanup = getMindSpaceCleanup(); if (!mindSpaceCleanup || !ensureMindSpaceEnabled(res, req)) return; try { const username = req.currentUser.username ?? req.currentUser.slug; const itemIds = Array.isArray(req.body?.item_ids) ? req.body.item_ids : []; const result = await mindSpaceCleanup.runCleanup( req.currentUser.id, username, itemIds, ); const quota = await getMindSpace().getQuota(req.currentUser.id); await getMindSpaceAudit()?.write({ userId: req.currentUser.id, action: 'space.cleanup', objectType: 'space', objectId: req.currentUser.id, ip: req.ip, detail: { removedCount: result.removedCount, freedBytes: result.freedBytes, }, }); return sendData(res, req, { ...result, quota }); } catch (error) { return handleMindSpaceError(res, req, error); } }); }