Files
scoreodonto.com/backend/dist/modules/chatbot/chatbot.routes.js
T

203 lines
9.5 KiB
JavaScript

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.buildChatbotRoutes = buildChatbotRoutes;
/**
* chatbot.routes.ts — CRUD de credenciais e bots IA por instância.
*
* Montado em /api/chatbot (ver server.ts)
* Todos os endpoints requerem authMiddleware.
*/
const express_1 = require("express");
const zod_1 = require("zod");
const prisma_1 = require("../../infra/database/prisma");
const chatbot_repository_1 = require("./chatbot.repository");
const logger_1 = require("../../config/logger");
function buildChatbotRoutes() {
const router = (0, express_1.Router)();
// ── Helpers ──────────────────────────────────────────────────────────────
const validateInstance = async (instanceId, tenantId) => {
return prisma_1.prisma.instance.findFirst({ where: { id: instanceId, tenantId } });
};
// ─────────────────────────────────────────────────────────────────────────
// CREDENTIALS
// ─────────────────────────────────────────────────────────────────────────
// GET /api/chatbot/credentials/:instanceId
router.get('/credentials/:instanceId', async (req, res) => {
try {
const tenantId = req.tenantId;
const instanceId = req.params['instanceId'];
if (!await validateInstance(instanceId, tenantId)) {
res.status(404).json({ error: 'Instância não encontrada' });
return;
}
const creds = await chatbot_repository_1.credentialRepo.findAll(tenantId, instanceId);
res.json(creds);
}
catch (err) {
logger_1.logger.error({ err }, 'Erro ao listar credenciais');
res.status(500).json({ error: err.message });
}
});
// POST /api/chatbot/credentials/:instanceId
router.post('/credentials/:instanceId', async (req, res) => {
const schema = zod_1.z.object({
name: zod_1.z.string().min(1).max(80),
apiKey: zod_1.z.string().min(10),
provider: zod_1.z.enum(['GEMINI', 'OPENAI']).default('GEMINI'),
});
try {
const tenantId = req.tenantId;
const instanceId = req.params['instanceId'];
if (!await validateInstance(instanceId, tenantId)) {
res.status(404).json({ error: 'Instância não encontrada' });
return;
}
const body = schema.parse(req.body);
const cred = await chatbot_repository_1.credentialRepo.create({ tenantId, instanceId, ...body });
res.status(201).json(cred);
}
catch (err) {
if (err instanceof zod_1.z.ZodError) {
res.status(400).json({ error: err.errors });
return;
}
logger_1.logger.error({ err }, 'Erro ao criar credencial');
res.status(500).json({ error: err.message });
}
});
// DELETE /api/chatbot/credentials/:instanceId/:credId
router.delete('/credentials/:instanceId/:credId', async (req, res) => {
try {
const tenantId = req.tenantId;
const credId = req.params['credId'];
await chatbot_repository_1.credentialRepo.delete(credId, tenantId);
res.status(204).send();
}
catch (err) {
logger_1.logger.error({ err }, 'Erro ao deletar credencial');
res.status(500).json({ error: err.message });
}
});
// ─────────────────────────────────────────────────────────────────────────
// BOTS
// ─────────────────────────────────────────────────────────────────────────
// GET /api/chatbot/bots/:instanceId
router.get('/bots/:instanceId', async (req, res) => {
try {
const tenantId = req.tenantId;
const instanceId = req.params['instanceId'];
if (!await validateInstance(instanceId, tenantId)) {
res.status(404).json({ error: 'Instância não encontrada' });
return;
}
const bots = await chatbot_repository_1.botRepo.findAll(tenantId, instanceId);
res.json(bots);
}
catch (err) {
logger_1.logger.error({ err }, 'Erro ao listar bots');
res.status(500).json({ error: err.message });
}
});
// POST /api/chatbot/bots/:instanceId
router.post('/bots/:instanceId', async (req, res) => {
const schema = zod_1.z.object({
credentialId: zod_1.z.string().uuid(),
name: zod_1.z.string().min(1).max(80),
systemPrompt: zod_1.z.string().min(10),
model: zod_1.z.string().default('gemini-1.5-flash'),
enabled: zod_1.z.boolean().default(false),
triggerMode: zod_1.z.enum(['ALL', 'KEYWORD']).default('ALL'),
keywords: zod_1.z.array(zod_1.z.string()).default([]),
});
try {
const tenantId = req.tenantId;
const instanceId = req.params['instanceId'];
if (!await validateInstance(instanceId, tenantId)) {
res.status(404).json({ error: 'Instância não encontrada' });
return;
}
const body = schema.parse(req.body);
const existing = await chatbot_repository_1.botRepo.findAll(tenantId, instanceId);
if (existing.length > 0) {
res.status(409).json({ error: 'Já existe um bot nesta instância. Use PUT para atualizar.' });
return;
}
const bot = await chatbot_repository_1.botRepo.create({ tenantId, instanceId, ...body });
res.status(201).json(bot);
}
catch (err) {
if (err instanceof zod_1.z.ZodError) {
res.status(400).json({ error: err.errors });
return;
}
logger_1.logger.error({ err }, 'Erro ao criar bot');
res.status(500).json({ error: err.message });
}
});
// PUT /api/chatbot/bots/:instanceId/:botId
router.put('/bots/:instanceId/:botId', async (req, res) => {
const schema = zod_1.z.object({
credentialId: zod_1.z.string().uuid().optional(),
name: zod_1.z.string().min(1).max(80).optional(),
systemPrompt: zod_1.z.string().min(10).optional(),
model: zod_1.z.string().optional(),
enabled: zod_1.z.boolean().optional(),
triggerMode: zod_1.z.enum(['ALL', 'KEYWORD']).optional(),
keywords: zod_1.z.array(zod_1.z.string()).optional(),
});
try {
const tenantId = req.tenantId;
const botId = req.params['botId'];
const body = schema.parse(req.body);
await chatbot_repository_1.botRepo.update(botId, tenantId, body);
const updated = await chatbot_repository_1.botRepo.findById(botId, tenantId);
res.json(updated);
}
catch (err) {
if (err instanceof zod_1.z.ZodError) {
res.status(400).json({ error: err.errors });
return;
}
logger_1.logger.error({ err }, 'Erro ao atualizar bot');
res.status(500).json({ error: err.message });
}
});
// DELETE /api/chatbot/bots/:instanceId/:botId
router.delete('/bots/:instanceId/:botId', async (req, res) => {
try {
const tenantId = req.tenantId;
const botId = req.params['botId'];
await chatbot_repository_1.botRepo.delete(botId, tenantId);
res.status(204).send();
}
catch (err) {
logger_1.logger.error({ err }, 'Erro ao deletar bot');
res.status(500).json({ error: err.message });
}
});
// ─────────────────────────────────────────────────────────────────────────
// BOT STATE — Human Takeover por chat
// ─────────────────────────────────────────────────────────────────────────
// POST /api/chatbot/chats/:chatId/pause — pausa o bot (human takeover)
router.post('/chats/:chatId/pause', async (req, res) => {
try {
await chatbot_repository_1.chatBotState.pauseBot(req.params['chatId']);
res.json({ paused: true });
}
catch (err) {
res.status(500).json({ error: err.message });
}
});
// POST /api/chatbot/chats/:chatId/resume — retoma o bot
router.post('/chats/:chatId/resume', async (req, res) => {
try {
await chatbot_repository_1.chatBotState.resumeBot(req.params['chatId']);
res.json({ paused: false });
}
catch (err) {
res.status(500).json({ error: err.message });
}
});
return router;
}