# DGestor — como integrar um sistema novo à Central **Documento técnico de integração.** Descreve o que um sistema novo precisa implementar para ser criado, atualizado, licenciado e acessado pela Central do DGestor, do mesmo jeito que o PDV já é hoje. Tudo aqui foi extraído do código da Central em produção (`dgestor-central`) e do produto já integrado (`base-dgestor`). Onde houver convenção em vez de regra forçada pelo código, está dito explicitamente. --- ## 1. Como a plataforma funciona A Central **não é um servidor de aplicação**. Ela é um painel que: 1. cria subdomínio, banco e usuário de banco na hospedagem (cPanel/UAPI); 2. copia os arquivos de uma **pasta-modelo** para o diretório do cliente novo; 3. gera os arquivos de configuração do cliente a partir de moldes `.template`; 4. mantém o registro de empresas, usuários, licenças e produtos; 5. empurra atualizações copiando a pasta-modelo por cima dos clientes; 6. entra no sistema do cliente por SSO assinado; 7. libera e bloqueia a licença por HTTP. Cada cliente roda **uma instalação própria, com banco próprio**. Não há banco compartilhado e não há multi-tenant por coluna. Isso é central para o projeto do sistema novo: ele é instalado inteiro, uma vez por empresa. ``` ┌──────────────────────────┐ │ Central (dgestor.com.br/dgestor-central) │ painel + provisionador + licenças └───────┬──────────┬───────┘ cPanel UAPI │ │ HTTPS (SSO e licença) cria sub/banco │ │ ┌───────▼───┐ ┌───▼────────┐ │ cliente A │ │ cliente B │ cada um: │ subdomínio│ │ subdomínio │ - pasta própria │ banco A │ │ banco B │ - banco próprio └───────────┘ └────────────┘ - config própria ``` --- ## 2. O que o sistema novo precisa ter ### 2.1 O manifesto da pasta-modelo A Central recusa criar uma empresa se a pasta-modelo não tiver, na raiz, o arquivo **`DGESTOR_TEMPLATE.json`**: ```json { "produto": "DGestor Construção", "versao": "1.0.0", "gerado_em": "2026-10-01", "migracao_minima": "001_base", "arquivos_obrigatorios": [ "index.php", "config/app.php", "config/database.php", "config/local_database.php.template", "dgestor-sso-config.php.template", "sso-entrar.php" ] } ``` Regras verificadas pelo código (`dgestor_provisionar_validar_template`): | Regra | Comportamento se violada | |---|---| | O arquivo tem de existir | recusa: "não possui DGESTOR_TEMPLATE.json" | | `produto` preenchido e `versao` >= 1.0 | recusa: "manifesto inválido ou incompatível" | | `arquivos_obrigatorios` não vazio | recusa: "não informa os arquivos obrigatórios" | | Todos os arquivos listados existem | recusa nomeando o arquivo ausente | | Caminho sem `..` | recusa | | **Não** existir `config/local_database.php` na pasta-modelo | recusa: "contém credenciais de banco" | A última é importante: a pasta-modelo **nunca** guarda credencial. Só o molde `.template`. ### 2.2 Os moldes de configuração Qualquer arquivo terminado em **`.template`**, em qualquer lugar da pasta, é processado na criação: os marcadores são substituídos, o arquivo é gravado sem a extensão `.template`, e o molde é apagado do destino. Marcadores disponíveis: | Marcador | Conteúdo | |---|---| | `__DGESTOR_DB_HOST__` | sempre `localhost` | | `__DGESTOR_DB_NAME__` | nome do banco já com o prefixo da conta | | `__DGESTOR_DB_USER__` | usuário do banco, já com o prefixo | | `__DGESTOR_DB_PASS__` | senha gerada na criação | | `__DGESTOR_APP_URL__` | `https://.dgestor.com.br` | | `__DGESTOR_LICENSE_PASSWORD__` | senha mestra de licença desta empresa | | `__DGESTOR_EMPRESA_SLUG__` | o slug da empresa | | `__DGESTOR_SSO_SECRET__` | segredo compartilhado do SSO | **Se nenhum `.template` for encontrado, a criação falha** com "Nenhum arquivo .template foi encontrado na pasta-modelo". É o jeito de garantir que o cliente novo saia com configuração própria. Molde mínimo de banco, `config/local_database.php.template`: ```php '__DGESTOR_DB_HOST__', 'database' => '__DGESTOR_DB_NAME__', 'username' => '__DGESTOR_DB_USER__', 'password' => '__DGESTOR_DB_PASS__', 'license_password' => '__DGESTOR_LICENSE_PASSWORD__', ]; ``` Molde do SSO, `dgestor-sso-config.php.template`: ```php '__DGESTOR_EMPRESA_SLUG__', 'sso_secret' => '__DGESTOR_SSO_SECRET__', ]; ``` ### 2.3 Por que a configuração fica em arquivo separado `config/local_database.php` é **protegido na atualização**: a Central nunca o substitui ao empurrar versão nova. É o que permite atualizar o código sem perder a conexão de banco nem a senha de licença de cada cliente. Consequência de projeto para o sistema novo: **nada que seja por-empresa pode morar em arquivo que a atualização sobrescreve.** Se morar, volta ao padrão sozinho na próxima atualização, sem ninguém perceber. --- ## 3. Banco de dados ### 3.1 Criação A Central cria o banco e o usuário via cPanel UAPI, nesta ordem: 1. `SubDomain / addsubdomain` — cria o subdomínio e o diretório 2. `Mysql / create_database` — nome **já com o prefixo da conta** 3. `Mysql / create_user` — usuário **já com o prefixo** 4. `Mysql / set_privileges_on_database` — privilégios totais 5. cópia dos arquivos da pasta-modelo 6. processamento dos `.template` 7. importação do schema (ver abaixo) Nomes derivam do slug: sufixo de até 18 caracteres com `-` trocado por `_` para o banco, e até 7 caracteres para o usuário (limite do cPanel). ### 3.2 Duas formas de criar as tabelas **Forma A — o produto se instala sozinho (recomendada).** O sistema detecta banco vazio e cria tudo na primeira abertura. É como o PDV funciona, com um `MigrationManager` que: - mantém uma tabela `schema_migrations (version, applied_at)`; - lê os arquivos de migração em ordem de nome; - aplica só o que ainda não está registrado; - **faz backup antes** de aplicar qualquer alteração. Cada migração é um arquivo que devolve uma lista de comandos: ```php [ "CREATE TABLE IF NOT EXISTS obras ( id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, empresa_id INT UNSIGNED NOT NULL, nome VARCHAR(120) NOT NULL, criado_em DATETIME NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4", ]]; ``` Para acrescentar coluna em tabela que já existe, o padrão usado é condicional, para a migração poder rodar duas vezes sem erro: ```php return ['sql' => [ "SET @sql = IF((SELECT COUNT(*) FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='obras' AND column_name='status')=0, 'ALTER TABLE obras ADD COLUMN status VARCHAR(20) NOT NULL DEFAULT \"aberta\"','DO 1')", "PREPARE st FROM @sql", "EXECUTE st", "DEALLOCATE PREPARE st", ]]; ``` **Forma B — `database.sql` na raiz.** Se existir um `database.sql` na raiz da pasta-modelo, a Central importa esse arquivo logo após criar o banco. Serve para produtos que não se instalam sozinhos. É menos flexível: não há caminho de atualização de schema depois. ### 3.3 Convenções de schema que valem a pena seguir - `ENGINE=InnoDB` e `utf8mb4` em tudo. Chave estrangeira não funciona em MyISAM, e a Central depende de integridade referencial em vários pontos. - Uma coluna `empresa_id` mesmo com banco por cliente. Custa pouco e deixa a porta aberta para consolidar dados depois. - Datas em `DATETIME`, dinheiro em `DECIMAL(10,2)`, quantidade em `DECIMAL(12,3)`. `FLOAT` para dinheiro gera diferença de centavo que aparece no fechamento de caixa. --- ## 4. SSO — a Central entra no sistema O botão "Acessar" da Central leva o usuário para dentro do sistema do cliente sem digitar senha. O mecanismo é um token assinado com HMAC-SHA256. ### 4.1 O que a Central envia ``` GET https:///sso-entrar.php?token=. ``` Payload, antes do base64: ```json { "sub": 12, "email": "dono@empresa.com.br", "nome": "Nome do Usuário", "papel": "admin", "empresa": "slug-da-empresa", "exp": 1790000060 } ``` Assinatura: `hash_hmac('sha256', base64_do_payload, SSO_SECRET)`. **O token vale 60 segundos** (`exp = time() + 60`). ### 4.2 O que o sistema novo precisa validar O receptor (`sso-entrar.php`) tem de recusar, nesta ordem: ```php /api/licenca/comando Content-Type: application/json X-DGESTOR-KEY: { "acao": "liberar", "validade": "2026-12-31" } { "acao": "bloquear" } ``` ### 5.2 O que o sistema novo precisa implementar Um endpoint que: 1. compara a chave do cabeçalho `X-DGESTOR-KEY` com a sua, usando `hash_equals` — e devolve **401** quando não bate; 2. aceita só as ações `liberar` e `bloquear`; 3. em `liberar`, grava a data em `assinatura_valida_ate` e limpa o bloqueio manual; em `bloquear`, marca o bloqueio manual; 4. devolve JSON com o status resultante. ```json { "sucesso": true, "status": { "ativo": true, "valida_ate": "2026-12-31" } } ``` ### 5.3 A senha mestra local Além do comando remoto, cada empresa tem uma **senha mestra de licença**, gerada na criação e gravada em `local_database.php` (campo `license_password`). Ela serve para destravar o sistema no local, quando a Central estiver fora do ar. Detalhe que custou caro no PDV e vale herdar: essa senha **não pode** ficar em arquivo substituído na atualização. Ficava, e toda atualização devolvia a senha ao padrão sem ninguém perceber. ### 5.4 Comportamento esperado quando bloqueado O sistema não deve apagar nem esconder dados. O padrão do PDV é mostrar uma tela de licença expirada com campo para a senha mestra, e bloquear as telas operacionais — mantendo login, relatórios de leitura e backup acessíveis. --- ## 6. Atualização de versão A Central copia a pasta-modelo por cima da pasta do cliente. O que ela **nunca substitui** (`dgestor_atualizar_protegido`): | Item | Motivo | |---|---| | `config/local_database.php` | conexão e senha de licença da empresa | | Qualquer arquivo `*.local.php` | configuração local | | Qualquer arquivo `*.template` | molde, não configuração | | `.htaccess` da raiz | a hospedagem escreve regras próprias ali | | `.user.ini`, `php.ini`, `error_log`, `.env` | ambiente | | `dgestor_template.json`, `sso-entrar.php` | costumam ter ajuste por empresa | | `*.md`, `*.txt` (exceto `robots.txt`) | documentação, não é sistema | | Diretórios | a cópia é arquivo a arquivo | **Consequência prática:** regra nova de `.htaccess` nunca chega aos clientes por atualização. Se o sistema novo depender de `.htaccess`, precisa de um script próprio que acrescente o bloco sem apagar o que a hospedagem escreveu. A atualização é **arquivo a arquivo, com comparação byte a byte**. Arquivo alterado no cliente é detectado e a atualização é recusada até ser conferida — o que evita sobrescrever um conserto feito à mão. --- ## 7. Registro na Central ### 7.1 Tabelas ```sql dgestor_empresas ( id, nome, slug UNIQUE, url_gestao, modelo_codigo, senha_licenca, banco, usuario_banco, ativo, criado_em ) dgestor_usuarios ( id, nome, email UNIQUE, senha_hash, papel ENUM('admin','gestor','equipe'), ativo, criado_em ) dgestor_usuario_empresas ( usuario_id, empresa_id, papel, PRIMARY KEY (usuario_id, empresa_id) ) dgestor_licencas ( id, empresa_id UNIQUE, token, plano ENUM('teste','mensal','trimestral','semestral','anual'), status ENUM('ativo','bloqueado'), inicio DATE, vencimento DATE, criado_em ) dgestor_modelos ( id, codigo UNIQUE, nome, caminho_pasta, admin_email, admin_senha ) ``` ### 7.2 Cadastrar o produto novo Para a Central poder criar empresas do sistema novo, basta uma linha em `dgestor_modelos` — pela tela **Modelos** do painel, sem mexer em código: | Campo | Exemplo | |---|---| | `codigo` | `construcao` | | `nome` | `DGestor Construção` | | `caminho_pasta` | `/home2//modelos/construcao` | | `admin_email` | e-mail do administrador inicial, se o produto usar | | `admin_senha` | senha inicial, se o produto usar | A partir daí o produto aparece no provisionador como qualquer outro. --- ## 8. Roteiro de implementação Na ordem em que se consegue testar cada etapa: 1. **Sistema rodando com banco próprio**, lendo a conexão de `config/local_database.php`, com um instalador que cria as tabelas na primeira abertura. 2. **Controle de versão de schema** (`schema_migrations` + migrações numeradas), com backup antes de aplicar. 3. **`DGESTOR_TEMPLATE.json`** na raiz, listando os arquivos obrigatórios. 4. **`config/local_database.php.template`** com os marcadores. 5. **`dgestor-sso-config.php.template`** e **`sso-entrar.php`** com as três validações (assinatura, validade, slug). 6. **Endpoint de licença** com `X-DGESTOR-KEY`, mais a tela de licença expirada com a senha mestra. 7. **Cadastro em `dgestor_modelos`** apontando para a pasta-modelo. 8. **Criar uma empresa de teste** pelo provisionador e conferir: subdomínio no ar, banco criado, `.template` processados e apagados, SSO entrando. --- ## 9. Erros que já custaram caro no PDV Vale herdar os consertos em vez de repetir os erros. **Configuração por empresa em arquivo que a atualização substitui.** A senha de licença voltava ao padrão a cada atualização, calada. Tudo que é por empresa vai em arquivo protegido. **`@page` global no CSS.** Uma regra de tamanho de papel escrita para uma etiqueta valia para o sistema inteiro, e todo relatório saía impresso em 10×15 cm. Regra de página é global por natureza: use página nomeada. **Permissão lida só no login.** Perfil e permissões eram gravados na sessão na entrada e nunca mais relidos; mudar a permissão de alguém não tinha efeito até a pessoa sair e entrar, o que ninguém faz no meio do expediente. Releia a cada requisição. **Aviso de PHP impresso na página.** `display_errors` ligado na hospedagem fazia um `Undefined array key` aparecer na tela do operador. Desligue a exibição e mande para o log, mantendo o nível de erro completo. **Transação aninhada.** O PDO não aninha: uma função que abre a própria transação não pode ser chamada de dentro de outra. Decida qual camada é dona da transação. **Exceção engolida em `try/catch` vazio.** O defeito some e sobra o resultado errado. Se não dá para tratar, pelo menos registre. --- ## 10. Checklist de aceite O sistema novo está integrado quando: - [ ] A Central cria uma empresa do zero sem erro, e o subdomínio abre. - [ ] Os `.template` foram processados e **apagados** do diretório do cliente. - [ ] O banco foi criado com tabelas e o sistema abre sem instalação manual. - [ ] O botão "Acessar" da Central entra sem pedir senha. - [ ] Um token de outra empresa é **recusado**. - [ ] Um token com mais de 60 segundos é **recusado**. - [ ] `bloquear` pela Central bloqueia o sistema; `liberar` destrava. - [ ] A senha mestra destrava localmente com a Central fora do ar. - [ ] Uma atualização não apaga `config/local_database.php`. - [ ] Rodar a mesma migração duas vezes não dá erro. --- *Documento gerado a partir do código da Central DGestor em produção e do produto base já integrado. Última revisão: 01/10/2026.*