Apos supabase stop/start a GUC de exposicao dos schemas tenant some -> 404 nas tabelas tenant. HANDOFF Passo 0 agora manda rodar refresh_pgrst_schemas() pos-start. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
10 KiB
Handoff — Onde paramos, Riscos e Passo a passo de teste
Estado consolidado dos dois épicos grandes (schema-per-tenant + freemium/PLG). Última atualização: 2026-06-13. Branch de trabalho:
feat/schema-per-tenant(base) efeat/freemium-plg(empilhada — contém tudo).mainsegue no modelo RLS antigo.
1. Onde paramos (estado atual)
Branches
main— modelo RLS-only (produção atual). Recebeu só F0/F1/F2 aditivos da schema-per-tenant.feat/schema-per-tenant— migração completa F0→F6.2 + wiring + F6.4. F6.3 DROP NÃO aplicado.feat/freemium-plg— ramificada da schema-per-tenant, contém TODO o freemium (F1/F2/F3) + os dois runbooks de deploy + este handoff. É a branch a deployar (tem os dois épicos).
Banco LOCAL (Docker supabase_db_agenciapsi-primesakai)
Está no estado schema-per-tenant + freemium aplicado:
- Schemas
tenant_<slug>existem (9 tenants clonados) + dados COPIADOS (espelho ainda empublic). - Todas as migrations + todos os
manual/*.supabase_admin.sqlaplicados, EXCETO o F6.3 DROP. enable_confirmationsestátruenoconfig.tomlmas só ativa após reiniciar o stack.
Schema-per-tenant — ✅ feito / ⏳ pendente
- ✅ Estrutura, helpers, template, clone/drop, provisionamento, 66 funções migradas, dados dos 9 tenants copiados+verificados, PostgREST dinâmico (local), frontend/edge roteando por schema.
- ⏳ F6.3 DROP (remove o espelho em
public) — preparado, NÃO aplicado. Aguarda teste no browser + OK + backup fresco. (task #7) - 📄 Deploy:
docs/DEPLOY_SCHEMA_PER_TENANT.md.
Freemium/PLG — ✅ feito / ⏳ pendente
- ✅ F1 limite de pacientes (trigger runtime + toast + Upgrade PRO).
- ✅ F2 self-service (confirmação de e-mail, RPCs idempotentes, signup reescrito, /onboarding,
welcome email, vitrine "Grátis") + fix de regressão do audit em
tenant_members. - ✅ F3 4 extras (blacklist, /saas/usuarios + notify devs, esqueci-email, root_redirect).
- ⏳ F4 deploy (hosted) — runbook em
docs/DEPLOY_FREEMIUM_F4.md. Não deployado. - ⏳ Teste local ponta-a-ponta — exige reiniciar o stack (seção 3).
Tudo commitado e pushado em feat/freemium-plg. Nada pendente no working tree
(só .env/dashboard/.claude locais, intencionalmente fora).
2. Riscos (todos)
🔴 Críticos
- PostgREST dinâmico no hosted — a exposição de schemas tenant usa
ALTER ROLE authenticator SET pgrst.db_schemas. Pode ser restrito no hosted. Se falhar, o app novo recebe 404 nas tabelas tenant. Testar cedo (Fase C do runbook); fallback = Exposed schemas no dashboard (estático → problema com signup self-service). Decidir antes de abrir signup. - F6.3 DROP é irreversível — remove as tabelas tenant de
public. Só após dias de soak no modelo novo + backup fresco. Rollback = restore (f6_3_ROLLBACK.md). - Confirmação de e-mail + SMTP do GoTrue (hosted) — com
Confirm email = ON, se o SMTP do GoTrue não estiver configurado com provedor real, ninguém consegue logar (o link de confirmação não chega). Configurar SMTP no dashboard ANTES de ligar a confirmação.
🟠 Importantes
- Manual files fora do fluxo do
db.cjs— osmanual/*.supabase_admin.sqlNÃO são aplicados pelodb.cjs migrate. São aplicados à mão (psql comosupabase_admin). Fácil esquecer um → função/trigger ausente. Os runbooks listam a ordem. postgresnão é superuser no stack local — por isso vários objetos sãosupabase_admin. No hosted opostgresé mais privilegiado, mas o schemaauthé desupabase_admin: o trigger da blacklist emauth.userse osOWNER TO supabase_adminpodem precisar de SQL Editor ou troca praOWNER TO postgres.config.tomlé gitignored —enable_confirmations=trueestá só no arquivo local (não versionado). No hosted a confirmação vai pelo dashboard (Auth → Confirm email).- Migração de dados (cutover) —
f6_1COPIA; conferir paridade de contagens por tenant/tabela antes de confiar (e antes do DROP). - Edge functions novas precisam deploy —
recover-accessesend-welcome-email(freemium)- as edges de roteamento por schema (schema-per-tenant). Esquecer = esqueci-email/welcome/ webhooks quebram.
- Slug é IMUTÁVEL — = nome do schema físico. Uma vez escolhido, não muda (trava em 3 camadas). UX do signup deixa claro, mas é definitivo.
🟡 Menores / a observar
- Enforcement de limite é por-linha (BEFORE INSERT) — um bulk insert de pacientes numa única statement pode passar marginalmente do limite (cada linha não vê as anteriores da mesma statement). Na prática o cadastro é 1 a 1; ok.
- notify_all_devs dispara a cada subscription (inclui a free do auto_provision) — em escala, muitos avisos no sino do dev. Intencional; reavaliar se incomodar.
- send-welcome-email usa SMTP de sistema (separado do canal do tenant) — precisa secrets no hosted; é best-effort (falha não bloqueia login).
- auto_provision idempotente retorna o 1º tenant ativo se o user já tem algum — usuário multi-tenant que se cadastra de novo não ganha tenant novo (esperado).
- Local vs main inconsistente — o banco local está no modelo novo; o código da
mainé RLS. Se fizergit checkout main, o app antigo ainda funciona porquepublictem as tabelas (até o DROP). Não rodarmainesperando o modelo novo (e vice-versa).
3. Passo a passo — como testar TUDO (local)
Pré: Docker do Supabase rodando (portas 643xx). Frontend via
npm run dev.
Passo 0 — Ativar a confirmação de e-mail
A confirmação só vale após reiniciar o stack (o volume do banco persiste — nada se perde):
supabase stop && supabase start # se falhar com containers unhealthy, rode start de novo (transiente)
Conferir no Studio/Mailpit que está de pé. (Se preferir NÃO testar confirmação agora, pule — o front trata os dois casos; mas o fluxo "confirme e-mail" só aparece com isto ligado.)
🔴 GOTCHA OBRIGATÓRIO pós-restart — a GUC
pgrst.db_schemas(exposição dos schemas tenant no PostgREST) NÃO sobrevive aosupabase stop/start(ostartreseta a roleauthenticator). Sem isso o app dá 404 em todas as tabelas tenant. Rodar SEMPRE após start:docker exec -i supabase_db_agenciapsi-primesakai psql -U supabase_admin -h 127.0.0.1 -d postgres \ -c "select public.refresh_pgrst_schemas();"(Confirma exposição:
curl -s -o /dev/null -w "%{http_code}" "http://127.0.0.1:64321/rest/v1/patients?limit=1" -H "Accept-Profile: tenant_<slug>"deve dar 200.)
Passo 1 — Schema-per-tenant: tenants EXISTENTES ainda funcionam
npm run dev, logar num tenant existente (ex.: clínica Bem-Estar / um terapeuta).- Abrir Agenda, Pacientes, Financeiro, Conversas → tudo carrega (lendo de
tenant_<slug>). - Criar/editar um registro (ex.: um bloqueio na agenda, editar um paciente) → salva sem erro.
- Sino de notificações abre (dual-source tenant + sistema).
Se algo não carregar, é sinal de roteamento de schema — anotar a tela/erro.
Passo 2 — Freemium: signup self-service NOVO (o fluxo principal)
- Deslogar. Ir em
/lp→ conferir o cartão "Grátis" na vitrine. - Criar conta grátis → escolher tipo (terapeuta/clínica) + seu nome + nome do negócio + slug (ver a checagem de disponibilidade ao vivo) + e-mail + senha.
- Submeter → cai na tela "Confirme seu e-mail" (NÃO loga ainda).
- Abrir o Mailpit (caixa de e-mail local) → achar o e-mail de confirmação → clicar no link.
- Voltar/entrar em
/auth/login→ logar → cai em/onboarding→ "Preparando seu ambiente…" → provisiona → entra no painel do tenant novo. - Conferir no Mailpit o e-mail de boas-vindas (welcome — best-effort).
- Conferir que o schema
tenant_<slug-escolhido>foi criado (Studio) e que você é master.
Passo 3 — Limite do plano gratuito
- No tenant gratuito recém-criado (ou num clinic_free existente), cadastrar pacientes.
- Ao passar do limite (clínica=30, terapeuta=20) → aparece o toast "Limite do plano gratuito" com botão "Fazer upgrade" (não o erro cru).
- Conferir o botão "Upgrade PRO" dourado no topbar (visível porque o plano é free).
Passo 4 — SaaS / dev (logar como saas_admin)
/saas/usuarios→ o cliente novo aparece com selo "Novo" (verde, 24h), com slug/e-mail/plano.- Sino do dev → recebeu "Nova assinatura" (do provisionamento).
/saas/app-config:- Adicionar um e-mail na blacklist (ex.:
bloqueado@x.com). Depois, deslogar e tentar criar conta com ele → bloqueado de verdade. - Testar
@dominio.com(domínio inteiro). - Trocar root_redirect (landing↔login) e abrir
/deslogado → confere o destino.
- Adicionar um e-mail na blacklist (ex.:
Passo 5 — Esqueci meu e-mail
/auth/login→ "Esqueci meu e-mail" → digitar o slug do tenant criado no Passo 2.- Recebe a confirmação com a dica mascarada (jo****@gm****.com) e um magic link no Mailpit.
- Clicar no magic link → entra. (O e-mail real nunca aparece na tela.)
- ⚠️ Edge functions locais: precisam estar servidas (
supabase functions serveou o runtime do stack). Se o esqueci-email/welcome não responder, é a edge não estar de pé localmente.
Passo 6 — Pegadinha #4 (sino ao trocar de usuário)
- Logado como user A, com notificações no sino → logout.
- Logar como user B → o sino não mostra notificações do A (foi resetado no logout).
Passo 7 (opcional, destrutivo, só quando confiante) — preparar o DROP
NÃO aplicar agora. Quando tudo acima estiver validado por dias: seguir a Fase G do
docs/DEPLOY_SCHEMA_PER_TENANT.md (backup fresco → f6_3_drop_public_tenant_tables).
4. Atalhos / referências
- Runbooks:
docs/DEPLOY_SCHEMA_PER_TENANT.md,docs/DEPLOY_FREEMIUM_F4.md. - Rollback do DROP:
database-novo/manual/f6_3_ROLLBACK.md. - Migrations:
database-novo/migrations/(aplicar vianode database-novo/db.cjs migrate). - Manual privilegiados:
database-novo/manual/*.supabase_admin.sql(aplicar comosupabase_admin). - Wiki:
Obsidian/Brain/wiki/Migracao Schema-per-Tenant.mdeObsidian/Brain/wiki/Freemium PLG.md. - Portas locais: API 64321 · DB 64322 · Studio 64323 (stack shiftada +10000).