# Instalação — XML Monitor

Guia completo para colocar o sistema no ar em **cPanel compartilhado**, que é o
cenário mais restrito. Em VPS ou servidor dedicado tudo aqui funciona igual, com
mais folga.

Ao final, confira tudo com um comando:

```bash
php artisan xml:diagnostico
```

---

## 1. Requisitos do servidor

### PHP 8.3 ou superior

No cPanel: **Select PHP Version**. Versões abaixo de 8.3 não sobem a aplicação.

### Extensões obrigatórias

Ative todas em **Select PHP Version → Extensions**. Sem qualquer uma delas o
`composer install` falha logo na instalação, com o nome da que falta.

| Extensão | Para quê |
|---|---|
| `openssl` | Ler o certificado A1 e assinar as consultas à SEFAZ |
| `soap` | Comunicação com o NFeDistribuicaoDFe |
| `curl` | Transporte HTTP das chamadas |
| `dom`, `simplexml`, `libxml` | Leitura e validação dos XMLs |
| `zlib` | Descompactar os documentos (a SEFAZ entrega em gzip) |
| `zip` | Montar o pacote `.zip` de download |
| `gd` | Gerar o código de barras do DANFE |
| `mbstring` | Acentuação |
| `intl` | Formatação de valores em real |
| `fileinfo` | Validar arquivos enviados (certificado, logo) |
| `bcmath` | Cálculos de precisão nos valores fiscais |
| `json` | Serialização interna |

> ### Confirme o `soap` ANTES de contratar
>
> É a extensão mais frequentemente ausente em planos compartilhados, e alguns
> provedores simplesmente não a oferecem. Sem `soap` **não há comunicação com a
> SEFAZ** — o sistema inteiro deixa de cumprir sua função. Verifique em
> *Select PHP Version → Extensions* ou abra um chamado antes de fechar o plano.

### Banco de dados

**MySQL 5.7+** ou **MariaDB 10.3+**. Em *MySQL Databases*, crie o banco e o
usuário e conceda **ALL PRIVILEGES**.

> SQLite funciona para testes, mas trava com acessos simultâneos. Não use em produção.

### Acesso a terminal

*Terminal* do cPanel ou SSH. É necessário para as migrações e o usuário master.
Se a hospedagem não oferecer nenhum dos dois, troque de plano — não há caminho
seguro sem linha de comando.

### Qual PHP usar na linha de comando

Esta é a pegadinha mais comum do cPanel: o `php` do terminal costuma ser uma
versão **diferente** da que o site usa. Descubra o caminho real:

```bash
php -v                                   # pode ser 7.4, mesmo com o site em 8.3
ls /opt/cpanel/ea-php83/root/usr/bin/php # caminho típico do EasyApache
which ea-php83
```

Use sempre o caminho completo nos comandos e no cron:

```bash
/opt/cpanel/ea-php83/root/usr/bin/php artisan xml:diagnostico
```

Para encurtar, crie um atalho no seu `~/.bashrc`:

```bash
alias php='/opt/cpanel/ea-php83/root/usr/bin/php'
```

O `php artisan xml:instalar` imprime o cron já com o binário correto — ele usa
o mesmo PHP com que foi executado.

---

## 2. Preparar o pacote na sua máquina

O servidor compartilhado não roda Node. Os assets são compilados aqui e enviados prontos.

```bash
composer install --no-dev --optimize-autoloader
npm ci && npm run build
```

Confirme que `public/build/manifest.json` existe. Sem ele, o site sobe sem CSS.

**Envie a pasta `vendor/` junto.** Assim o servidor não precisa de Composer nem
de acesso à internet — e você não depende de a hospedagem ter memória suficiente
para resolver dependências.

### Como enviar

`vendor/` tem milhares de arquivos pequenos; por FTP isso leva horas e costuma
falhar no meio. Compacte antes:

```bash
# na sua máquina, dentro da pasta do projeto
zip -r app-xmlmonitor.zip . -x "node_modules/*" ".git/*" "tests/*" "*.zip"
```

No cPanel, em *File Manager*: envie o `.zip` para `/home/USUARIO/`, clique com o
botão direito e escolha **Extract**. Depois mova o conteúdo de `public/` para o
`public_html`.

> No File Manager, ative **Settings → Show Hidden Files** antes de procurar o
> `.env` e o `.htaccess`. Sem isso eles ficam invisíveis e parecem não ter sido enviados.

---

## 3. Estrutura de diretórios

A aplicação fica **fora** do document root. Só o conteúdo de `public/` vai para `public_html`.

```
/home/USUARIO/
├── app-xmlmonitor/           ← projeto completo, fora da web
│   ├── app/  bootstrap/  config/  database/  lang/
│   ├── resources/  routes/  storage/  vendor/
│   ├── artisan
│   └── .env
└── public_html/              ← apenas o conteúdo de public/
    ├── index.php             ← copie de deploy/public_html-index.php
    ├── .htaccess             ← copie de deploy/public_html-.htaccess
    ├── robots.txt
    └── build/                ← copiado de public/build
```

