diff --git a/.env.example b/.env.example index e7a8efb..0f1ec35 100644 --- a/.env.example +++ b/.env.example @@ -18,11 +18,11 @@ TZ=America/Sao_Paulo # ── Porta publicada no host ─────────────────────────────── # A porta DENTRO do container é sempre 3000; esta é a do servidor. -# Só precisa mudar quando outro stack já ocupa a porta no mesmo host — -# é o caso do ambiente de TESTE, que usa 3001 (a 3000 está ocupada). -# Sem esta variável o compose assume 3000. +# Neste branch (teste) o padrão do docker-compose.yml JÁ É 3001, porque a 3000 +# está ocupada por outro stack no mesmo NAS. Esta linha só é necessária para +# usar uma porta diferente da padrão. # ⚠️ Tem que casar com o destino do proxy reverso (DSM / Cloudflare). -PORTA_APP=3000 +PORTA_APP=3001 # ── Banco de dados (seu PostgreSQL já existente) ───────────── # Aponte DB_HOST para o IP ou hostname do seu servidor PostgreSQL @@ -91,9 +91,21 @@ SMTP_DOMAIN=gade.com.br NOTIFICACAO_SECRET= # ── App ─────────────────────────────────────────────────────── -APP_HOST=localhost:3000 +# ⚠️ SÓ O HOST, sem "https://". O código monta os links como +# "https://#{APP_HOST}/motorista" (consolidacao_mailer.rb, +# notificacao_service.rb, gatilhos.rb): com o esquema aqui sai +# "https://https://..." e todo link de e-mail e de WhatsApp quebra. +# O docker-compose.yml já traz o host do ambiente como padrão — esta linha só +# é necessária para apontar para outro endereço. +APP_HOST=teste.reemtransportes.com.br APP_NAME=Reem Logística +# Hosts extras aceitos além do APP_HOST, separados por vírgula (só vale com +# RAILS_ENV=production, onde o Rails checa o cabeçalho Host). +# Use quando o sistema também for acessado por IP:porta na rede interna — +# sem isso o Rails responde "Blocked hosts: :". +HOSTS_PERMITIDOS=100.75.222.23 + # ── WhatsApp (ponte própria — sessão pareada por QR code) ────────────────── # Token compartilhado entre o Rails e o container `whatsapp`. Qualquer string # longa e aleatória: `openssl rand -hex 32`. Sem ele a ponte recusa TUDO. diff --git a/config/environments/production.rb b/config/environments/production.rb index ea00f54..7eeee1a 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -21,11 +21,42 @@ Rails.application.configure do config.action_mailer.perform_caching = false config.action_mailer.default_url_options = { host: ENV.fetch("APP_HOST", "localhost:3000") } - config.i18n.fallbacks = true + # `= true` significa "caia no default_locale" — e o default AQUI é o próprio + # pt-BR, então o fallback apontava para si mesmo e qualquer chave ausente + # virava "Translation missing" NA TELA DO USUÁRIO (aconteceu em 27/08/2026 na + # tela de login, com devise.failure.unauthenticated). Em development o + # config/application.rb já usa [:en] e por isso o problema não aparecia lá. + # Mantido igual ao application.rb: chave faltando mostra o texto em inglês, + # que é feio mas legível — nunca a mensagem de erro do i18n. + config.i18n.fallbacks = [:en] config.active_support.report_deprecations = false config.active_record.dump_schema_after_migration = false - # Aceita o host configurado no .env + # ── Hosts aceitos (proteção contra DNS rebinding) ───────────────────────── + # Regra do Rails: lista VAZIA = aceita qualquer Host; lista com QUALQUER item + # = só o que estiver nela entra. Por isso a linha abaixo, que parece só + # "liberar o APP_HOST", na verdade LIGA a verificação e bloqueia todo o resto + # com "ActionDispatch::HostAuthorization ... Blocked hosts: ". + # + # ⚠️ APP_HOST tem que vir SEM "https://": com o esquema, este split libera um + # host chamado "https" e nada funciona. config.hosts << ENV["APP_HOST"]&.split(":")&.first if ENV["APP_HOST"].present? + + # O próprio servidor. Sem isto, um `curl http://127.0.0.1:3001/` de dentro do + # NAS — que é o teste que separa "app caiu" de "proxy errado" — volta + # bloqueado e faz parecer que a aplicação está quebrada. + config.hosts += ["localhost", "127.0.0.1", "[::1]"] + + # Endereços extras, separados por vírgula. Caso real (27/08/2026): o acesso + # pela rede interna chega com `Host: 100.75.222.23:3001` (o IP do NAS), que + # não bate com o domínio público e caía em "Blocked hosts". A porta é + # ignorada pelo Rails na comparação — basta o IP/nome. + # HOSTS_PERMITIDOS=100.75.222.23,10.0.0.20 + ENV.fetch("HOSTS_PERMITIDOS", "").split(",").map(&:strip).reject(&:empty?).each do |permitido| + config.hosts << permitido + end + + # Escotilha de emergência: desliga a verificação por completo. Último recurso — + # com ela ligada, qualquer domínio apontado para este servidor é aceito. config.hosts.clear if ENV["RAILS_ALLOW_ALL_HOSTS"] == "true" end diff --git a/config/locales/pt-BR.yml b/config/locales/pt-BR.yml index d43cf2a..0162c57 100644 --- a/config/locales/pt-BR.yml +++ b/config/locales/pt-BR.yml @@ -8,8 +8,48 @@ pt-BR: default: "%d/%m/%Y %H:%M" short: "%d/%m %H:%M" - # Mensagem de login recusado para conta desativada (User#inactive_message). - # Sem esta chave, o Devise mostraria o símbolo cru "conta_desativada". + # ── Devise ──────────────────────────────────────────────────────────────── + # POR QUE ESTÁ TUDO AQUI E NÃO SÓ A CHAVE DA CONTA DESATIVADA: + # o projeto não usa a gem `devise-i18n`, então as únicas traduções do Devise + # são estas. Em development a ausência passava despercebida porque o + # config/application.rb faz `i18n.fallbacks = [:en]` e o texto vinha em inglês + # do próprio gem. Em produção o `fallbacks` apontava para o pt-BR (ele mesmo) + # e a tela de login exibiu, para o usuário final: + # "Translation missing. Options considered were: pt-BR.devise.failure.unauthenticated" + # Traduzir de fato é melhor do que cair no inglês: quem usa o sistema é + # operador e motorista brasileiros. devise: failure: + already_authenticated: "Você já está autenticado." + inactive: "Sua conta ainda não foi ativada." + invalid: "%{authentication_keys} ou senha inválidos." + not_found_in_database: "%{authentication_keys} ou senha inválidos." + timeout: "Sua sessão expirou. Faça login novamente para continuar." + unauthenticated: "Para continuar, faça login." + # Conta desativada pelo administrador (User#inactive_message). + # Sem esta chave, o Devise mostraria o símbolo cru "conta_desativada". conta_desativada: "Sua conta está desativada. Fale com um administrador." + sessions: + signed_in: "Login efetuado com sucesso." + signed_out: "Logout efetuado com sucesso." + already_signed_out: "Logout efetuado com sucesso." + passwords: + send_instructions: "Em alguns minutos você receberá um e-mail com instruções para redefinir sua senha." + send_paranoid_instructions: "Se o seu e-mail existir em nosso cadastro, você receberá um link para redefinir a senha." + updated: "Sua senha foi alterada com sucesso. Você já está autenticado." + updated_not_active: "Sua senha foi alterada com sucesso." + no_token: "Esta página só pode ser acessada pelo link do e-mail de redefinição de senha. Peça um novo link." + mailer: + reset_password_instructions: + subject: "Instruções para redefinir sua senha" + + # Chaves que o Devise procura na RAIZ do locale (não dentro de `devise:`). + errors: + messages: + not_found: "não encontrado" + already_confirmed: "já foi confirmado, tente fazer login" + not_locked: "não está bloqueado" + expired: "expirou, solicite um novo" + not_saved: + one: "1 erro impediu que este %{resource} fosse salvo:" + other: "%{count} erros impediram que este %{resource} fosse salvo:" diff --git a/docker-compose.yml b/docker-compose.yml index 00c9a9c..cbfde5b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -10,7 +10,10 @@ services: # POR QUE NÃO É FIXA: o mesmo NAS roda mais de um stack, e dois containers # não publicam a mesma porta ("driver failed programming external # connectivity ... port is already allocated"). O ambiente de TESTE usa a - # 3001; a 3000 já está ocupada. + # 3001; a 3000 já está ocupada. Por isso o PADRÃO aqui é 3001 e não 3000: + # é o valor que mantém ESTE ambiente de pé se o .env se perder — foi + # exatamente o que aconteceu, e o padrão 3000 fez o container morrer com + # "port is already allocated". Na produção (branch main) o padrão é 3000. # # Isto vivia como alteração LOCAL no docker-compose.yml do servidor — e em # 27/08/2026 um `git reset --hard` levou junto, derrubando o ambiente com o @@ -21,7 +24,7 @@ services: # ⚠️ Ao mudar, ajuste JUNTO o proxy reverso (DSM/Cloudflare) — senão o 502 # só muda de lugar. ports: - - "${PORTA_APP:-3000}:3000" + - "${PORTA_APP:-3001}:3000" env_file: .env environment: DATABASE_URL: ${DATABASE_URL} @@ -37,6 +40,24 @@ services: # ajuste sem rebuild — basta TZ=... no .env. Ver deploy/ntp-seguro.sh: # a HORA em si é do host, isto aqui só define o fuso de leitura. TZ: ${TZ:-America/Sao_Paulo} + + # Endereço público e nome do sistema. Ficam aqui com PADRÃO porque não são + # segredo — assim um .env perdido não leva junto a configuração do + # ambiente. O .env continua mandando: se a variável existir lá, ela vence. + # + # ⚠️ APP_HOST é SÓ O HOST, sem "https://". O código monta os links como + # "https://#{APP_HOST}/motorista" (consolidacao_mailer.rb, + # notificacao_service.rb, gatilhos.rb) — com o esquema no valor sai + # "https://https://..." e todo link de e-mail e WhatsApp quebra. + APP_HOST: ${APP_HOST:-teste.reemtransportes.com.br} + APP_NAME: "${APP_NAME:-Reem Logística}" + # Hosts extras aceitos além do APP_HOST (vírgula separa vários). Com + # RAILS_ENV=production o Rails só aceita os hosts listados; o acesso pela + # rede interna chega com o IP do NAS e era recusado com + # "Blocked hosts: 100.75.222.23:3001". Ver config/environments/production.rb. + HOSTS_PERMITIDOS: ${HOSTS_PERMITIDOS:-100.75.222.23} + # A ponte do WhatsApp é alcançada pelo nome do serviço na rede do compose. + WHATSAPP_URL: ${WHATSAPP_URL:-http://whatsapp:3001} volumes: - ".:/app" - "bundle_cache:/usr/local/bundle"