Atualização de Read.me e \correção da autorização no primeiro login

This commit is contained in:
2026-08-28 02:19:08 -03:00
parent ab126d13db
commit b20299dd6b
4 changed files with 222 additions and 6 deletions

View File

@@ -41,8 +41,10 @@ RUN bundle install --jobs 4 --retry 3
# Copia o restante do código
COPY . .
# Pré-compila assets (em produção)
# RUN bundle exec rails assets:precompile
# Assets NÃO são pré-compilados aqui, e a linha comentada foi removida para
# ninguém "descomentar para resolver": o compose monta o projeto por cima
# (`volumes: - ".:/app"`), então o public/assets gerado na imagem some no boot.
# Quem pré-compila é bin/docker-boot, com o código que está de fato rodando.
EXPOSE 3000

153
README.md
View File

@@ -3360,3 +3360,156 @@ config/routes.rb # POST atualizar_logo
```
</details>
---
<details>
<summary><strong>🩺 28/08/2026 — "A primeira vez que entra dá tela de erro" + os assets 404 que o Cloudflare escondia</strong></summary>
Dois problemas achados a partir de **um** relato ("a primeira vez que entra dá a
tela de erro, em `/auth/login`"). O primeiro era o relatado; o segundo apareceu
com o navegador aberto na mesma tela e era o mais grave dos dois.
| Sintoma na tela | Causa real | Onde se resolveu |
|---|---|---|
| Primeira entrada do dia parece falha do sistema | `devise.failure.unauthenticated` sai como `flash[:alert]` — vermelho, e **duas vezes** | `Users::SessionsController#new` |
| Carrossel e editor de romaneio mortos no teste | `RAILS_ENV=production` + **nada no deploy pré-compilava assets** | `bin/docker-boot` (passo 5) |
## 1. O "erro" que era só o Devise pedindo login
Quem abre `teste.reemtransportes.com.br` deslogado — ou seja, **toda** primeira
entrada — passa por `authenticate_user!`, que redireciona para `/auth/login`
gravando `flash[:alert] = "Para continuar, faça login."`. Como `alert`, a
mensagem saía **em vermelho e em dois lugares ao mesmo tempo**: o toast com
triângulo de alerta no canto superior direito (`layouts/application.html.erb`) e
a caixa vermelha acima do formulário (`devise/sessions/new.html.erb`).
Entrar no sistema pelo caminho normal ficava com a cara de sistema quebrado. E
como na segunda visita a pessoa já está em `/auth/login` sem flash nenhum, a tela
aparecia limpa — daí o "só na primeira vez", que fazia parecer bug intermitente.
A mensagem agora é **descartada**: a tela já é o formulário de login, o aviso não
acrescenta nada.
```ruby
# app/controllers/users/sessions_controller.rb
flash.delete(:alert) if flash[:alert] == I18n.t('devise.failure.unauthenticated', default: nil)
```
⚠️ **O filtro é pela mensagem exata, e não `flash.delete(:alert)` seco.** Senha
errada, conta desativada e `timeout` ("Sua sessão expirou") chegam ao mesmo
`#new` pelo mesmo caminho — essas **precisam** continuar vermelhas. Apagar o
alerta inteiro no `new` deixaria a pessoa digitando a senha errada para sempre
sem entender por quê.
## 2. Os assets 404 — e por que ninguém tinha visto
O console da tela de login acusava dois módulos:
```
Failed to fetch dynamically imported module:
/assets/controllers/carrossel_controller-913b47f7.js -> 404
/assets/controllers/romaneio_controller-0105d794.js -> 404
```
Não é pouco: `carrossel_controller` é a **paginação por tamanho de tela** (a
regra 3 do `CLAUDE.md`) e `romaneio_controller` é o editor de romaneio inteiro.
Os dois estavam mortos no ambiente de teste.
### O disfarce
À primeira vista pareciam só dois arquivos com problema — os outros dez
controllers respondiam 200. **Não respondiam.** Os 200 vinham do **cache do
Cloudflare**:
```
$ curl -sI .../validacao_controller-ca4de84f.js
cache-control: public, max-age=31536000, immutable
age: 275844 # ~3,2 dias em cache
cf-cache-status: HIT # ← nunca chegou no servidor
```
Os assets saem com `max-age` de **um ano**. Os dez que "funcionavam" eram cópias
guardadas na borda dias antes; os dois quebrados eram os dois arquivos alterados
**depois** dessa fotografia. Batendo direto na origem (com querystring, para
furar o cache), **todos** davam 404.
> **Lição de diagnóstico:** ao investigar asset em produção, olhe
> `cf-cache-status` antes de concluir qualquer coisa. `HIT` não é prova de que o
> servidor está servindo — é prova de que ele serviu **algum dia**.
### A causa
O corpo do 404 era `public/404.html`, ou seja, a requisição chegou no **roteador
do Rails**: o servidor de assets do Propshaft nem estava montado. E a resposta do
HTML não trazia o cabeçalho `Server-Timing`, que só existe em development
(`config.server_timing`). Somando os dois: **o servidor roda
`RAILS_ENV=production`**.
Em production o Propshaft **não compila sob demanda** — ele resolve o caminho
pelo `public/assets/.manifest.json` e o arquivo digerido tem que existir em
disco. E nada no deploy gerava isso:
- a linha `RUN bundle exec rails assets:precompile` do `Dockerfile` estava
**comentada** (e não adiantaria descomentar: o compose monta `.:/app` por cima
e apagaria o `public/assets` da imagem);
- `bin/docker-boot` não tocava no assunto;
- `/public/assets` é **gitignorado**.
Resultado: o que existia no servidor era o resto de algum `precompile` manual
antigo. Todo JS alterado depois dele virava 404 silencioso — silencioso porque a
tela **continua abrindo** (o Tailwind vem de CDN e os ícones são `public/`
direto), só sem comportamento.
### A correção
Novo **passo 5** no `bin/docker-boot`, que roda em todo start de container — logo,
em todo deploy:
```bash
if [ "${RAILS_ENV:-development}" = "production" ]; then
rm -rf public/assets tmp/cache/assets
bundle exec rails assets:precompile
else
rm -rf public/assets # em dev o Propshaft calcula o digest ao vivo
fi
```
- **Limpa antes de pré-compilar**: sem isso um manifesto antigo convive com
arquivos novos e a divergência volta na primeira alteração de JS. Assim
manifesto e arquivos saem sempre da **mesma execução**. (`rm -rf` em vez de
`rails assets:clobber`: faz o mesmo sem pagar outro boot do Rails.)
- **Falha não derruba o boot**: `exit 1` aqui viraria loop de reinício com o
`restart: unless-stopped` e deixaria todo mundo de fora. O erro é gritado no
log — o que não podia continuar é a falha **muda**.
- **Em development apaga `public/assets`**: sobra de precompile faz o Propshaft
voltar a resolver pelo manifesto, e aí alteração de JS só aparece depois de
recompilar — parece cache do navegador quando não é.
### Como conferir depois do deploy
```bash
# 1. os assets do HTML respondem na ORIGEM (querystring fura o cache do CDN)
curl -s https://teste.reemtransportes.com.br/auth/login \
| grep -o '/assets/controllers/[a-z_]*-[a-f0-9]*\.js' | sort -u \
| while read a; do
printf "%s -> " "$a"
curl -s -o /dev/null -w "%{http_code}\n" "https://teste.reemtransportes.com.br$a?cb=$RANDOM"
done
# 2. o passo apareceu no log do boot
docker compose logs app | grep '\[boot\] assets'
```
Todos têm que responder **200**. Se algum der 404, o `precompile` não rodou ou
falhou — o log do passo 5 diz qual dos dois.
## 📂 Arquivos
```
app/controllers/users/sessions_controller.rb # descarta o "faça login" vermelho
bin/docker-boot # passo 5: clobber + precompile
Dockerfile # tira a linha de precompile morta e explica por quê
```
</details>

