Files

302 lines
11 KiB
TypeScript

import { Router, Request, Response } from 'express'
import type { Knex } from 'knex'
import { ProtocolEngine } from './brain'
import type { PluginConfigStore } from '../../backend/src/core/plugin-config'
function uuid(): string {
try { return (crypto as any).randomUUID() } catch { return `${Date.now()}-${Math.random().toString(36).slice(2)}` }
}
export function createSecretariaRoutes(db: Knex, config: PluginConfigStore): Router {
const router = Router()
const brain = new ProtocolEngine(db, config)
// ── Agents ───────────────────────────────────────────────────────────────
router.get('/agents', async (_req: Request, res: Response) => {
try {
const agents = await db('sec_agents').orderBy('created_at')
res.json(agents)
} catch (err: any) {
res.status(500).json({ error: err.message })
}
})
router.post('/agents', async (req: Request, res: Response) => {
try {
const { name, description, model, provider, temperature, context_window } = req.body
const [agent] = await db('sec_agents').insert({
id: uuid(), name, description, model: model ?? 'gpt-4o-mini',
provider: provider ?? 'openai', temperature: temperature ?? 0.7,
context_window: context_window ?? 8, active: true,
}).returning('*')
res.status(201).json(agent)
} catch (err: any) {
res.status(500).json({ error: err.message })
}
})
router.put('/agents/:id', async (req: Request, res: Response) => {
try {
const { id } = req.params
const { name, description, model, provider, temperature, context_window, active } = req.body
const [agent] = await db('sec_agents').where({ id })
.update({ name, description, model, provider, temperature, context_window, active, updated_at: new Date() })
.returning('*')
res.json(agent)
} catch (err: any) {
res.status(500).json({ error: err.message })
}
})
router.delete('/agents/:id', async (req: Request, res: Response) => {
try {
await db('sec_agents').where({ id: req.params.id }).delete()
res.json({ ok: true })
} catch (err: any) {
res.status(500).json({ error: err.message })
}
})
// ── Brain Nodes ───────────────────────────────────────────────────────────
router.get('/agents/:agentId/nodes', async (req: Request, res: Response) => {
try {
const nodes = await db('sec_brain_nodes')
.where({ agent_id: req.params.agentId })
.orderBy('sort_order')
res.json(nodes)
} catch (err: any) {
res.status(500).json({ error: err.message })
}
})
router.post('/agents/:agentId/nodes', async (req: Request, res: Response) => {
try {
const { type, title, content, sort_order } = req.body
const [node] = await db('sec_brain_nodes').insert({
id: uuid(), agent_id: req.params.agentId,
type, title, content, active: true,
sort_order: sort_order ?? 99,
}).returning('*')
res.status(201).json(node)
} catch (err: any) {
res.status(500).json({ error: err.message })
}
})
router.put('/nodes/:id', async (req: Request, res: Response) => {
try {
const { title, content, active, sort_order } = req.body
const [node] = await db('sec_brain_nodes').where({ id: req.params.id })
.update({ title, content, active, sort_order, updated_at: new Date() })
.returning('*')
res.json(node)
} catch (err: any) {
res.status(500).json({ error: err.message })
}
})
router.delete('/nodes/:id', async (req: Request, res: Response) => {
try {
await db('sec_brain_nodes').where({ id: req.params.id }).delete()
res.json({ ok: true })
} catch (err: any) {
res.status(500).json({ error: err.message })
}
})
// ── Conversations ────────────────────────────────────────────────────────
router.get('/conversations', async (req: Request, res: Response) => {
try {
const { agent_id } = req.query
let q = db('sec_conversations').orderBy('updated_at', 'desc')
if (agent_id) q = q.where({ agent_id: agent_id as string })
const convs = await q
res.json(convs)
} catch (err: any) {
res.status(500).json({ error: err.message })
}
})
router.post('/conversations', async (req: Request, res: Response) => {
try {
const { agent_id, contact_name } = req.body
const [conv] = await db('sec_conversations').insert({
id: uuid(), agent_id,
contact_name: contact_name ?? 'Usuário Teste',
protocol_number: ProtocolEngine.generateProtocolNumber(),
status: 'active',
}).returning('*')
res.status(201).json(conv)
} catch (err: any) {
res.status(500).json({ error: err.message })
}
})
router.patch('/conversations/:id', async (req: Request, res: Response) => {
try {
const { status, contact_name } = req.body
const [conv] = await db('sec_conversations').where({ id: req.params.id })
.update({ status, contact_name, updated_at: new Date() }).returning('*')
res.json(conv)
} catch (err: any) {
res.status(500).json({ error: err.message })
}
})
router.delete('/conversations/:id', async (req: Request, res: Response) => {
try {
await db('sec_conversations').where({ id: req.params.id }).delete()
res.json({ ok: true })
} catch (err: any) {
res.status(500).json({ error: err.message })
}
})
// ── Messages ──────────────────────────────────────────────────────────────
router.get('/conversations/:id/messages', async (req: Request, res: Response) => {
try {
const messages = await db('sec_messages')
.where({ conversation_id: req.params.id })
.orderBy('created_at')
res.json(messages)
} catch (err: any) {
res.status(500).json({ error: err.message })
}
})
// Enviar mensagem → aciona o cérebro → retorna resposta da IA
router.post('/conversations/:id/chat', async (req: Request, res: Response) => {
try {
const { message } = req.body
if (!message?.trim()) return res.status(400).json({ error: 'message is required' })
const reply = await brain.chat(String(req.params.id), message.trim())
res.json({ reply })
} catch (err: any) {
res.status(500).json({ error: err.message })
}
})
// Finalizar protocolo → gera resumo completo → apaga mensagens → fecha conversa
router.post('/conversations/:id/finalize', async (req: Request, res: Response) => {
try {
const result = await brain.finalizeProtocol(String(req.params.id))
res.json(result)
} catch (err: any) {
res.status(500).json({ error: err.message })
}
})
// ── Calendar ──────────────────────────────────────────────────────────────
router.get('/calendar', async (req: Request, res: Response) => {
try {
const qs = (k: string) => { const v = req.query[k]; return v ? String(v) : undefined }
const from = qs('from'), to = qs('to'), status = qs('status')
let q = db('sec_calendar').orderBy('date').orderBy('time_start')
if (from) q = q.where('date', '>=', from)
if (to) q = q.where('date', '<=', to)
if (status) q = q.where({ status })
res.json(await q)
} catch (err: any) {
res.status(500).json({ error: err.message })
}
})
router.post('/calendar', async (req: Request, res: Response) => {
try {
const { title, date, time_start, time_end, attendee_name, attendee_phone, notes } = req.body
const [slot] = await db('sec_calendar').insert({
id: uuid(), title, date, time_start, time_end,
attendee_name, attendee_phone, notes, status: 'available',
}).returning('*')
res.status(201).json(slot)
} catch (err: any) {
res.status(500).json({ error: err.message })
}
})
router.put('/calendar/:id', async (req: Request, res: Response) => {
try {
const { title, date, time_start, time_end, attendee_name, attendee_phone, status, notes } = req.body
const [slot] = await db('sec_calendar').where({ id: req.params.id })
.update({ title, date, time_start, time_end, attendee_name, attendee_phone, status, notes, updated_at: new Date() })
.returning('*')
res.json(slot)
} catch (err: any) {
res.status(500).json({ error: err.message })
}
})
router.delete('/calendar/:id', async (req: Request, res: Response) => {
try {
await db('sec_calendar').where({ id: req.params.id }).delete()
res.json({ ok: true })
} catch (err: any) {
res.status(500).json({ error: err.message })
}
})
// ── Numbers ───────────────────────────────────────────────────────────────
router.get('/numbers', async (_req: Request, res: Response) => {
try {
const numbers = await db('sec_numbers').orderBy('priority').orderBy('label')
res.json(numbers)
} catch (err: any) {
res.status(500).json({ error: err.message })
}
})
router.post('/numbers', async (req: Request, res: Response) => {
try {
const { instance_id, label, role, area, priority, notes } = req.body
if (!instance_id) return res.status(400).json({ error: 'instance_id é obrigatório' })
// Upsert: se já existe um registro para esse instance_id, atualiza o role
const existing = await db('sec_numbers').where({ instance_id }).first()
if (existing) {
const [num] = await db('sec_numbers').where({ instance_id })
.update({ label, role: role ?? 'clinic', area: area ?? null, priority: priority ?? 10, notes: notes ?? null, updated_at: new Date() })
.returning('*')
return res.json(num)
}
const [num] = await db('sec_numbers').insert({
id: uuid(), instance_id, label, role: role ?? 'clinic',
area: area ?? null, priority: priority ?? 10, active: true, notes: notes ?? null,
}).returning('*')
res.status(201).json(num)
} catch (err: any) {
res.status(500).json({ error: err.message })
}
})
router.put('/numbers/:id', async (req: Request, res: Response) => {
try {
const { label, role, area, instance_id, priority, active, notes } = req.body
const [num] = await db('sec_numbers').where({ id: req.params.id })
.update({ label, role, area, instance_id, priority, active, notes, updated_at: new Date() })
.returning('*')
res.json(num)
} catch (err: any) {
res.status(500).json({ error: err.message })
}
})
router.delete('/numbers/:id', async (req: Request, res: Response) => {
try {
await db('sec_numbers').where({ id: req.params.id }).delete()
res.json({ ok: true })
} catch (err: any) {
res.status(500).json({ error: err.message })
}
})
return router
}