Adição da função de configuração de email e whatts
This commit is contained in:
158
README.md
158
README.md
@@ -1786,3 +1786,161 @@ app/views/admin/configuracoes/index.html.erb # idem (arquivo distint
|
||||
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
<details>
|
||||
<summary><strong>✉️ Notificações e E-mail configuráveis pela tela — SMTP + WhatsApp (11/08/2026)</strong></summary>
|
||||
|
||||
> ⚠️ **STATUS: implementado, ainda NÃO executado.** Nada aqui foi rodado contra um banco nem
|
||||
> contra os servidores reais (Gmail/Twilio) — não há Ruby nem Postgres na máquina de
|
||||
> desenvolvimento. Foi conferida a sintaxe de todos os arquivos `.rb` e `.erb` e validado o
|
||||
> algoritmo de normalização de telefone em Ruby puro. **A migration, a suíte e o envio real
|
||||
> continuam pendentes** — roteiro no fim desta seção.
|
||||
|
||||
### 🎯 O problema
|
||||
Servidor de e-mail e credenciais do Twilio viviam **só no `.env`**: trocar a senha de app do Gmail
|
||||
ou o número remetente exigia editar o arquivo no servidor e **reiniciar o container**. O ADM não
|
||||
tinha como fazer nada disso pela interface.
|
||||
|
||||
Pior: as chaves `notificacao_whatsapp` e `notificacao_email` existiam em `configuracoes` mas
|
||||
**nunca tiveram UI** — `Admin::ConfiguracoesController#index` filtra por `CHAVES_MOEDA`. Ligar
|
||||
notificação só era possível pelo `rails console`.
|
||||
|
||||
### 🆕 A tela
|
||||
**Configurações → card "Notificações e E-mail"** (`/admin/configuracao_notificacao`).
|
||||
**Só `admin`** — `ConfiguracaoNotificacaoPolicy` é mais restrita que `ConfiguracaoPolicy`, que
|
||||
libera `index?` para gerente: aqui ficam senha de e-mail e token do Twilio.
|
||||
|
||||
O acesso é **exclusivamente pelo card dentro de Configurações** — de propósito não há item no menu
|
||||
lateral, para não expor um atalho de credenciais na navegação de todo dia.
|
||||
|
||||
| Bloco | Campos |
|
||||
|---|---|
|
||||
| **Servidor de e-mail (SMTP)** | ativo, servidor, porta, usuário, senha, autenticação, domínio, e-mail e nome do remetente + toggle "avisar motoristas por e-mail" |
|
||||
| **WhatsApp (Twilio)** | ativo, Account SID, Auth Token, número remetente |
|
||||
| **Destinatários administrativos** | e-mail do admin, WhatsApp do admin |
|
||||
|
||||
Três botões: **Salvar**, **Salvar e enviar e-mail de teste**, **Salvar e enviar WhatsApp de teste**.
|
||||
|
||||
### ⚙️ Como funciona (técnico) — os 6 pontos não-óbvios
|
||||
|
||||
**1. Hierarquia banco > `.env`, sem quebrar nada.** `ConfiguracaoNotificacao#smtp_settings` devolve
|
||||
**`nil`** quando não está pronto. O ActionMailer faz `.merge(options || {})` por cima do que o
|
||||
`config/initializers/smtp.rb` montou no boot — então o fallback para o `.env` é **automático**.
|
||||
Enquanto os toggles estiverem desligados, o comportamento é **idêntico ao de antes desta tela**.
|
||||
|
||||
**2. `default delivery_method_options:`, NÃO um `before_action`.** Um callback que mexesse em
|
||||
`message.delivery_method` seria **descartado**: `ActionMailer::Base#mail` roda *depois* dos
|
||||
callbacks e chama `wrap_delivery_behavior!`, que reconfigura a mensagem. O
|
||||
`delivery_method_options` é lido *dentro* do próprio `mail()`.
|
||||
|
||||
**3. `proc`, NÃO lambda.** O Devise avalia o `default from:` com `instance_eval(&proc)`, que passa
|
||||
1 argumento. Um `-> { }` de aridade 0 estouraria **`ArgumentError` em todo "esqueci minha senha"**.
|
||||
|
||||
**4. `config.parent_mailer = 'ApplicationMailer'`** no `devise.rb` — sem isso o reset de senha
|
||||
continuaria preso ao `.env`. Efeito colateral aceito: os e-mails do Devise passam a usar
|
||||
`app/views/layouts/mailer.html.erb`.
|
||||
|
||||
**5. Segredos cifrados sem `master.key`.** Senha SMTP e Auth Token vão para colunas
|
||||
`*_cifrado` (AES-256-GCM) via `AtributoCifrado`, com chave derivada do `secret_key_base`. **Não** se
|
||||
usou ActiveRecord Encryption: exigiria 3 chaves novas, dependeria da ordem dos initializers e
|
||||
estouraria `Errors::Decryption` na leitura. Aqui o reader faz `rescue → nil`, o app **degrada para
|
||||
o `.env`** e a tela mostra um banner amarelo pedindo para redigitar.
|
||||
|
||||
**6. Testes com `deliver_now` e `raise_delivery_errors = true` forçado.** O `development.rb` define
|
||||
`raise_delivery_errors = false` e o adapter do ActiveJob é `:async` (thread in-process) — com
|
||||
`deliver_later` **o teste "passaria" em silêncio mesmo com a senha errada**.
|
||||
|
||||
### 🩹 Bug pré-existente corrigido de passagem
|
||||
`NotificacaoService` mandava `to: "whatsapp:#{user.telefone}"` com o telefone **cru do cadastro**.
|
||||
Um telefone gravado como `(11) 92005-1157` vira `whatsapp:(11) 92005-1157`, o Twilio devolve
|
||||
**21211** — e o `rescue` engolia. **Provavelmente nenhum WhatsApp a motorista jamais chegou.**
|
||||
Agora passa por `ConfiguracaoNotificacao.canal`, que normaliza para E.164.
|
||||
|
||||
> Casos cobertos: `11 920051157`, `(11) 92005-1157`, `011 …`, `+55 11 …`, `whatsapp:+55…` →
|
||||
> `+5511920051157`. O prefixo `55` só é removido quando sobra número demais — senão quebraria o
|
||||
> **DDD 55** (Santa Maria/RS), onde `55991234567` já é o número completo.
|
||||
|
||||
### 🔐 Segurança
|
||||
- A senha gravada **nunca volta para o HTML** (`password_field value: nil`). Campo em branco
|
||||
significa "mantenha a atual" — salvar sem redigitar não apaga o que está lá.
|
||||
- `AuditoriaLog` registra a mudança com `acao: 'editar_notificacoes'`, mas grava apenas
|
||||
`smtp_password_definida: true/false`. **Nunca a senha nem o token** — `dados_novos` é exibido em
|
||||
`/admin/auditoria_logs`, que **gerente também acessa**.
|
||||
- Os erros do Twilio vêm traduzidos (63003/63015 = falta o `join <palavra>` do sandbox, 21608 =
|
||||
conta trial só envia a número verificado, 20003 = SID/token inválidos…), para o ADM resolver
|
||||
sozinho sem abrir o log do container.
|
||||
|
||||
### 🆕 Migration adicionada (rodar `db:migrate`)
|
||||
```
|
||||
20260811000001_create_configuracao_notificacoes.rb
|
||||
```
|
||||
Tabela **singleton** (índice único em `singleton_guard` — impede dois workers Puma criarem linhas
|
||||
concorrentes). Copia as flags antigas de `configuracoes` e **não** importa credenciais do `.env`:
|
||||
os campos nascem vazios e o fallback segue mandando até alguém preencher a tela.
|
||||
|
||||
### 📂 Arquivos
|
||||
```
|
||||
db/migrate/20260811000001_create_configuracao_notificacoes.rb # NOVO — tabela singleton
|
||||
app/models/configuracao_notificacao.rb # NOVO — smtp_settings, credenciais, E.164
|
||||
app/models/concerns/atributo_cifrado.rb # NOVO — AES-256-GCM sem master.key
|
||||
app/policies/configuracao_notificacao_policy.rb # NOVO — show?/update? = admin
|
||||
app/controllers/admin/configuracao_notificacoes_controller.rb # NOVO — show/update + params[:acao]
|
||||
app/views/admin/configuracao_notificacoes/show.html.erb # NOVO — a tela
|
||||
app/services/notificacao/resultado.rb # NOVO — ok?/mensagem/detalhe → flash
|
||||
app/services/notificacao/teste_email.rb # NOVO — erros SMTP em português
|
||||
app/services/notificacao/teste_whatsapp.rb # NOVO — códigos Twilio em português
|
||||
app/services/notificacao/cliente_twilio.rb # NOVO — client com timeout de 15s
|
||||
app/mailers/teste_mailer.rb · app/views/teste_mailer/teste.html.erb # NOVO
|
||||
app/services/notificacao_service.rb # lê do banco; unifica os 2 pares duplicados
|
||||
app/mailers/application_mailer.rb # default from: / delivery_method_options: proc
|
||||
app/mailers/consolidacao_mailer.rb # removido o `default from:` que anulava o proc
|
||||
config/initializers/devise.rb # + parent_mailer
|
||||
config/initializers/smtp.rb # vira fallback (só comentário)
|
||||
config/initializers/inflections.rb # + irregular 'notificacao'
|
||||
config/routes.rb # + resource :configuracao_notificacao
|
||||
app/views/admin/configuracoes/index.html.erb # + card "Notificações e E-mail" (só admin)
|
||||
app/models/configuracao.rb # marca notificacao_* como obsoletas
|
||||
.env.example # hierarquia banco > .env + NOTIFICACAO_SECRET
|
||||
spec/models/configuracao_notificacao_spec.rb # NOVO
|
||||
spec/policies/configuracao_notificacao_policy_spec.rb # NOVO
|
||||
spec/requests/admin/configuracao_notificacoes_spec.rb # NOVO
|
||||
spec/models/table_names_spec.rb # + ConfiguracaoNotificacao
|
||||
```
|
||||
|
||||
### ⏳ Pendente de execução — roteiro
|
||||
```bash
|
||||
# 1. Migrar
|
||||
docker-compose exec app bundle exec rails db:migrate
|
||||
|
||||
# 2. Suíte
|
||||
docker-compose exec app bundle exec rspec spec/models spec/policies spec/requests
|
||||
|
||||
# 3. Confirmar a assinatura da gem (não pôde ser verificada fora do container)
|
||||
docker-compose exec app bundle exec rails runner \
|
||||
'p Twilio::HTTP::Client.instance_method(:initialize).parameters'
|
||||
```
|
||||
4. **Permissão:** logar como **gerente** → o card não aparece e `/admin/configuracao_notificacao`
|
||||
redireciona com "Você não tem permissão". Como **admin** → a tela abre.
|
||||
5. **E-mail:** `smtp.gmail.com`, porta 587, **Senha de app de 16 caracteres** (não a senha da
|
||||
conta), marcar "Ativar envio de e-mail" → **Salvar e enviar e-mail de teste**. Conferir o spam.
|
||||
6. **WhatsApp:** SID / Auth Token / número remetente do Twilio, marcar "Ativar WhatsApp" →
|
||||
**Salvar e enviar WhatsApp de teste**. ⚠️ **No sandbox, o número que vai RECEBER precisa antes
|
||||
mandar `join <sua-palavra>`** para o número do sandbox — sem esse opt-in volta 63003/63015.
|
||||
7. **Não regressão:** com os toggles desligados, finalizar uma consolidação e conferir que nada
|
||||
mudou; e testar o **"Esqueci minha senha"**, que trocou de mailer pai.
|
||||
|
||||
### ⚠️ Alertas
|
||||
- **Rotação do `SECRET_KEY_BASE` torna senha e token ilegíveis.** O sistema não quebra (volta ao
|
||||
`.env` e avisa na tela), mas os dois campos precisam ser redigitados. Para desacoplar, defina
|
||||
**`NOTIFICACAO_SECRET`** no `.env` com uma string longa e **fixa**.
|
||||
- **Não cachear a config em `Rails.cache`:** o `production.rb` usa `:memory_store`, que é por
|
||||
processo — a tela pareceria "não salvar" para os outros workers. É 1 `SELECT` por e-mail.
|
||||
- `app/views/configuracoes/index.html.erb` (fora do `admin/`) **não recebeu o card**: não tem rota
|
||||
e é código morto — o vivo é `app/views/admin/configuracoes/index.html.erb`.
|
||||
|
||||
> **Migration nova** (`db:migrate` obrigatório) e **sem gem nova** — `twilio-ruby` já estava no
|
||||
> Gemfile. ⚠️ Reiniciar o Puma após o deploy.
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user