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>
This commit is contained in:
@@ -0,0 +1,176 @@
|
||||
# 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):
|
||||
|
||||
```bash
|
||||
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):
|
||||
```sql
|
||||
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
|
||||
```bash
|
||||
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):
|
||||
```bash
|
||||
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):
|
||||
```bash
|
||||
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) ✅
|
||||
Reference in New Issue
Block a user