/** * Rota /api/sessions — lista e detalhe de sessões Claude Code. * Validação Zod em query params; detalhe carrega eventos via parseSessionFile. * @author Descomplicar® | Projecto Observabilidade (Espelho) */ import { Router } from 'express' import { z } from 'zod' import type { SessionsDb } from '../services/sessions/db.js' import { parseSessionFile } from '../services/sessions/parser.js' const ListQuerySchema = z.object({ days: z.coerce.number().int().min(1).max(3650).optional(), project: z.string().min(1).max(200).optional(), tool: z.string().min(1).max(100).optional(), skill: z.string().min(1).max(200).optional(), q: z.string().max(500).optional(), limit: z.coerce.number().int().min(1).max(200).default(50), offset: z.coerce.number().int().min(0).default(0), }) const IdParamSchema = z.object({ id: z.string().min(1).max(200) }) export function createSessionsRouter(db: SessionsDb): Router { const router = Router() router.get('/', (req, res) => { const parsed = ListQuerySchema.safeParse(req.query) if (!parsed.success) { return res.status(400).json({ error: 'Invalid query', details: parsed.error.format() }) } const filters = parsed.data const items = db.listSessions(filters) const total = db.countSessions(filters) return res.json({ total, items }) }) router.get('/:id', async (req, res) => { const parsed = IdParamSchema.safeParse(req.params) if (!parsed.success) { return res.status(400).json({ error: 'Invalid id' }) } const session = db.getSession(parsed.data.id) if (!session) return res.status(404).json({ error: 'Session not found' }) try { const { events } = await parseSessionFile(session.jsonl_path) return res.json({ meta: session, events }) } catch (err) { return res.status(500).json({ error: 'Failed to parse session', message: (err as Error).message }) } }) return router }