Pendências — Migração de Rotas EN → PT-BR

Documento de acompanhamento da migração das rotas públicas de inglês para português brasileiro.

Data: 2026-06-30 Branch: feat/rotas-pt-br Contexto: Migração das rotas voltadas ao usuário de inglês para português brasileiro. As rotas /about, /store, /auth, /user, /style e as subrotas de /admin/* foram renomeadas para PT-BR. As raízes /admin, /ai, /blog, /edu e os endpoints /api/* foram mantidos em inglês (caráter técnico/universal).

O código já está migrado, o build passa e o astro check não introduziu erros novos. O que falta é configurar serviços externos e monitorar a reindexação.


1. Supabase — Atualizar URLs de redirecionamento de auth

Por que é pendência: O Supabase Auth envia e-mails (confirmação de cadastro, recuperação de senha, mudança de e-mail) com um link que aponta para uma URL do site. Essa URL é configurada no dashboard do Supabase, não no código. Com a migração, o callback agora é /entrar/callback (antes /auth/callback).

O arquivo public/_redirects tem uma regra /auth/callback → /entrar/callback (301), então links antigos em trânsito ainda funcionam — mas os novos e-mails disparados após o deploy devem apontar direto para a URL nova para evitar um redirect desnecessário.

Como resolver

  1. Acesse https://supabase.com/dashboard/project/_/auth/url-configuration (substitua _ pelo ID do projeto).
  2. Em Site URL, atualize para:
    https://valmor.net.br/entrar/callback
  3. Em Redirect URLs (allowlist), adicione (mantendo as existentes):
    https://valmor.net.br/entrar/callback
    https://preview.webapp-civ.pages.dev/entrar/callback
    http://localhost:4321/entrar/callback
    http://localhost:4322/entrar/callback
  4. Salve. Não é necessário reiniciar o serviço.

Verificação

Após configurar, criar um usuário novo (ou pedir reset de senha) e confirmar que o link no e-mail caí diretamente em /entrar/callback?... (sem redirect 301).


2. Google Search Console — Reenviar sitemap e monitorar reindexação

Por que é pendência: O Google indexou as URLs antigas (/about, /store, etc.). Os redirects 301 do public/_redirects preservam o ranking, mas é necessário reenviar o sitemap novo para acelerar a reindexação. Pode levar de dias a semanas para o Google consolidar tudo.

Como resolver

  1. Acesse https://search.google.com/search-console (propriedade valmor.net.br).
  2. Em Sitemaps:
    • Remova (ou aguarde expirar) o sitemap antigo se houver.
    • Envie https://valmor.net.br/sitemap-index.xml.
  3. Em Cobertura / Páginas, observe nas próximas semanas:
    • As URLs antigas vão aparecer como redirecionadas (301).
    • As URLs novas vão ganhar indexação progressiva.
  4. Manter o arquivo public/_redirects no ar por no mínimo 6 meses após o deploy. Remover antes disso quebra links externos/indexados e perde ranking.

Verificação

No Search Console, em Inspeção de URL, testar:

  • https://valmor.net.br/sobre → deve constar como indexada após alguns dias.
  • https://valmor.net.br/about → deve constar como “Redirecionada”.

3. Deploy em produção — Cloudflare Pages

Por que é pendência: A migração só entra em vigor após o deploy. O public/_redirects é processado automaticamente pelo Cloudflare Pages na borda.

Como resolver

# Deploy de preview primeiro (recomendado p/ testar _redirects em ambiente real)
npm run deploy:preview

# Após validar o preview:
npm run deploy

Verificação pós-deploy

# Testar 301s (deve retornar Location: /sobre, /loja, etc.)
curl -sI https://valmor.net.br/about | rg -i '^(http|location)'
curl -sI https://valmor.net.br/store | rg -i '^(http|location)'
curl -sI https://valmor.net.br/auth/signin | rg -i '^(http|location)'
curl -sI https://valmor.net.br/user/profile | rg -i '^(http|location)'
curl -sI https://valmor.net.br/style | rg -i '^(http|location)'

# Confirmar que rotas novas respondem 200
curl -sI https://valmor.net.br/sobre | rg '^http'
curl -sI https://valmor.net.br/loja | rg '^http'

4. (Opcional) Commit e PR

Por que é pendência: A branch feat/rotas-pt-br está local, sem commit. O usuário deve revisar as mudanças antes de ir para master.

Como resolver

# Revisar mudancas
git diff master..HEAD --stat    # depois de commitar
git status

# Commitar (apos revisao)
git add -A
git commit -m "feat: traduz rotas EN->PT-BR (/sobre /loja /entrar /conta /estilo)

- Renomeia pastas em src/pages/
- Atualiza nav, middleware, sitemap filter
- Adiciona public/_redirects com 301 para todas URLs antigas
- Mantem /admin /ai /blog /edu /api/* em ingles"

# Merge para master (opcional - pode ser via PR)
git checkout master
git merge feat/rotas-pt-br
git push origin master

Notas adicionais

  • APIs mantidas em EN: Os endpoints /api/* (incluindo /api/auth/*, /api/user/*, /api/store/*) não foram traduzidos. São URLs internas chamadas via fetch() pelo frontend, não visíveis ao usuário.
  • Tokens de pedido/orçamento: As URLs públicas /loja/pedido/[token] e /loja/orcamento/[token] já estavam em PT; apenas o prefixo mudou de /store/ para /loja/. O _redirects cobre os links antigos. Como o projeto está em versão 0.1.0 (sem tráfego significativo), o impacto é mínimo.
  • Manter _redirects por ≥6 meses para preservar qualquer link externo ou indexado pelo Google.

5. Supabase / Backend — Auditoria pós-migração

Status: Auditoria completa. Nenhuma alteração estrutural necessária no banco. Abaixo os pontos encontrados e como tratá-los.

5.1. page_views.path — histórico duplicado no analytics

O que acontece: A tabela page_views (migration 009) registra cada visita com o path exato da URL. Após o deploy, novos registros terão paths PT (/sobre, /loja, etc.) e o histórico anterior terá paths EN (/about, /store, etc.).

O dashboard /admin/analytics/site agrega por path exato (inc(pageCounts, v.path)), então a mesma página aparece duas vezes no ranking de páginas populares (ex.: /about e /sobre como entradas separadas).

Impacto: Cosmético. Não quebra nada. Para um projeto em 0.1.0 com pouco tráfeye, é desnecessário resolver. Se quiser limpar, rodar uma query de normalização uma única vez, após o deploy:

-- Rodar no Supabase SQL Editor após o deploy.
-- Reescreve paths antigos EN -> PT no histórico do analytics.
UPDATE page_views SET path = '/sobre'              WHERE path IN ('/about', '/about/');
UPDATE page_views SET path = '/sobre/termos'       WHERE path IN ('/about/terms', '/about/terms/');
UPDATE page_views SET path = '/sobre/privacidade'  WHERE path IN ('/about/privacy', '/about/privacy/');
UPDATE page_views SET path = REPLACE(path, '/store/services', '/loja/servicos') WHERE path LIKE '/store/services%';
UPDATE page_views SET path = REPLACE(path, '/store/checkout', '/loja/checkout') WHERE path LIKE '/store/checkout%';
UPDATE page_views SET path = REPLACE(path, '/store/success',  '/loja/sucesso')  WHERE path LIKE '/store/success%';
UPDATE page_views SET path = REPLACE(path, '/store/orcamento', '/loja/orcamento') WHERE path LIKE '/store/orcamento%';
UPDATE page_views SET path = REPLACE(path, '/store/pedido',    '/loja/pedido')    WHERE path LIKE '/store/pedido%';
UPDATE page_views SET path = REPLACE(path, '/store/estampas',  '/loja/estampas')  WHERE path LIKE '/store/estampas%';
UPDATE page_views SET path = REPLACE(path, '/store/', '/loja/') WHERE path LIKE '/store/%';
UPDATE page_views SET path = '/loja'               WHERE path = '/store';
UPDATE page_views SET path = REPLACE(path, '/auth/forgot-password', '/entrar/recuperar-senha') WHERE path LIKE '/auth/forgot-password%';
UPDATE page_views SET path = REPLACE(path, '/auth/reset-password',  '/entrar/redefinir-senha') WHERE path LIKE '/auth/reset-password%';
UPDATE page_views SET path = REPLACE(path, '/auth/verify-email',    '/entrar/verificar-email') WHERE path LIKE '/auth/verify-email%';
UPDATE page_views SET path = REPLACE(path, '/auth/callback',        '/entrar/callback')        WHERE path LIKE '/auth/callback%';
UPDATE page_views SET path = REPLACE(path, '/auth/signin',          '/entrar')                 WHERE path LIKE '/auth/signin%';
UPDATE page_views SET path = '/entrar'              WHERE path IN ('/auth', '/auth/');
UPDATE page_views SET path = REPLACE(path, '/user/profile',   '/conta/perfil')     WHERE path LIKE '/user/profile%';
UPDATE page_views SET path = REPLACE(path, '/user/addresses', '/conta/enderecos')  WHERE path LIKE '/user/addresses%';
UPDATE page_views SET path = REPLACE(path, '/user/orders',    '/conta/pedidos')    WHERE path LIKE '/user/orders%';
UPDATE page_views SET path = REPLACE(path, '/user/orcamentos','/conta/orcamentos') WHERE path LIKE '/user/orcamentos%';
UPDATE page_views SET path = REPLACE(path, '/user/assets',    '/conta/designs')    WHERE path LIKE '/user/assets%';
UPDATE page_views SET path = REPLACE(path, '/user/payment',   '/conta/pagamento')  WHERE path LIKE '/user/payment%';
UPDATE page_views SET path = REPLACE(path, '/user/settings',  '/conta/ajustes')    WHERE path LIKE '/user/settings%';
UPDATE page_views SET path = '/conta'               WHERE path IN ('/user', '/user/');
UPDATE page_views SET path = REPLACE(path, '/style/theme', '/estilo/tema') WHERE path LIKE '/style/theme%';
UPDATE page_views SET path = REPLACE(path, '/style/ui',    '/estilo/ui')    WHERE path LIKE '/style/ui%';
UPDATE page_views SET path = REPLACE(path, '/style/specs', '/estilo/specs') WHERE path LIKE '/style/specs%';
UPDATE page_views SET path = '/estilo'              WHERE path IN ('/style', '/style/');

-- Subrotas de /admin/* (segunda etapa)
UPDATE page_views SET path = REPLACE(path, '/admin/products/materials', '/admin/produtos/materiais') WHERE path LIKE '/admin/products/materials%';
UPDATE page_views SET path = REPLACE(path, '/admin/products/pricing',   '/admin/produtos/precos')    WHERE path LIKE '/admin/products/pricing%';
UPDATE page_views SET path = REPLACE(path, '/admin/products/new',       '/admin/produtos/novo')      WHERE path LIKE '/admin/products/new%';
UPDATE page_views SET path = REPLACE(path, '/admin/products/',          '/admin/produtos/')          WHERE path LIKE '/admin/products/%';
UPDATE page_views SET path = '/admin/produtos'      WHERE path = '/admin/products';
UPDATE page_views SET path = REPLACE(path, '/admin/orders/',    '/admin/pedidos/')    WHERE path LIKE '/admin/orders/%';
UPDATE page_views SET path = '/admin/pedidos'        WHERE path = '/admin/orders';
UPDATE page_views SET path = '/admin/cupons'         WHERE path = '/admin/vouchers';
UPDATE page_views SET path = '/admin/arquivos'       WHERE path = '/admin/assets';
UPDATE page_views SET path = '/admin/usuarios'       WHERE path = '/admin/users';
UPDATE page_views SET path = REPLACE(path, '/admin/analytics/', '/admin/estatisticas/') WHERE path LIKE '/admin/analytics/%';
UPDATE page_views SET path = '/admin/estatisticas'   WHERE path = '/admin/analytics';
UPDATE page_views SET path = '/admin/configuracoes'  WHERE path = '/admin/settings';
UPDATE page_views SET path = '/admin/diagnosticos'   WHERE path = '/admin/auth-diagnostics';

⚠️ Não empacotar isso como migration. É uma operação one-shot de saneamento de dados históricos. Rodar manualmente após o deploy e arquivar.

5.2. Documentação do Supabase — já atualizada

Foram atualizados como parte desta migração:

  • db/supabase/templates/SPEC.md — referências a /auth/callback, /store/checkout, src/pages/auth/callback.astro, signin.astro corrigidas para /entrar/callback, /loja/checkout, src/pages/entrar/callback.astro, entrar/index.astro.

5.3. Comentário em migration antiga (não alterar)

  • db/supabase/migrations/031_quote_share_token.sql:5 ainda menciona /store/orcamento/{token} no cabeçalho. Migrations são imutáveis depois de aplicadas em produção — o cabeçalho é histórico e reflete o estado correto à época. Não alterar.

5.4. Itens verificados — nenhuma ação necessária

ItemVerificaçãoResultado
Templates HTML (confirm-signup, recovery, email-change)Paths hardcoded✅ Só usam {{ .ConfirmationURL }} (variável) + domínio raiz
Tabela ordersColunas de URL✅ Guarda apenas order_ref e public_token; URL montada em runtime
Tabela quote_requestsColuna share_token✅ Guarda apenas o token; URL montada via quoteShareUrl() (já PT)
Tabela profilesCampos de URL✅ Sem colunas com paths de rota
Tabela vouchersURL de resgate✅ Sem URL hardcoded; o code é validado via RPC
Tabela site_settings (migration 032)Chaves com URL✅ Só guarda default_ai_model
Cron jobs (migrations 013, 014)URLs hardcoded✅ Cron 014 pinga REST do próprio Supabase; cron 013 sonda AI providers
Funções RPCURLs externas✅ Nenhuma gera URL de rota interna
RLS policiesDependência de path✅ Todas baseadas em user_id/role, não em paths
Coluna next_url/redirect_url em tabelasExistência✅ Nenhuma tabela tem coluna com esse nome

5.5. Frontend → API (mantido EN, sem impacto)

As páginas chamam os endpoints via fetch() com paths em inglês. Isso é intencional e correto — APIs não são visíveis ao usuário. Confirmado:

  • fetch("/api/auth/login"), fetch("/api/auth/logout"), etc. → mantidos
  • fetch("/api/user/update"), fetch("/api/user/avatar") → mantidos
  • fetch("/api/store/vouchers/validate") → mantido
  • fetch("/api/orders/pix"), fetch("/api/quotes/[id]/accept") → mantidos
  • fetch("/api/admin/orders/update-status"), fetch("/api/admin/orders/delete") → mantidos
  • fetch("/api/admin/pricing") → mantido

6. Subrotas do /admin/* (segunda etapa da migração)

Status: Adicionado após usuário solicitar que as subrotas do painel admin também fossem traduzidas. Build passa, typecheck com 83 erros (= baseline, 0 novos), smoke test com 17 rotas admin respondendo 302 (área protegida por middleware).

6.1. Mapeamento aplicado

Antes (EN)Agora (PT)
/admin/orders (e /admin/orders/[id])/admin/pedidos (e /admin/pedidos/[id])
/admin/vouchers/admin/cupons
/admin/products (raiz)/admin/produtos
/admin/products/pricing/admin/produtos/precos
/admin/products/materials/admin/produtos/materiais
/admin/products/new/admin/produtos/novo
/admin/assets/admin/arquivos
/admin/users/admin/usuarios
/admin/analytics/site/admin/estatisticas/site
/admin/analytics/ai/admin/estatisticas/ai
/admin/settings/admin/configuracoes
/admin/auth-diagnostics/admin/diagnosticos
/admin (raiz)mantém (dashboard)
/admin/orcamentos (e [id])mantém (já era PT)
/admin/blog (e /editor)mantém (universal)
/admin/ai (e /models, /providers)mantém (sigla)
/admin/setupmantém (one-shot técnico)

6.2. _redirects atualizado

O arquivo public/_redirects ganhou a seção /admin/* com regras 301 para todas as subrotas renomeadas (arquivos products/*, orders/*, etc.). A raiz /admin não tem redirect (continua válida).

6.3. nav/admin.ts atualizado

Todos os href do menu lateral do painel admin apontam para as novas rotas PT-BR. Labels já eram PT antes desta migração.

6.4. Bug pré-existente identificado (não resolvido)

Em src/pages/admin/usuarios.astro:261 há um fetch("/api/admin/contas/update-role") — esse endpoint não existe (o correto seria /api/admin/users/update-role). É bug pré-existente da fase EN. Não resolvido nesta migração (escopo diferente), mas fica registrado aqui para tratamento futuro.

Outro bug pré-existente (este sim corrigido nesta migração): o href /admin/contas em src/pages/admin/index.astro:184 apontava para rota inexistente. Foi corrigido para /admin/usuarios.

6.5. Itens verificados — nenhuma ação adicional

ItemResultado
/api/admin/* (settings, vouchers, orders/delete, etc.)✅ Mantidos em EN (internos)
components/admin/* (imports internos)✅ Pastas físicas intactas (Sidebar/, assets/, icons/, ui/, ai/)
lib/store/pricing/anchor.ts (deep-link produtos↔precos)✅ Atualizado para /admin/produtos/precos#...
Subpastas dinâmicas ([id], [model], [provider])✅ Mantidas — só o prefixo mudou