Relatório técnico — rotas Admin x Operadores

Projeto analisado: ServidorDelphiRestDataware.zip

Foco principal: Source/DataModule/ServidorWhatsapp.DataModule.DmServer.pas e ServidorWhatsapp.DataModule.DmServer.dfm

Data da análise: 19/05/2026


1. Conclusão principal

A separação entre usuários administrativos e operadores de atendimento está correta e deve ser mantida.

A tabela usuarios deve representar apenas os admins do painel/gestão.

A nova tabela operadores deve representar os usuários operacionais do atendimento, com login próprio e campos de atendimento.

O projeto já possui parte da estrutura nova nos models/migration, principalmente em Model.Atendimento.pas e Model.Database.pas, mas o DmServer ainda possui pontos que continuam tratando operador como derivado de usuario_id.

O ajuste mais urgente está na rota:

/api/atendimento/operadores

Atualmente o POST e o PUT ainda usam usuario_id. Para a estrutura nova, essa rota deve receber os dados próprios do operador: nome, email, senha, nome_exibicao, status e max_atendimentos_simultaneos.


2. Situação atual encontrada no projeto

2.1. Componentes de rota no DFM

No arquivo ServidorWhatsapp.DataModule.DmServer.dfm, existem 4 grupos principais:

Componente Base Função
`Srv_Whats` `/whatsapp/` Rotas de integração direta com WhatsApp/TInject
`Srv_ApiAdmin` `/` Rotas administrativas atuais: usuários, webhooks e destinatários
`SvCtx_ApiAdmin` `/` Páginas HTML internas: login/main/main_red
`SrvEvt_Atendimento` `/api/atendimento/` Rotas do módulo multiatendimento

2.2. Ponto de atenção de segurança

Todas as rotas do componente SrvEvt_Atendimento estão com:

Routes.All.NeedAuthorization = False

Isso significa que, hoje, endpoints sensíveis como operadores, setores, atendimentos, fila e mensagens podem ficar sem autenticação dependendo da configuração final do servidor.

Recomendação: proteger todas as rotas de atendimento, exceto uma rota interna de entrada de mensagens caso ela seja validada por token/assinatura própria.


3. Rotas atuais mapeadas

3.1. WhatsApp

Base: /whatsapp/

Autenticação atual: NeedAuthorization=True

Rota Status Observação
`/whatsapp/send` Manter Envio de mensagem/arquivo
`/whatsapp/prelistacontatos` Manter Pré-listagem de contatos
`/whatsapp/listacontatos` Manter Lista contatos carregados
`/whatsapp/wqrcode` Manter Consulta QR Code
`/whatsapp/wautentication` Manter Inicia autenticação WhatsApp
`/whatsapp/wlogout` Manter Logout WhatsApp
`/whatsapp/meunumero` Manter Número conectado
`/whatsapp/wastatus` Manter Status da conexão
`/whatsapp/prelistagrupos` Manter Pré-listagem de grupos
`/whatsapp/listagrupos` Manter Lista grupos
`/whatsapp/solicitaconvitegrupo` Manter Solicita convite
`/whatsapp/convitegrupo` Manter Retorna convite

Essas rotas não são o foco da separação admin/operador.


3.2. Admin atual

Base atual: /

Rota atual Handler Situação Ação recomendada
`/usuarios` `Srv_ApiAdminEventsmeReplyEventByType` Funcional, mas nome do handler está confuso Renomear para `Srv_ApiAdminEventsusuariosReplyEventByType`
`/webhooks` `Srv_ApiAdminEventswebhooksReplyEventByType` Manter Sem alteração direta
`/webhook-destinatarios` `Srv_ApiAdminEventswebhookdestinatariosReplyEventByType` Manter Sem alteração direta

Recomendação de organização futura

Hoje as rotas administrativas ficam na raiz. Para uma estrutura mais limpa, o ideal seria evoluir para:

/api/admin/usuarios
/api/admin/webhooks
/api/admin/webhook-destinatarios

Isso não precisa ser feito imediatamente, porque impacta o painel atual. Pode ser uma segunda etapa.


3.3. Atendimento atual

Base atual: /api/atendimento/

