architecture.md 11 KB

Arquitetura — Backend

Visão geral

O backend segue uma arquitetura em camadas com bootstrap por factories. Cada domínio de negócio é encapsulado em um conjunto padronizado de arquivos.

index.js
└── CoreFactory.Iniciar()
    ├── BancoFactory   → conecta ao MySQL, valida conexão
    └── ServerFactory  → cria o Express app, registra middlewares, sobe o servidor,
         │                agenda os jobs em background (src/jobs/)
         ├── /webhooks/whatsapp (fora de /api, sem JWT, antes de express.json())
         └── Roteamento.IniciarRoteamento(app)
              └── Domínios: Auth · Chat · Conversations · Documents · Ingest · Search · Usuario ·
                            Atendimentos · Conversas (WhatsApp) · WhatsappConexao · IxcIntegrador (+ IxcBi)
                   └── *.Rotas.js → *.Controller.js → *Service.js → Model (Objection)

Camadas

Factories (src/factories/)

Responsáveis pelo bootstrap da aplicação. Chamadas em sequência por index.js na raiz.

Factory Responsabilidade
Core.factory.js Orquestra Banco + Server
Banco.factory.js Conecta ao MySQL e valida a conexão
Server.factory.js Cria o app Express, registra middlewares globais, sobe o servidor HTTP

Roteamento (src/routes/index.js)

A classe Roteamento registra todos os domínios no Express via IniciarRoteamento(app). Cada domínio tem seu próprio arquivo *.Rotas.js que define as rotas e middlewares daquele domínio.

Todos os domínios são montados sob o prefixo /api, que recebe dois middlewares globais antes de qualquer rota: rateLimitMiddleware (RateLimit.js) e authMiddleware (Validajwt.js). Ou seja, o JWT é decodificado globalmente; os middlewares requireUser/requireAdmin por rota apenas verificam o resultado. Ver auth.md para o fluxo completo e os níveis de acesso.

Controllers (src/controllers/)

Recebem a requisição HTTP, extraem os dados necessários (params, body, user) e delegam ao Service correspondente. Não contêm lógica de negócio.

Services (src/services/)

Contêm toda a lógica de negócio. São chamados pelos controllers. Podem chamar Models (Objection) ou clients externos (Qdrant, Ollama).

Models (src/models/)

Modelos Objection.js. Mapeiam as tabelas do MySQL e definem relacionamentos.

Model Tabela
Usuario.model.js usuarios
RefreshToken.model.js refresh_tokens
SolicitacaoResetSenha.model.js solicitacoes_reset_senha
Conversation.model.js conversations
Message.model.js messages
Atendimento.model.js atendimentos sincronizados do ifbot
AtendimentoMensagem.model.js mensagens de um atendimento
AtendimentoCliente.model.js vínculo atendimento ↔ cliente/contrato IXC
AtendimentoAvaliacao.model.js resultado da avaliação por LLM de um atendimento
AtendimentoGoldenLabel.model.js rótulos de referência (golden set) para calibrar a avaliação
AtendimentoRagIndex.model.js controle de ingestão do RAG de atendimentos
WhatsappConversa.model.js conversas do chat de atendente via WhatsApp Cloud API
WhatsappMensagem.model.js mensagens de uma WhatsappConversa
Integracao.model.js credenciais/token do integrador IXC (Alfred), cacheadas no banco

Middleware (src/middleware/)

Arquivo Função
Validajwt.js Verifica e decodifica o JWT do header Authorization
RequireUser.js Exige usuário autenticado (qualquer nível)
RequireAdmin.js Exige nível "3" (administrador)
Validate.js Valida body/params/query com schema zod (schema.parse())
ParseId.js Converte req.params.id para número e valida
RateLimit.js Rate limiting por IP
ErrorHandler.js Captura erros e formata a resposta de erro

Schemas de validação (src/middleware/schemas/)

Schemas zod usados com o middleware Validate (que chama schema.parse()). Um arquivo por domínio que exige validação:

Arquivo Domínio
Auth.Schema.js Login, refresh, solicitação de reset de senha
Chat.Schema.js Corpo do chat
Ingest.Schema.js Corpo do ingest
Search.Schema.js Corpo da busca
Usuario.Schema.js Criar, atualizar e alterar senha de usuário
Atendimento.Schema.js Filtros e ações sobre atendimentos/avaliações
Conversas.Schema.js Corpo das rotas do chat de atendente (WhatsApp)
IxcIntegrador.Schema.js Corpo/query das buscas no integrador IXC (clientes, contratos, OS)

Padrão de arquivos por domínio

Todo domínio segue a convenção de nomenclatura abaixo (passo a passo completo, com exemplo, em conventions.md):

src/
├── routes/        NomeDoModulo.Rotas.js
├── controllers/   NomeDoModulo.Controller.js
├── services/      nomeDoModuloService.js
├── models/        NomeDoModulo.model.js          (se houver tabela)
└── middleware/
    └── schemas/   NomeDoModulo.Schema.js         (se houver validação)

