Onboarding da organização
Do zero até a empresa operando: uma requisição cria dois bancos, roda 70 migrations, aplica o template do espelho, semeia 163 permissões e devolve um administrador que já consegue entrar.
É a única jornada do AppStock que atravessa os três bancos (ent-bancos) na mesma transação lógica. Decisões que a sustentam: adr-001-banco-por-org e adr-002-email-unico-global.
flowchart TD A["/signup ou CLI<br/>python -m src.provision"] --> B{slug livre?} B -- não --> B1["409 slug já utilizado"] B -- sim --> C["bootstrap do appstock_core<br/>CREATE DATABASE + create_all"] C --> D{e-mail livre no<br/>user_directory?} D -- não --> D1["409 e-mail já cadastrado"] D -- sim --> E["1. organizations no core"] E --> F["2. CREATE DATABASE app_slug<br/>alembic upgrade head (70)"] F --> G["3. CREATE DATABASE sync_slug<br/>aplica sync_template.sql"] G --> H["4. org_databases (2 DSNs)<br/>org_modules (11 módulos)"] H --> I["5. seed no app_slug<br/>163 resources + perfil Admin + usuário"] I --> J["6. user_directory + sync_token"] J --> K["/login → JWT com org_id"] K --> L["primeira carga<br/>POST /sync/v1/*/batch"] style B1 fill:#fee,stroke:#c00 style D1 fill:#fee,stroke:#c00 style L stroke-dasharray: 5 5
Os 6 passos do provisionamento
Executados por provision_org() em repo/apps/api/src/provision/service_provision.py:78.
Cada passo grava uma linha em provisioning_log (OK ou ERROR).
| # | Passo | O que faz | Log |
|---|---|---|---|
| 1 | Organização | Linha em appstock_core.organizations com slug, nome e is_default | core.org |
| 2 | Banco do portal | CREATE DATABASE app_<slug> no db-app + alembic upgrade head (70 migrations, via subprocess com ALEMBIC_URL) | app.migrations |
| 3 | Banco do espelho | CREATE DATABASE sync_<slug> no db-sync + aplica db/sync_template.sql (51 tabelas + 13 views) | sync.template |
| 4 | Registro | 2 DSNs em org_databases (app/sync) + 11 módulos em org_modules, e invalida o cache de engines | — |
| 5 | Seed | Dentro do app_<slug>: linha da org (mesmo UUID do core), 163 resources, perfil Administrador com todos os grants, usuário admin is_superuser | app.seed |
| 6 | Identidade e token | E-mail no ent-user-directory + ent-sync-token (guarda só o SHA-256) | sync.token |
Os 11 módulos de MODULES_V1: stock, commercial, clients, offers, opportunities,
orders, dashboards, dictionary_studio, ai_assistant, reports, media.
Estado real
Funciona. As duas organizações atuais (appstock e santonio) nasceram assim. A rotina é
idempotente por etapa — reexecutar completa o que faltou, porque cada passo checa antes de
agir (pg_database, pg_tables, select no core).
Três buracos, em ordem de gravidade:
1. Provisionamento síncrono dentro do HTTP [M]
POST /signup cria dois bancos, roda 70 migrations por subprocess e semeia tudo antes de
responder. Não há fila, worker nem tela de progresso.
2. Retry travado — a armadilha [P]
Se o passo 2 ou 3 falhar, a organização já existe no core. O endpoint responde 500, e a
segunda tentativa com o mesmo slug bate na guarda de route_signup.py:53 e devolve 409 “slug
já utilizado”. O usuário fica preso: não consegue nem entrar nem recriar.
O CLI não sofre disso — provision_org é idempotente e retoma. Só o caminho público trava.
Conserto: o signup deveria distinguir “slug de organização provisionada” de “slug de
provisionamento incompleto” (o provisioning_log já tem a informação).
3. O token nasce e se perde [M]
O ent-sync-token é gerado no passo 6 e devolvido em claro uma única vez. O CLI imprime.
O POST /signup não devolve — o comentário em route_signup.py:68 diz que “o admin o emite
autenticado”, mas esse endpoint não existe.
Consequência direta: quem se cadastra pelo signup público recebe uma organização que não tem como ser integrada. E token vazado não tem como ser revogado sem SQL manual.
Continua em
- flx-resolucao-tenant — como cada request seguinte descobre em qual banco rodar.
- flx-primeira-carga — como o ERP do cliente começa a alimentar o espelho.
Lacunas
Rastreadas na fase-1-onboarding.