Rota Situação Observação
`/api/atendimento/canais` Manter CRUD de canais
`/api/atendimento/palavras_chave` Manter CRUD de palavras-chave
`/api/atendimento/setores` Manter CRUD de setores e `/setores/menu`
`/api/atendimento/operadores` Alterar Ainda usa `usuario_id` em POST/PUT
`/api/atendimento/contatos` Manter Listagem/consulta/status de contatos
`/api/atendimento/atendimentos` Manter com ajuste conceitual `operador_id` agora deve ser `operadores.id`
`/api/atendimento/fila` Manter com ajuste conceitual Usa `operador_id` na query string
`/api/atendimento/em_andamento` Manter com ajuste conceitual Usa `operador_id` na query string
`/api/atendimento/entrada` Revisar segurança Entrada de mensagens do WhatsApp; deve ter token/assinatura

4. Rotas que devem ser alteradas

4.1. /api/atendimento/operadores — prioridade alta

Problema atual

No DmServer, o POST /api/atendimento/operadores ainda chama:

TAtendimentoOperadorModel.Inserir(
  JsonGetInt(LBody, 'usuario_id', 0),
  JsonGetStr(LBody, 'nome_exibicao'),
  JsonGetInt(LBody, 'max_atendimentos_simultaneos', 5),
  LId,
  JsonGetStatus(LBody)
);

E o PUT /api/atendimento/operadores/{id} ainda chama:

TAtendimentoOperadorModel.Atualizar(
  LId,
  JsonGetInt(LBody, 'usuario_id', 0),
  JsonGetStr(LBody, 'nome_exibicao'),
  JsonGetInt(LBody, 'max_atendimentos_simultaneos', 5),
  JsonGetStatus(LBody)
);

Essas chamadas mantêm o modelo antigo, onde operador nasce de um usuario_id.

Como deve ficar

POST correto

POST /api/atendimento/operadores
Content-Type: application/json

Body recomendado:

{
  "nome": "Operador Suporte",
  "email": "operador@empresa.com.br",
  "senha": "123456",
  "nome_exibicao": "Suporte",
  "max_atendimentos_simultaneos": 5,
  "status": "A"
}

Chamada recomendada no DmServer:

LModel := TAtendimentoOperadorModel.Inserir(
  JsonGetStr(LBody, 'nome'),
  JsonGetStr(LBody, 'email'),
  JsonGetStr(LBody, 'senha'),
  JsonGetStr(LBody, 'nome_exibicao'),
  JsonGetInt(LBody, 'max_atendimentos_simultaneos', 5),
  LId,
  JsonGetStatus(LBody)
);

PUT correto

PUT /api/atendimento/operadores/{id}
Content-Type: application/json

Body recomendado:

{
  "nome": "Operador Suporte Atualizado",
  "email": "suporte@empresa.com.br",
  "nome_exibicao": "Suporte N1",
  "max_atendimentos_simultaneos": 8,
  "status": "A"
}

Chamada recomendada no DmServer:

LModel := TAtendimentoOperadorModel.Atualizar(
  LId,
  JsonGetStr(LBody, 'nome'),
  JsonGetStr(LBody, 'email'),
  JsonGetStr(LBody, 'nome_exibicao'),
  JsonGetInt(LBody, 'max_atendimentos_simultaneos', 5),
  JsonGetStatus(LBody)
);

4.2. /usuarios — manter somente para admins

A rota /usuarios deve continuar usando TUsuarioModel, mas agora com semântica clara:

/usuarios = administradores do sistema

Não deve mais ser usada para criar operador.

Recomendação

Renomear internamente:

Srv_ApiAdminEventsmeReplyEventByType

para:

Srv_ApiAdminEventsusuariosReplyEventByType

Isso evita confusão, porque o handler não é mais /me; ele é o CRUD de usuários administrativos.


4.3. Token/login — prioridade alta

Problema atual

O evento global de autenticação:

ServerMethodDataModuleGetToken

valida apenas:

TUsuarioModel.ValidateLogin(...)

Ou seja, hoje o token é de usuário/admin.

Mas o TAtendimentoOperadorModel já possui:

ValidateLogin(const AEmail, ASenha: string; out AId: Integer; out ANome, ANomeExibicao: string; out AStatus: Char)

Esse método ainda não está integrado ao fluxo de login/token.

Caminho recomendado

Usar o mesmo endpoint global de token do REST Dataware, mas incluir o tipo de usuário no segredo do token.

Exemplo para admin:

{
  "id": 1,
  "tipo": "admin",
  "nome": "Administrador",
  "status": "A"
}

Exemplo para operador:

{
  "id": 3,
  "tipo": "operador",
  "nome": "Operador Suporte",
  "nome_exibicao": "Suporte",
  "status": "A"
}

