# 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:

```http
/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:

```pascal
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:

```http
/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:

```pascal
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:

```pascal
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

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

Body recomendado:

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

Chamada recomendada no `DmServer`:

```pascal
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

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

Body recomendado:

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

Chamada recomendada no `DmServer`:

```pascal
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:

```http
/usuarios = administradores do sistema
```

Não deve mais ser usada para criar operador.

### Recomendação

Renomear internamente:

```pascal
Srv_ApiAdminEventsmeReplyEventByType
```

para:

```pascal
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:

```pascal
ServerMethodDataModuleGetToken
```

valida apenas:

```pascal
TUsuarioModel.ValidateLogin(...)
```

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

Mas o `TAtendimentoOperadorModel` já possui:

```pascal
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:

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

Exemplo para operador:

```json
{
  "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:

```pascal
TAtendimentoOperadorModel.AlterarSenha
```

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

### Rota recomendada

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

Body:

```json
{
  "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

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

Body:

```json
{
  "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

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

Retorno esperado:

```json
{
  "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:

```http
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

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

### Forma recomendada futura

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

Body:

```json
{
  "online": true
}
```

---

## 5.5. Rota de entrada com assinatura ou token interno

A rota:

```http
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

```http
X-Internal-Token: token_configurado_no_servidor
```

#### Opção B — assinatura HMAC

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

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

```http
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:

```json
{
  "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:

```sql
RENAME TABLE atendimento_operadores TO atendimento_operadores_legado;
```

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

```sql
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:

```pascal
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:

```http
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:

```text
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.
