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).

#PassoO que fazLog
1OrganizaçãoLinha em appstock_core.organizations com slug, nome e is_defaultcore.org
2Banco do portalCREATE DATABASE app_<slug> no db-app + alembic upgrade head (70 migrations, via subprocess com ALEMBIC_URL)app.migrations
3Banco do espelhoCREATE DATABASE sync_<slug> no db-sync + aplica db/sync_template.sql (51 tabelas + 13 views)sync.template
4Registro2 DSNs em org_databases (app/sync) + 11 módulos em org_modules, e invalida o cache de engines
5SeedDentro do app_<slug>: linha da org (mesmo UUID do core), 163 resources, perfil Administrador com todos os grants, usuário admin is_superuserapp.seed
6Identidade e tokenE-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

Lacunas

Rastreadas na fase-1-onboarding.