Files
agenciapsilmno/docs/DEPLOY_FREEMIUM_F4.md
T
Leonardo 2f72886d4b freemium F4: runbook de deploy hosted
docs/DEPLOY_FREEMIUM_F4.md — passo a passo ordenado:
- PRE-REQUISITO #0: schema-per-tenant precisa estar no hosted antes (freemium
  depende de tenant_schemas/clone/is_saas_admin/pgrst schemas)
- migrations 05/06/07 + 5 manual supabase_admin (ordem + nota de permissoes hosted)
- Auth dashboard (confirm email + redirect URLs + SMTP GoTrue)
- deploy das edges recover-access/send-welcome-email + secrets SMTP
- rebuild front + smoke test (8 passos) + rollback/kill-switch

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-13 20:41:28 -03:00

8.5 KiB

Deploy F4 — Freemium / PLG (hosted)

Runbook de produção do épico freemium/PLG (branch feat/freemium-plg). Gerado em 2026-06-13. Faça um passo de cada vez e valide antes de seguir.


PRÉ-REQUISITO #0 (bloqueante) — schema-per-tenant no hosted

O freemium foi construído em cima da migração schema-per-tenant. As RPCs (auto_provision_free_tenant, slug_disponivel, enforcement de limite, etc.) dependem de infra que só existe se a schema-per-tenant já estiver no hosted:

  • tenant_schemas, _tenant_template, clone_tenant_template, seed_*
  • helpers tenant_id_for_schema, tenant_schema_name, is_saas_admin, is_tenant_member
  • exposição dinâmica de schemas no PostgREST (pgrst.db_schemas)
  • v_tenant_active_subscription, notifications_sistema, tenant_members.slug

Se o hosted ainda está no modelo RLS (branch main), NÃO aplique o freemium — ele vai quebrar. Ordem obrigatória:

  1. Deployar e validar a schema-per-tenant no hosted (migrations 20260612* + 20260613000001..000004 + os manual/f5*..f6_2h* + f6_4), sem o F6.3 DROP num primeiro momento (dados espelhados em public — ver database-novo/manual/f6_3_ROLLBACK.md).
  2. Só então seguir este runbook do freemium.

Enquanto a schema-per-tenant não estiver no hosted + testada no browser (task #7 / DROP F6.3 pendente), este deploy fica em espera.


Inventário do que vai pro hosted (freemium)

Migrations (rodam como postgres via supabase db push ou SQL Editor)

Ordem Arquivo O quê
1 database-novo/migrations/20260613000005_freemium_f1_therapist_free_patient_limit.sql max_patients=20 no therapist_free
2 database-novo/migrations/20260613000006_fix_audit_global_tables.sql fix regressão audit em tenant_members (aplicar SEMPRE)
3 database-novo/migrations/20260613000007_freemium_f2_vitrine_free.sql cartão "Grátis" na vitrine (plan_public + bullets)

Manual supabase_admin (rodam com role elevada — ver nota de permissões)

Aplicar nesta ordem (idempotentes; BEGIN/COMMIT internos):

  1. database-novo/manual/freemium_f1_plan_limits.supabase_admin.sql
  2. database-novo/manual/freemium_f2_provisioning.supabase_admin.sql
  3. database-novo/manual/freemium_f3a_blacklist.supabase_admin.sql
  4. database-novo/manual/freemium_f3b_saas_owners_notify.supabase_admin.sql
  5. database-novo/manual/freemium_f3c_app_config.supabase_admin.sql

Edge functions

  • supabase/functions/recover-access (esqueci-email por slug → magic link)
  • supabase/functions/send-welcome-email (boas-vindas ao dono — best-effort)

Config

  • Auth → Confirm email = ON + Site/Redirect URLs
  • Secrets de SMTP do send-welcome-email (+ APP_URL)

Passo a passo

1) Banco — migrations

Com a CLI apontando pro projeto hosted (supabase link já feito):

supabase db push           # aplica as migrations pendentes (inclui as 3 do freemium)

Ou, se preferir manual, cole cada arquivo migrations/2026061300000[567]_*.sql no SQL Editor do dashboard (roda como postgres), na ordem da tabela acima.

⚠️ A 20260613000006_fix_audit_global_tables.sql é obrigatória — sem ela, qualquer novo tenant_members (provisionamento, convite) falha no hosted também.

2) Banco — manual supabase_admin

Nota de permissões (hosted): no Supabase hosted, o postgres da connection string tem mais privilégio que o local, mas o schema auth é de supabase_admin. A blacklist (freemium_f3a) cria trigger em auth.users e vários objetos são ALTER FUNCTION ... OWNER TO supabase_admin. Caminhos:

  • SQL Editor do dashboard roda como postgres (costuma conseguir criar trigger em auth.users no hosted) — tente por aí primeiro.
  • Se algum OWNER TO supabase_admin ou o trigger em auth.users falhar por permissão, rode via a connection string de serviço (Settings → Database → Connection string), ou abra ticket de acesso. Os OWNER TO supabase_admin podem ser trocados por OWNER TO postgres no hosted se necessário (sem perda funcional).