Estratégia prática

No ServerMethodDataModuleGetToken:

1. Tentar login em TUsuarioModel.ValidateLogin.

2. Se não encontrar admin válido, tentar TAtendimentoOperadorModel.ValidateLogin.

3. Gerar token com campo tipo.

No ServerMethodDataModuleUserTokenAuth:

1. Ler tipo do token.

2. Se tipo = admin, validar em usuarios.

3. Se tipo = operador, validar em operadores.


5. Rotas novas recomendadas

5.1. Alterar senha de operador

O model já possui:

TAtendimentoOperadorModel.AlterarSenha

Mas não existe rota no DmServer usando esse método.

Rota recomendada

PATCH /api/atendimento/operadores/{id}/senha
Content-Type: application/json

Body:

{
  "senha": "novaSenha123"
}

Observação

Essa rota pode ser implementada dentro do próprio handler SrvEvt_AtendimentoEventsoperadoresReplyEventByType, no bloco rtPatch, adicionando a ação SENHA.


5.2. Alterar senha de admin

O TUsuarioModel também possui AlterarSenha, mas a rota /usuarios não expõe isso claramente.

Rota recomendada

PATCH /usuarios/{id}/senha
Content-Type: application/json

Body:

{
  "senha": "novaSenhaAdmin123"
}

5.3. Perfil do operador logado

Depois que operador tiver token próprio, será útil ter uma rota para o painel descobrir quem está logado.

Rota recomendada

GET /api/atendimento/operadores/me
Authorization: Bearer {token}

Retorno esperado:

{
  "success": true,
  "data": {
    "id": 3,
    "nome": "Operador Suporte",
    "email": "operador@empresa.com.br",
    "nome_exibicao": "Suporte",
    "status": "A",
    "online": 1
  }
}

Essa rota depende de recuperar os dados do token atual. Se isso for mais difícil no REST Dataware, pode ficar para uma etapa posterior.


5.4. Ping/status online do operador

Hoje já existe:

PATCH /api/atendimento/operadores/{id}/online?novo=1
PATCH /api/atendimento/operadores/{id}/online?novo=0

Funciona, mas para APIs modernas eu recomendo também aceitar body JSON.

Forma atual mantida

PATCH /api/atendimento/operadores/3/online?novo=1

Forma recomendada futura

PATCH /api/atendimento/operadores/3/online
Content-Type: application/json

Body:

{
  "online": true
}

5.5. Rota de entrada com assinatura ou token interno

A rota:

POST /api/atendimento/entrada

recebe mensagens de entrada do WhatsApp.

Ela deve ser tratada como rota interna, não como rota pública aberta.

Recomendação

Adicionar uma das opções:

Opção A — header fixo interno

X-Internal-Token: token_configurado_no_servidor

Opção B — assinatura HMAC

X-Hub-Signature-256: sha256=...

Opção C — Bearer token próprio do serviço

Authorization: Bearer {token-do-servidor-whatsapp}

6. Rotas de operador após ajuste

CRUD principal

Método Rota Função
`GET` `/api/atendimento/operadores` Listar operadores
`GET` `/api/atendimento/operadores/{id}` Obter operador
`POST` `/api/atendimento/operadores` Criar operador independente
`PUT` `/api/atendimento/operadores/{id}` Atualizar operador independente
`PATCH` `/api/atendimento/operadores/{id}/status?novo=A` Alterar status
`PATCH` `/api/atendimento/operadores/{id}/online?novo=1` Alterar online/offline
`PATCH` `/api/atendimento/operadores/{id}/senha` Alterar senha

Vínculo com setor

Método Rota Função
`GET` `/api/atendimento/operadores/{id}/setores` Listar setores do operador
`POST` `/api/atendimento/operadores/{id}/setores` Vincular setor
`DELETE` `/api/atendimento/operadores/{id}/setores/{setor_id}` Remover vínculo

Body para vincular setor:

{
  "setor_id": 1,
  "prioridade": 0,
  "ativo": true
}

7. Rotas de atendimento que devem continuar usando operador_id

Essas rotas continuam válidas, mas agora o operador_id deve sempre ser o ID da tabela operadores, não da tabela usuarios.

