e101577d61
Task 5 do MVP Espelho: endpoint Express com factory createSessionsRouter(db) que expõe GET / (lista filtrável por days/project/tool/skill/q + limit/offset validados via Zod) e GET /:id (meta + eventos via parseSessionFile). Integrado em server.ts com DB aberta a partir de OBSERVABILIDADE_DB ?? DEFAULT_DB_PATH. Validação empírica: total=559 sessões (últimos 7d), detalhe com 37 eventos. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
55 lines
1.9 KiB
TypeScript
55 lines
1.9 KiB
TypeScript
/**
|
|
* 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
|
|
}
|