import { Router } from 'express'; import type { Repository } from '../db/repository.js'; import { spaceViewerOf, parseTzOffset, monthBounds } from './space-api.js'; // 横断(全スペース)カレンダー API。 // spec: docs/superpowers/specs/2026-06-19-space-calendar-design.md §V2 // // GET /api/calendar/cross?month=YYYY-MM&tz_offset= // → { days: {[date]: {[spaceId]: {taskCount,eventCount}}}, events, spaces } // // 可視性は Repository.getCrossSpaceCalendarMonth が listSpaces(buildVisibilityWhere) // 経由で絞るので、viewer が見られないスペースのカウント・イベントは一切返さない。 // 認可ミドルウェア(requireAuth / no-auth フォールバック)はマウント側(server.ts)で // 他のトップレベル API と同じパターンを付与する。 export interface CrossCalendarApiDeps { repo: Repository; authActive: boolean; } export function createCrossCalendarApi(deps: CrossCalendarApiDeps): Router { const router = Router(); const { repo } = deps; router.get('/cross', async (req, res) => { const viewer = spaceViewerOf(req, deps.authActive); const month = String(req.query.month ?? ''); if (!/^\d{4}-\d{2}$/.test(month)) { return res.status(400).json({ error: 'month must be YYYY-MM' }); } const tzOffsetMin = parseTzOffset(req.query.tz_offset); const { monthStart, monthEnd } = monthBounds(month); const result = await repo.getCrossSpaceCalendarMonth(viewer, { monthStart, monthEnd, tzOffsetMin, }); res.json(result); }); return router; }