Platform Admin
Status: 🟢 Estável (operações cross-tenant centralizadas) Code: backend/app/modules/platform_admin UI: frontend/src/app/(admin)/[slug]/admin/platform (super-admin only) Última revisão deste doc: 2026-05-13 por Felipe + Claude Dependências fortes:
mandir_platformDB (cross-DB writes), billing (approval workflow), all tenants
1. Identidade
O que faz (uma frase)
Operações cross-tenant centralizadas que vivem no mandir_platform DB: provisioning de novo tenant (cria DB + role + cifragem), approval queue de credenciais Asaas (cross-DB writes), dunning lifecycle (D+1 → D+15), drop_scheduler (LGPD offboarding D+45), audit chain SHA256 e custom domains com Traefik provisioning.
Por que existe (negócio)
Algumas operações precisam ser globais (não escopadas a 1 tenant):
- Provisioning de novo tenant cria DB + role + password — superuser only.
- Billing approval — só admin Mandir aprova credenciais Asaas (anti-fraude).
- Dunning — workflow padronizado pra todos os tenants em atraso (não pode ser per-tenant config divergente).
- Audit chain — tamper-proof central (SHA256 hash chain).
- Custom domains — Traefik YAML escrito uma vez, serve todos os tenants.
Por que existe (técnico)
- 0 tabelas próprias no Suite DB — usa
mandir_platform.*. - Cross-DB writes documentados em ADR (cross-db-writes.md): superuser_conn separado, transacional por tenant DB, audit log entry.
- Reentrant-safe — endpoint
retry-applypermite reaplicar approval após erro.
Status atual
Em prod, todas as features deployadas. Memória [[postgres-role-separation]] documenta padrão de role separation (alembic conecta como dono via *_MIGRATION_DATABASE_URL; runtime usa role limitado).
2. Cases de uso reais
Case 1: Provisioning novo tenant
- Felipe via UI
/admin/platform/tenants→ "Novo Tenant" → form (slug, plan, region). - Backend
provision_tenant()→ cria DB físico (mandir_<slug>) + role (<slug>_app) + password (gerada + cifrada SOPS) + insere emmandir_platform.tenant. - Tenant pronto pra alembic migrations.
Case 2: Approval credencial Asaas
Tenant submete credencial → fica em mandir_platform.provider_approval_request(status=pending) → Felipe aprova → backend faz cross-DB write em tenant DB (tenant_provider_credentials.status=active) → audit log central.
Case 3: Dunning automático
Beat platform_admin.dunning.run → query tenants com fatura overdue → D+1 envia email soft_reminder → D+3 marca read_only → D+7 suspend → D+15 offboard → D+45 drop_scheduler executa DROP DATABASE (irreversível).
3. Oportunidades de negócio
- Self-service tenant provisioning: atualmente via super-admin; abrir flow self-service é direto vendas (CRM premium).
- Multi-region routing: custom_domains + Traefik permite balanceamento global (US/BR/EU).
- Audit trail vendido como compliance pack: SHA256 chain auditável é diferencial pra setores regulados.
4. Arquitetura interna
Arquivos
| Arquivo | Propósito |
|---|---|
provisioning.py | provision_tenant (DB + role + password) |
billing_approvals.py | Workflow cross-DB |
dunning.py | Beat task lifecycle |
custom_domains.py | DNS verify + Traefik YAML |
drop_scheduler.py | DROP DATABASE D+45 |
subscriptions.py | Trial + Asaas webhook (Fase 3) |
audit_chain.py | SHA256 hash chain |
routes.py | 15 endpoints |
Tasks Celery
| Task | Schedule |
|---|---|
platform_admin.run_dunning | Beat diário |
platform_admin.run_drop_scheduler | Beat diário |
5. Tabelas (em mandir_platform, não no Suite DB)
mandir_platform.tenant— slug, db_host, db_name, db_user, db_password_secret_ref, status, plan, region.mandir_platform.provider_approval_request— provider, instance_key, status, applied_at, apply_error.mandir_platform.audit_log— SHA256 chain (actor_type, action, prev_hash, current_hash).mandir_platform.subscription— trial/paid (Fase 3).mandir_platform.evolution_server— fleet config global.
Relacionamentos cross-módulo
| Direção | Outro módulo | Como |
|---|---|---|
| ↘ Escreve cross-DB | tenant DB.tenant_provider_credentials | Approval workflow |
| ↗ Lê | billing | Submit pra approval |
| ↗ Lê tenants | Todos | Iteração em dunning, drop_scheduler |
6. API / Endpoints (15)
Prefixo /api/admin/platform.
Tenant lifecycle (4)
| Método | Rota | O que faz |
|---|---|---|
| POST | /tenants | Provision (DB + role + password) |
| POST | /tenants/{id}/suspend | Read-only mode |
| POST | /tenants/{id}/reactivate | Volta active |
| POST | /tenants/{id}/offboard | LGPD offboarding (DROP em D+45) |
Billing approvals (5)
| Método | Rota | O que faz |
|---|---|---|
| GET | /billing/approval-requests | Lista (filter status) |
| GET | /billing/approval-requests/{id} | Detalhe |
| POST | /billing/approval-requests/{id}/approve | Cross-DB write → tenant credential active |
| POST | /billing/approval-requests/{id}/reject | Marca rejected |
| POST | /billing/approval-requests/{id}/retry-apply | Retry cross-DB |
Audit + Health + Subscriptions + Domains (6)
- GET
/audit/verify?start_id&end_id— verifica hash chain. - GET
/router/metrics— load balancer snapshot. - GET
/health— agregado. - GET
/whatsapp/evolution-status— global WA por tenant. - POST
/subscriptions/checkout— trial + Asaas (idempotency-key). - GET
/subscriptions/{tenant_id}— list. - POST
/dunning/run— trigger ad-hoc. - POST
/tenants/{id}/domains/request— gera token TXT. - POST
/tenants/{id}/domains/verify— DNS lookup + Traefik YAML. - DELETE
/tenants/{id}/domains— remove.
7. Configuração
Env vars
| Var | Propósito |
|---|---|
MANDIR_PLATFORM_DATABASE_URL | Central DB |
MANDIR_SUPERUSER_DATABASE_URL | Provisioning superuser |
MANDIR_TENANT_PWD_<SLUG> | Cifrado SOPS |
MANDIR_TRAEFIK_DYNAMIC_DIR | /data/coolify/proxy/dynamic/mandir |
MANDIR_VASUDEVA_HOST | Hostname remoto |
8. Operações + Troubleshooting
Sintoma: Approval workflow apply_error populado
Causa: Cross-DB write falhou (tenant DB indisponível, FK error).
Diagnóstico: Verificar apply_error no record; logs platform_admin.billing_approvals.error.
Fix: POST /retry-apply após resolver causa raiz.
Sintoma: Dunning não enviou email
Causa: Brevo offline ou user já recebeu (anti-duplicate). Fix: Verificar logs; manual re-trigger.
9. Limitações e débitos técnicos
| # | Item |
|---|---|
| 1 | Eventual consistency mandir_platform ↔ tenant DB |
| 2 | Custom domain DNS apenas TXT — sem CNAME automático |
| 3 | Offboarding D+45 irreversível |
| 4 | Audit chain rebuild não automático |
| 5 | Alembic não é multi-tenant nativo |
10. Histórico
ADR 0008 cravou DB-per-tenant. ADR 0009 crypto + Asaas. Memórias [[postgres-role-separation]] + [[alembic-multi-tenant-gotchas]] documentam padrões.