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.