**Por que separado:** certificados digitais A1 e XMLs ficam em
`app-xmlmonitor/storage/app/`. Se a aplicação estivesse dentro do `public_html`,
qualquer pessoa poderia baixar o `.pfx` dos seus clientes pela URL — e com ele
emitir nota em nome deles.

Se você renomear a pasta do projeto, ajuste a variável `$base` no `public_html/index.php`.

---

## 4. Variáveis de ambiente

Copie `.env.example` para `.env` e preencha. Abaixo, tudo o que importa.

### Aplicação

| Variável | Valor | Observação |
|---|---|---|
| `APP_NAME` | `"XML Monitor"` | Pode ser trocado depois pelo painel master |
| `APP_ENV` | `production` | |
| `APP_KEY` | *(gerada)* | Veja o aviso abaixo |
| `APP_DEBUG` | `false` | **Nunca `true` em produção**: a tela de erro mostra as senhas do `.env` |
| `APP_URL` | `https://seudominio.com.br` | Usada nos links de e-mail |
| `APP_LOCALE` | `pt_BR` | |
| `APP_TIMEZONE` | `America/Sao_Paulo` | Datas fiscais dependem disso |

> ### A APP_KEY é insubstituível
>
> As senhas dos certificados digitais são criptografadas com ela. Trocar a
> `APP_KEY` depois de cadastrar clientes torna todas as senhas ilegíveis, e cada
> empresa terá de reenviar o certificado. **Guarde uma cópia em local seguro.**

### Banco

| Variável | Exemplo |
|---|---|
| `DB_CONNECTION` | `mysql` |
| `DB_HOST` | `127.0.0.1` |
| `DB_PORT` | `3306` |
| `DB_DATABASE` | `usuario_xmlmonitor` |
| `DB_USERNAME` | `usuario_xml` |
| `DB_PASSWORD` | *(a senha do banco)* |

> **Senhas com caracteres especiais** (`#`, espaço, `$`) precisam de **aspas duplas**:
> `DB_PASSWORD="minha#senha"`. O `.env` corta tudo depois de um `#` sem aspas —
> é um erro comum e o sintoma é "acesso negado" sem explicação.

### Sessão, fila e cache

Tudo em banco. Hospedagem compartilhada não tem Redis nem supervisor.

| Variável | Valor |
|---|---|
| `SESSION_DRIVER` | `database` |
| `SESSION_LIFETIME` | `240` |
| `SESSION_SECURE_COOKIE` | `true` *(exige HTTPS)* |
| `QUEUE_CONNECTION` | `database` |
| `CACHE_STORE` | `database` |
| `FILESYSTEM_DISK` | `local` |

### E-mail

Sem SMTP, a recuperação de senha não chega ao usuário. Use uma conta do próprio cPanel.

| Variável | Exemplo |
|---|---|
| `MAIL_MAILER` | `smtp` |
| `MAIL_HOST` | `mail.seudominio.com.br` |
| `MAIL_PORT` | `465` |
| `MAIL_SCHEME` | `smtps` |
| `MAIL_USERNAME` | `nao-responda@seudominio.com.br` |
| `MAIL_PASSWORD` | *(senha da conta)* |
| `MAIL_FROM_ADDRESS` | `nao-responda@seudominio.com.br` |

### Monitor SEFAZ

| Variável | Padrão | O que faz |
|---|---|---|
| `NFE_AMBIENTE` | `1` | 1 = Produção, 2 = Homologação |
| `NFE_INTERVALO_MIN` | `60` | Intervalo mínimo entre consultas da mesma empresa, em minutos |
| `NFE_MAX_REQUISICOES` | `20` | Teto de chamadas por sincronização (cada uma traz até 50 documentos) |
| `NFE_PAUSA_MS` | `800` | Pausa entre chamadas |
| `NFE_MAX_MANIFESTACOES` | `20` | Manifestações automáticas por ciclo |

Estes limites **não são conservadorismo**: a SEFAZ bloqueia o CNPJ por
consumo indevido (cStat 656) quando são ultrapassados. Baixar os valores
prejudica o cliente, não o sistema.

### Atalhos de login

`DEMO_LOGIN` só existe para desenvolvimento. Em produção deixe **ausente ou `false`**,
caso contrário a tela de login exibe botões com credenciais.

---

## 5. Instalação

No *Terminal* do cPanel, dentro de `app-xmlmonitor`:

```bash
php artisan xml:instalar
```

O comando faz, na ordem correta:

1. Confere as extensões do PHP
2. Gera a `APP_KEY` (se ainda não existir)
3. Testa a conexão com o banco e roda as migrações
4. Pergunta nome, e-mail e senha do **usuário master**
5. Gera os caches de configuração, rotas e views
6. Imprime a linha exata do cron **com os caminhos do seu servidor**

Para criar outro master depois:

```bash
php artisan xml:instalar --somente-master
```

### Terminal sem interatividade

O *Terminal* do cPanel aceita as perguntas normalmente. Já um SSH sem TTY, um
script de implantação ou o `Cron Jobs` não aceitam — e um comando interativo
ficaria travado esperando digitação. Nesses casos, passe os dados por parâmetro:

```bash
php artisan xml:instalar --no-interaction
php artisan xml:instalar --somente-master \
    --nome="Administrador Master" \
    --email=voce@seudominio.com.br \
    --senha="SuaSenhaForte123"
```

A senha exige no mínimo 8 caracteres, com letras e números. Use **aspas duplas**
se ela tiver `#`, `$` ou espaço — o shell corta esses caracteres sem elas.

> Depois de instalar, limpe o histórico do shell: a senha fica gravada nele.
> `history -c` (bash) ou apague `~/.bash_history`.

### Permissões

```bash
chmod -R 775 storage bootstrap/cache
chmod -R 700 storage/app/certificados
```

---

## 6. Cron — o motor do sistema

**Uma única entrada**, a cada minuto, em *Cron Jobs*:

```
* * * * * /usr/local/bin/php /home/USUARIO/app-xmlmonitor/artisan schedule:run >/dev/null 2>&1
```

Confirme o caminho do PHP 8.3 com `which php` ou em *Select PHP Version*.
O `php artisan xml:instalar` imprime a linha já com os caminhos do seu servidor.

O agendador cuida de tudo:

| Tarefa | Frequência | O que faz |
|---|---|---|
| Batimento | 1 min | Marca que o cron está vivo (usado pelo diagnóstico) |
| `xml:monitor` | 15 min | Dispara a sincronização das empresas que já cumpriram o próprio intervalo |
| `queue:work --stop-when-empty` | 5 min | Consome a fila; substitui o supervisor |
| `queue:prune-failed` | semanal | Limpa jobs falhos com mais de 30 dias |

**Sem o cron, nenhum XML é baixado.** É o erro de instalação mais comum, e o
`xml:diagnostico` acusa em vermelho quando o cron não roda há mais de 10 minutos.

---

## 7. Conferência final

```bash
php artisan xml:diagnostico
```

Verifica 25 pontos: versão do PHP, cada extensão, `APP_KEY`, `APP_DEBUG`,
conexão e tabelas do banco, permissões de escrita, os três discos privados,
**se os certificados estão fora da pasta pública**, assets compilados,
usuário master, cron e certificados vencidos.

Saída esperada: `Tudo certo. Sistema pronto para operar.`

Teste também o monitor sem esperar o cron:

```bash
php artisan xml:monitor --sync --force
```

---

## 8. Primeiros passos no sistema

1. Acesse `https://seudominio.com.br/entrar` com o usuário master.
2. **Configurações do site** — nome, cor, logo, favicon, textos do login e seus dados de desenvolvedor.
3. **Empresas → Nova empresa** — cadastre o cliente e o administrador dele.
4. Instale o certificado A1 em **Certificados**, ou peça ao cliente que o envie pelo próprio painel.
5. O monitor começa sozinho no ciclo seguinte. Para não esperar, use **Sincronizar** na tela da empresa.

Se preferir que os clientes se cadastrem sozinhos, mantenha
**Permitir cadastro público** ligado em Configurações do site. Desligado, só o
master cria empresas.

---

## 9. Atualizações

```bash
php artisan down
# envie os arquivos novos
composer install --no-dev --optimize-autoloader
php artisan migrate --force
php artisan optimize:clear
php artisan config:cache && php artisan route:cache && php artisan view:cache
php artisan up
php artisan xml:diagnostico
```

Envie também o `public/build` recompilado quando houver mudança de interface.

---

## 10. Problemas comuns

| Sintoma | Causa provável |
|---|---|
| Site sem CSS | `public/build` não foi enviado, ou falta `manifest.json` |
| "419 Page Expired" no login | `SESSION_SECURE_COOKIE=true` sem HTTPS ativo |
| Nenhum XML chega | Cron não criado. Rode `xml:diagnostico` |
| "acesso negado" no banco | Senha com `#` ou espaço sem aspas duplas no `.env` |
| "Não foi possível abrir o certificado" | Senha errada, ou arquivo A3 (token/cartão) em vez de A1 |
| SEFAZ devolve cStat 656 | Consumo indevido. Aumente `NFE_INTERVALO_MIN` e aguarde uma hora |
| Nota vem só como "resumo" | Normal. O XML completo exige manifestação de ciência |
| Erro 500 após atualizar | Cache antigo. Rode `php artisan optimize:clear` |
| `xml:instalar` trava sem responder | Terminal sem TTY. Use `--email` e `--senha` |
| "Class SoapClient not found" | Extensão `soap` desativada em *Select PHP Version* |
| Comando roda no navegador mas falha no cron | Cron usando outro PHP. Use o caminho completo do binário |
| `.env` some no File Manager | Ative *Settings → Show Hidden Files* |
| Erro de permissão ao gravar XML | `chmod -R 775 storage` |

Para suporte, anexe a saída de:

```bash
php artisan xml:diagnostico
```

---

## Guarda de documentos

Os XMLs devem ser mantidos por **5 anos**, por exigência legal. O sistema não
apaga nada automaticamente. Ao contratar a hospedagem, dimensione o disco
considerando esse período — e mantenha backup do banco **e** da pasta
`storage/app/xmls`.