Rota Observação
`GET /api/atendimento/fila?operador_id=1` `operador_id` = `operadores.id`
`GET /api/atendimento/em_andamento?operador_id=1` `operador_id` = `operadores.id`
`POST /api/atendimento/atendimentos/{id}/assumir` Body deve enviar `operador_id` de `operadores.id`
`POST /api/atendimento/atendimentos/{id}/mensagens` Body deve enviar `operador_id` de `operadores.id`
`PATCH /api/atendimento/atendimentos/{id}/finalizar` Body deve enviar `operador_id` de `operadores.id`
`PATCH /api/atendimento/atendimentos/{id}/lidas_operador` Body deve enviar `operador_id` de `operadores.id`

8. Ajustes recomendados no DFM

No componente SrvEvt_Atendimento, avaliar alterar NeedAuthorization para True nas rotas abaixo:

EventName Auth recomendada
`canais` `True`
`palavras_chave` `True`
`setores` `True`
`operadores` `True`
`contatos` `True`
`atendimentos` `True`
`fila` `True`
`em_andamento` `True`
`entrada` `False` somente se usar token/assinatura própria; caso contrário `True`

9. Ajustes recomendados nos models/migrations

9.1. Model.Database.pas

A MigrateToVersion15 já cria a tabela operadores, migra dados de atendimento_operadores e troca as FKs principais para operadores(id).

Isso está correto.

Próxima migration futura recomendada

Depois de validar em produção:

RENAME TABLE atendimento_operadores TO atendimento_operadores_legado;

ou, se não houver mais dependência:

DROP TABLE atendimento_operadores;

Eu recomendo renomear primeiro, não dropar direto.

9.2. Model.Atendimento.pas

O model já possui funções novas adequadas:

TAtendimentoOperadorModel.Inserir(nome, email, senha, nome_exibicao, max, id, status)
TAtendimentoOperadorModel.Atualizar(id, nome, email, nome_exibicao, max, status)
TAtendimentoOperadorModel.AlterarSenha(id, senha)
TAtendimentoOperadorModel.ValidateLogin(email, senha, ...)

O problema principal está no DmServer, que ainda chama os overloads antigos com usuario_id.


10. Ordem recomendada de implementação

Etapa 1 — corrigir rota de operadores

Alterar POST e PUT de /api/atendimento/operadores para usar os métodos novos com nome, email, senha, etc.

Etapa 2 — adicionar alteração de senha

Adicionar:

PATCH /api/atendimento/operadores/{id}/senha
PATCH /usuarios/{id}/senha

Etapa 3 — ajustar autenticação

Permitir token para:

- admin, vindo de usuarios

- operador, vindo de operadores

O token deve carregar um campo tipo.

Etapa 4 — proteger rotas do atendimento

Alterar NeedAuthorization=True nas rotas internas do atendimento.

Etapa 5 — revisar o painel web

Garantir que o painel use:

admin_id     -> usuarios.id
operador_id  -> operadores.id

Nunca misturar os dois.


11. Resumo das alterações obrigatórias

Prioridade Item Arquivo
Alta Corrigir `POST /api/atendimento/operadores` `ServidorWhatsapp.DataModule.DmServer.pas`
Alta Corrigir `PUT /api/atendimento/operadores/{id}` `ServidorWhatsapp.DataModule.DmServer.pas`
Alta Ajustar token/login para aceitar operador `ServidorWhatsapp.DataModule.DmServer.pas`
Alta Proteger rotas de atendimento `ServidorWhatsapp.DataModule.DmServer.dfm`
Média Adicionar rota de senha do operador `ServidorWhatsapp.DataModule.DmServer.pas`
Média Adicionar rota de senha do admin `ServidorWhatsapp.DataModule.DmServer.pas`
Média Renomear handler de `/usuarios` `ServidorWhatsapp.DataModule.DmServer.pas/.dfm`
Baixa Criar namespace `/api/admin/*` `ServidorWhatsapp.DataModule.DmServer.dfm` e painel
Baixa Renomear tabela antiga para legado Próxima migration

12. Parecer final

A abordagem está correta e deixa o projeto mais profissional.

Separar usuarios e operadores resolve vários problemas futuros:

- admin não precisa ser operador;

- operador pode ter login próprio;

- operador pode ter status online/offline;

- operador pode ter limite de atendimentos;

- setor pode ser vinculado diretamente ao operador;

- o painel pode diferenciar permissões administrativas e operacionais;

- fica mais fácil implementar permissões futuras.

O ponto principal agora é terminar a transição no DmServer, porque o banco/model já caminharam para a estrutura nova, mas as rotas ainda carregam parte da estrutura antiga.