Alias ESM (#*)

O package.json define um alias de importação para evitar caminhos relativos longos:

"imports": {
  "#*": "./src/*"
}

Como ficaria:

import { UsuarioController } from "#controllers/Usuario.Controller.js";
import { db } from "#config/db.config.js";

Estado atual: o alias está definido e funciona, mas ainda não é usado em nenhum arquivo do src/ — todo o código usa imports relativos (../config/db.config.js). Foi adicionado na refatoração para espelhar o @ do minha_back, mas a migração dos imports não foi feita.

Domínios existentes

Referência completa de endpoints, bodies e retornos em api.md.

Domínio Prefix da rota Descrição
Auth /api/auth Login, logout, refresh de token
Chat /api/chat Envio de mensagens ao LLM (pipeline em src/chat/, ver abaixo)
Conversations /api/conversations CRUD de conversas do chat persistidas
Documents /api/documents Listagem e exclusão de documentos
Ingest /api/ingest Ingestão de conteúdo na base RAG de documentos
Search /api/search Busca semântica nos documentos
Usuario /api/users CRUD de usuários do sistema
Atendimentos /api/atendimentos Sincronização, avaliação por LLM, golden set e ranking de atendentes sobre atendimentos de suporte
Conversas /api/conversas Chat de atendente via WhatsApp (mensagens, sugestão de resposta)
WhatsappConexao /api/whatsapp Status da conexão com a WhatsApp Cloud API
IxcIntegrador /api/ixc Proxy para o integrador Alfred/IXC (clientes, contratos, OS); sub-roteia /api/ixc/bi/* para IxcBi.Rotas.js (BI do Alfred)

O webhook do WhatsApp (/webhooks/whatsapp) não é um domínio sob /api — é montado direto no ServerFactory, fora do middleware de auth, autenticado por hub.verify_token (GET, verificação) e assinatura HMAC opcional (WHATSAPP_APP_SECRET, POST, recebimento).

Pipeline do chat (src/chat/)

Uma mensagem recebida em /api/chat passa por três estágios, nessa ordem:

  1. Comandos explícitos (commands.js) — antes de qualquer LLM, tenta casar a mensagem com um comando estruturado que consulta o ERP (Alfred/IXC) diretamente: /cto <busca> (ctoCommand.js) e /<pppoe> (loginCommand.js, fallback genérico — por isso vem por último no array COMMANDS, depois de comandos mais específicos). Sem chamada de LLM, sem risco de alucinação em dado técnico.
  2. Roteamento por LLM (chatRouter.js) — se nenhum comando bateu, classificarModoChat() classifica a intenção em um de três modos: documentos (RAG sobre a base de conhecimento), atendimentos (RAG sobre atendimentos de suporte já registrados) ou geral. Um regex de código de protocolo (CODIGO_PROTOCOLO_RE) bypassa o LLM quando a mensagem já traz um protocolo explícito. O resultado é cacheado (TTL 15 min) quando não há histórico de conversa.
  3. Chain de resposta (chatChain.js) — monta o RAG do modo escolhido: rewrite de query (resolve follow-ups usando o histórico), HyDE opcional (gera uma passagem hipotética para melhorar o embedding de busca), busca no Qdrant, rerank via LLM e geração da resposta final — streaming SSE (answerWithContextStream) ou não-streaming (answerWithContext).

Ao adicionar um novo comando explícito, siga o padrão parse/resolve de ctoCommand.js/loginCommand.js e registre-o no array COMMANDS.

RAG de atendimentos vs. RAG de documentos

Dois pipelines RAG paralelos e independentes, com collections Qdrant separadas:

  • Documentos: ingestão manual/upload via domínio Ingest, collection QDRANT_COLLECTION.
  • Atendimentos: ingestão automática via job (ingestAtendimentosRag.js), a partir de conversas sincronizadas do ifbot (syncAtendimentos.js), collection ATENDIMENTOS_RAG_COLLECTION. Enriquecido com dados da avaliação por IA (score/resumo/sugestões) via atendimentoRagService.js. A busca vetorial nesse modo é opcional, controlada por ATENDIMENTOS_RAG_SEARCH_ENABLED.

Atendimentos passam por avaliação automática via LLM-como-juiz (atendimentoAvaliacaoService.js, job avaliarAtendimentos.js), sem golden set formal — AtendimentoGoldenLabel existe para começar a calibrar isso.

Integração com o ERP (Alfred/IXC)

ixcIntegradorService.js fala com um integrador externo (repositório separado, roda em http://localhost:4001) que expõe Cliente/Contrato/OS do IXC. Credenciais (Url/Login/Senha/Token) ficam na tabela integracao (model Integracao), não no .env — só o seed (npm run seed:run) lê do .env para provisionar a linha inicial; o token sobrevive a restart e só é invalidado rodando o seed de novo com credenciais diferentes.

Jobs em background (src/jobs/)

Agendados pelo ServerFactory, intervalo configurável por env var (0 desativa; variáveis completas em setup.md):

Job Intervalo Função
cleanupTokens.js fixo, 6h (não configurável por env) Remove refresh tokens expirados
syncAtendimentos.js ATENDIMENTOS_SYNC_INTERVAL_MINUTES Importa atendimentos novos do ifbot
avaliarAtendimentos.js ATENDIMENTOS_AVALIACAO_INTERVAL_MINUTES Avalia atendimentos pendentes via LLM
ingestAtendimentosRag.js ATENDIMENTOS_RAG_INTERVAL_MINUTES Ingere atendimentos avaliados no Qdrant

Health check

GET /health — retorna o status de todos os serviços:

{
  "ok": true,
  "checks": {
    "api": true,
    "db": true,
    "qdrant": true,
    "ollama": true
  }
}