Aplicar os 5 arquivos manual/freemium_f*.supabase_admin.sql na ordem, colando no SQL Editor (cada um é uma transação). Verifique a saída sem erro a cada um.

Smoke SQL pós-aplicação (no SQL Editor):

select public.slug_disponivel('teste_slug_livre');         -- {ok:true}
select public.get_root_redirect();                          -- 'landing'
-- como saas_admin (logado no dashboard você é postgres; teste a RPC existe):
select proname from pg_proc where proname in
 ('auto_provision_free_tenant','processar_pos_signup','slug_disponivel',
  'saas_list_account_owners','notify_all_devs','is_email_blacklisted','get_root_redirect');
-- trigger de limite presente nos schemas:
select count(*) from pg_trigger where tgname='enforce_patient_plan_limit';

3) Auth — dashboard

Authentication → Providers / Email:

  • Confirm email = ON (equivale ao enable_confirmations=true do config.toml local).
  • Site URL = origem do app em produção (ex.: https://app.seudominio.com).
  • Redirect URLs — adicionar (magic link + confirmação caem aqui):
    • https://app.seudominio.com/onboarding
    • https://app.seudominio.com/auth/login
    • https://app.seudominio.com/** (se preferir curinga)
  • SMTP do GoTrue (o que manda confirmação + magic link): garantir que está configurado com um provedor real (não Mailpit) em Authentication → Emails → SMTP.

4) Edge functions — deploy + secrets

supabase functions deploy recover-access
supabase functions deploy send-welcome-email

recover-access usa só envs já injetadas (SUPABASE_URL / SERVICE_ROLE_KEY / ANON_KEY).

send-welcome-email usa um SMTP de sistema (defaults = Mailpit local). Em produção, configure os secrets pra um provedor real (pode ser o mesmo do GoTrue):

supabase secrets set \
  SMTP_HOST="smtp.seuprovedor.com" \
  SMTP_PORT="587" \
  SMTP_USER="..." \
  SMTP_PASS="..." \
  SMTP_FROM="no-reply@seudominio.com" \
  SMTP_FROM_NAME="Agência PSI" \
  APP_URL="https://app.seudominio.com"

É best-effort: se faltar SMTP, o welcome só não envia — o onboarding/login segue.

5) Frontend — rebuild + deploy

Build apontando pras envs do hosted (Supabase URL + anon key de produção):

npm run build

Publique o dist/ no hosting de sempre. (A confirmação de e-mail é resolvida server-side; o front já trata o caso "sem sessão" → tela "confirme seu e-mail".)

6) Smoke test no hosted (fluxo completo)

  1. /lp → o cartão Grátis aparece na vitrine.
  2. Criar conta grátis → escolher slug (disponibilidade ao vivo) → enviar.
  3. Cai na tela "confirme seu e-mail" (não loga ainda).
  4. Abre o e-mail (provedor real) → clica no link → entra → /onboarding provisiona → painel do tenant. Welcome email chega (se SMTP configurado).
  5. Cadastrar pacientes até passar do limite → toast PLAN_LIMIT_REACHED + Upgrade PRO.
  6. Logar como dev (saas_admin)/saas/usuarios lista o novo cliente com selo "Novo"; o sino recebeu "Nova assinatura".
  7. /auth/login"Esqueci meu e-mail" com o slug → recebe magic link, dica mascarada.
  8. /saas/app-config → adicionar um e-mail na blacklist → tentar cadastrar com ele → bloqueado. Trocar root_redirect e conferir o destino de /.

Rollback / kill-switch (se algo der errado)

  • Confirmação de e-mail: desligar "Confirm email" no dashboard volta ao signup sem confirmação (mas o signup novo já espera confirmação — prefira corrigir a frente).
  • Enforcement de limite: DROP TRIGGER enforce_patient_plan_limit ON <schema>.patients (ou ajustar plan_features.limits pra um número alto — vale em runtime, sem deploy).
  • Blacklist: DROP TRIGGER trg_enforce_email_blacklist ON auth.users;
  • notify devs: DROP TRIGGER trg_subscriptions_notify_devs ON public.subscriptions;
  • root_redirect: UPDATE public.saas_app_config SET root_redirect='login'; (ou 'landing').
  • Tudo é aditivo — nenhuma tabela/coluna existente foi removida pelo freemium.

Checklist rápido

  • schema-per-tenant já está no hosted e validada (PRÉ-REQUISITO #0)
  • migrations 05/06/07 aplicadas (supabase db push)
  • 5 manual/freemium_f*.supabase_admin.sql aplicados na ordem
  • Confirm email = ON + Site/Redirect URLs + SMTP do GoTrue
  • recover-access e send-welcome-email deployadas
  • secrets SMTP do send-welcome-email + APP_URL
  • frontend rebuildado e publicado
  • smoke test (8 passos)