66 lines
2.2 KiB
TypeScript
66 lines
2.2 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) {
|
|
const e = err as NodeJS.ErrnoException
|
|
if (e.code === 'ENOENT') {
|
|
return res.status(410).json({
|
|
error: 'Session file missing (stale index)',
|
|
session_id: parsed.data.id,
|
|
})
|
|
}
|
|
const isProduction = process.env.NODE_ENV === 'production'
|
|
return res.status(500).json({
|
|
error: 'Failed to parse session',
|
|
...(isProduction ? {} : { message: e.message }),
|
|
})
|
|
}
|
|
})
|
|
|
|
return router
|
|
}
|