View File

@@ -5,6 +5,19 @@ class Users::SessionsController < Devise::SessionsController
# GET /users/sign_in
def new
@pin_login = params[:pin].present?
# "Para continuar, faça login." NÃO é erro: é o que o Devise sempre grava
# quando alguém abre o sistema deslogado (raiz -> authenticate_user! -> aqui).
# Como `flash[:alert]` ele saía em vermelho DUAS vezes — o toast do layout e
# a caixa desta tela — e a primeira entrada do dia parecia falha do sistema
# (foi relatado como bug: "a primeira vez que entra dá a tela de erro").
#
# A mensagem é descartada: a tela já é o formulário de login, ela não
# acrescenta nada. NÃO troque isso por apagar todo `flash[:alert]` no `new`:
# senha errada, conta inativa e `timeout` ("Sua sessão expirou") chegam por
# aqui pelo mesmo caminho, e essas a pessoa PRECISA ver em vermelho.
flash.delete(:alert) if flash[:alert] == I18n.t('devise.failure.unauthenticated', default: nil)
super
end

View File

@@ -16,8 +16,10 @@
# gem nova no Gemfile não chega no container só com --build.
# 4. migrations — db:prepare: cria o banco se não existir, roda as migrations
# pendentes e só faz seed em banco recém-criado (idempotente).
# 5. cron — grava o crontab do config/schedule.rb e sobe o daemon.
# 6. Puma — processo em primeiro plano (é ele que segura o container).
# 5. assets — em production o Propshaft NÃO compila sob demanda: o
# arquivo digerido tem que estar em public/assets.
# 6. cron — grava o crontab do config/schedule.rb e sobe o daemon.
# 7. Puma — processo em primeiro plano (é ele que segura o container).
set -uo pipefail
log() { echo "[boot] $*"; }
@@ -85,7 +87,53 @@ for i in $(seq 1 $tentativas); do
sleep 6
done
# ── 5. Cron (best-effort) ────────────────────────────────────────────────────
# ── 5. Assets ────────────────────────────────────────────────────────────────
# POR QUE ISTO EXISTE: com RAILS_ENV=production — que é como o teste e o servidor
# rodam hoje — o Propshaft não serve asset dinamicamente. Ele resolve o caminho
# pelo public/assets/.manifest.json e quem entrega o arquivo é o servidor de
# estáticos; sem o arquivo em disco a requisição cai no roteador do Rails e volta
# 404 (a página public/404.html, não um erro de asset — por isso é difícil de
# reconhecer). Nada no deploy gerava esses arquivos: a linha do Dockerfile está
# comentada de propósito (o bind-mount `.:/app` do compose cobriria o que ela
# gerasse na imagem) e o boot não tocava no assunto. O que existia em
# public/assets no servidor era o resto de algum precompile manual antigo.
#
# CASO REAL (28/08/2026): o HTML pedia
# /assets/controllers/carrossel_controller-913b47f7.js e esse arquivo não existia
# mais -> 404 -> "Failed to fetch dynamically imported module" no console, com o
# carrossel (a paginação por tamanho de tela) e o editor de romaneio MORTOS no
# ambiente de teste. Só esses dois quebraram porque são os dois mais recentes:
# os outros dez controllers ainda vinham do cache do Cloudflare (max-age de 1
# ano), o que escondeu o problema por dias e fez parecer defeito de dois arquivos.
#
# A LIMPEZA ANTES do precompile é de propósito: sem ela um manifesto antigo
# convive com arquivos novos e a divergência volta na primeira alteração de JS.
# Assim manifesto e arquivos saem sempre da MESMA execução. É `rm -rf` e não
# `rails assets:clobber` porque faz exatamente o mesmo (essas duas pastas), sem
# pagar um boot inteiro do Rails só para apagar diretório.
if [ "${RAILS_ENV:-development}" = "production" ]; then
log "assets: limpando restos e pré-compilando (public/assets)"
rm -rf public/assets tmp/cache/assets
if bundle exec rails assets:precompile; then
log "assets prontos"
else
# NÃO sai com erro: sem JS o sistema fica ruim, mas ainda dá para entrar e
# ler os dados — e um `exit 1` aqui viraria loop de reinício com o
# `restart: unless-stopped`, deixando TODO MUNDO de fora. O aviso é gritado
# no log para não repetir a história de descobrir dias depois.
log "ERRO: assets:precompile falhou — o sistema vai subir SEM JS/CSS novos."
log " Rode 'docker compose exec app bundle exec rails assets:precompile'"
log " e veja o erro completo."
fi
else
# Em development o Propshaft calcula o digest do arquivo atual a cada
# requisição. Sobra de precompile faz ele voltar a resolver pelo manifesto, e
# aí toda alteração de JS/CSS só aparece depois de recompilar — parece que o
# navegador está com cache quando não está.
rm -rf public/assets
fi
# ── 6. Cron (best-effort) ────────────────────────────────────────────────────
# Best-effort de propósito: agendamento quebrado atrasa um relatório, mas NUNCA
# pode impedir o sistema de subir. As variáveis do banco chegam ao job pelo
# dotenv quando o rake carrega o Rails — o cron não precisa herdar o ENV.
@@ -95,6 +143,6 @@ else
log "AVISO: cron/crontab indisponível (rode com --build) — seguindo sem agendamento"
fi
# ── 6. Puma ──────────────────────────────────────────────────────────────────
# ── 7. Puma ──────────────────────────────────────────────────────────────────
log "subindo o Rails"
exec bundle exec rails s -b 0.0.0.0