- docs/F0_categorizacao.md: varredura completa (137 tabelas -> 84 tenant + 53 global, 66 funcoes, FKs, policies, edge functions) + decisoes Q1-Q4 - F1 (migrations 01-05): tenants.slug, helpers de schema, _tenant_template (84 tabelas sem tenant_id, singletons, views __SCHEMA__/__TENANT_ID__), clone_tenant_template/drop_tenant_schema, channel_routing, tenant_schemas - F2 (migration 06): provision_account_tenant/create_clinic_tenant/ ensure_personal_tenant_for_user clonam schema na mesma transacao - db.cjs: psqlFile agora usa ON_ERROR_STOP=1 (falha de migration nao passa mais como sucesso silencioso) - blueprint original em novo-rumo.txt; wiki Obsidian atualizada Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
5.0 KiB
Migração Schema-per-Tenant
Status: F2 concluída e smoke-testada (2026-06-12). Próximo: F3 (frontend useTenantDb).
F2 — entregue (migration 20260612000006)
Os 3 únicos pontos de criação de tenant (provision_account_tenant, create_clinic_tenant, ensure_personal_tenant_for_user — este último também acionado pelo trigger de signup handle_new_user_create_personal_tenant) agora chamam clone_tenant_template() na mesma transação: clone falhou → tenant não nasce. Smoke: ensure_personal criou tenant pessoal tenant_terapeuta_pessoal com 84 tabelas + registro, 2ª chamada idempotente, drop limpou tudo. Não há fluxo de exclusão de tenant no sistema (drop_tenant_schema fica pra uso admin/manual).
F1 — entregue (migrations 20260612000001–05 em database-novo/migrations/)
tenants.slugcriado + backfill dos 9 + trigger auto-gera/imutável- Helpers:
tenant_schema_name/for,tenant_id_for_schema,tenant_schema_checked(p_tenant_id)(validais_tenant_member— substitui current_tenant_schema do blueprint) _tenant_template: 84 tabelas sem tenant_id, 6 singletons (singleton boolean PK/UQnas configs 1-linha: company_profiles, email_layout_config, conversation_autoreply_settings/bots/sla_rules, session_reminder_settings), 4 sequences locais, 94 FKs (62 intra + 32 pra public/auth), 6 views com placeholders__SCHEMA__/__TENANT_ID__em_views, seeds de sistema (whitelist 8 lookups)clone_tenant_template(uuid)→ tabelas+seqs+seeds+FKs+views+RLS (policies com tenant_id EMBUTIDO:is_tenant_member('<uuid>')+ saas_admin_full)+realtime+grants+trigger routing+registro emtenant_schemasdrop_tenant_schema(uuid)protegido;public.channel_routing(webhook inbound acha tenant do canal) sincronizada por trigger- Smoke: clone tenant_smoke_f1 → 84 tabelas/168 policies/roundtrip/routing sync/singleton rejeitando 2ª linha → drop limpo
Gotchas aprendidos na F1
postgresnão é superuser no Supabase →session_replication_roleproibido; seeds usam retry-loop de FK (rounds). Vale pro F6 (migração de dados): rodar comosupabase_adminou retry-loop.- db.cjs aplicava migration sem
ON_ERROR_STOP→ rollback silencioso reportado como sucesso. Corrigido (psqlFile agora usa-v ON_ERROR_STOP=1). - Linhas operacionais órfãs com tenant_id NULL (intakes/convites/notifs) NÃO são seeds — whitelist explícita.
- Clones F1/F2 ainda SEM triggers de negócio (F6) e fora do PostgREST (F5) —
_meta.triggers_pending=true.
Migração de multi-tenant RLS-only (tenant_id em cada tabela) para schema físico por tenant (tenant_<slug>), seguindo blueprint do projeto irmão (novo-rumo.txt na raiz), adaptado.
Artefatos
docs/F0_categorizacao.md— varredura completa: classificação das 137 tabelas, 66 funções, 6 views, FKs, edge functions, divergências.novo-rumo.txt(raiz) — blueprint original com lições do projeto irmão.
Números-chave
- 137 tabelas public → 79 tenant-scoped + 5 em decisão (infra mensageria) + 53 globais
- 66 funções afetadas (blueprint avisava: listas pré-feitas subestimam — era "29" lá, 66 aqui)
- 1 única FK global→tenant problemática:
whatsapp_credits_transactions.conversation_message_id - 0 policies de tabelas globais usando funções a refatorar
- 9 tenants (3 clínicas + 6 therapists), volumetria minúscula (<400 linhas/tabela)
Divergências vs blueprint (decisivas)
- Sem
tenants.slug— precisa criar coluna ou usar uuid no nome do schema. - Multi-membership:
profiles.tenant_id100% NULL; verdade vive emtenant_members(4 users multi-tenant).current_tenant_schema()do blueprint não funciona → frontend escolhe schema (tenantStore já temactiveTenantId), segurança via policy com tenant_id embutido por schema + RPCs recebemp_tenant_idvalidado comis_tenant_member(). - 6/9 tenants são terapeutas individuais — schema por signup; custo operacional do config.toml do PostgREST cresce com tenants.
email_layout_config.tenant_ideemail_templates_tenant.tenant_idapontam pra auth.users (legado) — mapear na migração de dados.- View
current_tenant_idé código morto (claim JWT nunca populado).
Decisões (2026-06-12)
- Q1: criar
tenants.slug→ schemastenant_<slug> - Q2: todo tenant ganha schema (clínicas e therapists)
- Q3: mensageria tenant-scoped (isolamento máximo, contra rec. global) → crons varrem tenants em loop; webhooks inbound precisam de índice global
channel_routing(channel_external_id → tenant_id) pra rotear antes de gravar - Q4: asaas tenant (staging
asaas_webhook_eventsglobal roteia)
Total final: 84 tabelas tenant-scoped, 53 globais.
Fases (tasks #1–#7 na sessão)
F0 categorização ✅ · F1 template+helpers · F2 provisionamento · F3 frontend useTenantDb · F4 edge functions · F5 PostgREST config · F6 rewrite funções + migração dados + drops (lotes, backup antes de cada um)
Relacionados: Decisões de Billing da Agenda, Supabase Local, index