Atualização de Read.me e \correção da autorização no primeiro login
This commit is contained in:
@@ -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
153
README.md
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user