feat: edição de rotas de entrada, fix de 2 bugs reais no ESL, diagnóstico de NAT/áudio e softphone WebRTC (PHASE 65/66)
Três achados reportados pelo usuário numa mensagem só: (1) Rotas de Entrada não tinha edição depois de criada — implementada no mesmo padrão de Filas; (2) telas de Platform > Infraestrutura sempre davam "Timeout no ESL" nesta VM — não era limitação permanente como o comentário antigo dizia, e sim ESL_HOST=freeswitch (nome DNS que só existe dentro da rede do Docker) mais um segundo bug independente (`show gateways as json` não é comando válido nesta versão do FreeSWITCH); (3) ramal externo registrava mas sem áudio — diagnosticado com contadores de pacote do iptables: a VM está atrás de um roteador sem port-forward pra faixa de RTP, achado de infraestrutura de rede, não bug de código. Também integra o softphone WebRTC (handphone.js/OpenSIPS, já em produção): código-fonte encontrado em git.falehandix.com.br/Handix/handphone-2.0, patch mínimo pra aceitar o endereço do proxy em runtime (era build-time), nova config global (Platform > Infraestrutura > Softphone WebRTC) e widget na topbar do tenant que pega usuário/domínio/senha do ramal vinculado ao agente logado. Adiciona docs/QA_SETUP.md — runbook completo pra subir o ambiente do zero numa máquina nova (Docker, migrations, seed, systemd), e completa o .env.example que estava faltando a maioria das variáveis reais. Testado ponta a ponta com Playwright: edição de rota (criar/editar/F5), as 3 telas de Infraestrutura com dado real, e um tenant/ramal/agente de teste criados na hora confirmando que o script do softphone recebe as credenciais certas. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BFaBaBSQGhyXGEgtTYZGV8
This commit is contained in:
80
TODO.md
80
TODO.md
@@ -2337,6 +2337,86 @@ a funcionalidade de verdade)
|
||||
sobreviveu ao F5. Confirmado ainda que corrigir a Hydration
|
||||
eliminou o erro do console sem quebrar nenhum dos dois cenários
|
||||
|
||||
## PHASE 65 — 3 achados reais reportados pelo usuário numa mensagem só:
|
||||
edição de rota de entrada faltando, telas de Infraestrutura sempre com
|
||||
erro de ESL, e ramal externo registrando sem áudio
|
||||
- [x] Rotas de Entrada não tinha edição depois de criada (só criar/
|
||||
remover) — mesmo padrão de Filas (form inline por linha,
|
||||
`Fragment`-wrapped). DID não pode ser trocado depois de criado
|
||||
(é a chave de roteamento — trocar quebraria o vínculo com o
|
||||
tronco/operadora). Testado ponta a ponta com Playwright: criar,
|
||||
editar descrição, F5, confirma que persistiu.
|
||||
- [x] Telas de `Platform > Infraestrutura` sempre mostravam "Timeout
|
||||
conectando no ESL" nesta VM — suposição registrada até então
|
||||
(incorreta) era que isso seria permanente (`apps/api` fora do
|
||||
Docker, porta nunca publicada). Causa real: `ESL_HOST=freeswitch`
|
||||
no `.env` é um nome DNS que só existe dentro da rede do Docker,
|
||||
nunca resolve a partir do host. O host SEMPRE alcança o IP de
|
||||
qualquer container na rede bridge diretamente, com ou sem porta
|
||||
publicada — só outras máquinas são bloqueadas sem `ports:`. Fix:
|
||||
publicar 8021 só em loopback (`127.0.0.1:8021:8021`) + trocar
|
||||
`ESL_HOST` pra `127.0.0.1`. Ver docs/FREESWITCH.md ("Achados na
|
||||
sessão de PHASE 65") pro segundo bug achado na mesma investigação
|
||||
(`show gateways as json` não é um comando válido nesta versão do
|
||||
FreeSWITCH). Verificado com curl + Playwright nas 3 telas.
|
||||
- [x] "Registrei o ramal e não passou áudio" — diagnosticado com
|
||||
contadores de pacotes do `iptables` (não suposição): SIP chegava,
|
||||
RTP nunca chegava. Causa: esta VM está atrás de um roteador (NAT)
|
||||
sem port-forward configurado pra faixa de RTP — infraestrutura de
|
||||
rede fora do controle desta aplicação, não um bug de código (a
|
||||
configuração de NAT-detection do próprio FreeSWITCH já estava
|
||||
correta, testado direto via `fs_cli -x "acl <ip> nat.auto"`). Ver
|
||||
docs/NETWORK_ARCHITECTURE.md pro diagnóstico completo e o
|
||||
checklist de portas pra encaminhar no roteador.
|
||||
|
||||
## PHASE 66 — Softphone WebRTC embutido (pedido do usuário: "integre o
|
||||
widget handphone.js, é um webrtc que vai conectar em outro proxy [OpenSIPS,
|
||||
já em produção] que depois vai vir via sip comum ao b2bcall")
|
||||
- [x] O FreeSWITCH deste projeto nunca fala WebRTC — quem faz a ponte
|
||||
WebRTC↔SIP é um OpenSIPS externo já em produção. Cada ramal
|
||||
continua um registro SIP puro; só o NAVEGADOR do agente conecta
|
||||
via WebRTC no OpenSIPS.
|
||||
- [x] Código-fonte do widget (`handphone.js`, já commitado sem histórico
|
||||
neste repo) achado em `git.falehandix.com.br/Handix/handphone-2.0`
|
||||
(branch `main`, a mais atualizada — a branch default `handphone-
|
||||
2.0-alpha` está desatualizada). Nesse código, o endereço do proxy
|
||||
(`server`) vinha de `VITE_SIP_SERVER`, uma env var de BUILD-TIME —
|
||||
incompatível com "Super Admin configura em runtime, sem redeploy".
|
||||
Patch mínimo de 2 linhas em `widgetStorage.mergeConfig`/
|
||||
`widgetConfig.parseWidgetConfig` (só nesta cópia local, nunca
|
||||
enviado pro repo externo do usuário): `data-sip-server`/
|
||||
`window.HandphoneConfig.server` agora tem prioridade sobre o env
|
||||
var de build, mantendo o comportamento antigo como fallback.
|
||||
Rebuildado (`npm run build:widget`) e vendorizado em
|
||||
`apps/frontend/public/handphone.js` (era um arquivo solto na raiz
|
||||
do repo antes desta fase).
|
||||
- [x] `PlatformSetting` (tabela chave-valor genérica, sem RLS — mesmo
|
||||
padrão de Role/Permission) pra guardar o endereço WSS do proxy
|
||||
OpenSIPS, configurável em `Platform > Infraestrutura > Softphone
|
||||
WebRTC` (permissão `freeswitch.configure`, mesma família das
|
||||
outras telas de infra).
|
||||
- [x] `GET /agents/me/softphone-config` (novo, `agents-me.controller.ts`)
|
||||
— credenciais SIP do PRÓPRIO ramal vinculado ao agente logado
|
||||
(nunca um `extensionId` arbitrário) + o endereço do proxy. Sem
|
||||
`@RequirePermission` (mesmo padrão dos outros endpoints de
|
||||
`agents/me`) — é o próprio agente pegando a própria senha, não uma
|
||||
ação administrativa tipo `POST /extensions/:id/reveal-password`
|
||||
(que exige `extensions.manage`, permissão que um agente comum
|
||||
nunca tem). Cada acesso fica no audit log.
|
||||
- [x] `SoftphoneWidget` (novo componente client, ao lado do
|
||||
`AgentStatusWidget` na topbar do tenant) injeta um `<script
|
||||
src="/handphone.js" data-sip-*>` sob demanda (só quando
|
||||
`hasExtension`, uma vez por carregamento de página — nunca no
|
||||
layout server-side, pra não decifrar a senha nem gravar audit log
|
||||
a cada navegação). O widget se autoconecta sozinho via WebRTC
|
||||
independente do estado de ACD (Disponível/Pausa/Offline) — mesmo
|
||||
tipo de registro SIP que um telefone físico faria.
|
||||
- [x] Testado ponta a ponta com Playwright, tenant/agente/ramal reais
|
||||
criados na hora: tela de config do Super Admin salva e recarrega
|
||||
certo; script do softphone injetado na topbar com
|
||||
`sip-user`/`sip-domain`/`sip-server` corretos e a senha presente
|
||||
(não vazia) assim que o agente com ramal vinculado abre `/app`.
|
||||
|
||||
---
|
||||
|
||||
## Riscos conhecidos
|
||||
|
||||
Reference in New Issue
Block a user