# Implantação no cPanel (hospedagem compartilhada)

Guia passo a passo para publicar o Shared Inbox em hospedagem com **cPanel/WHM**
(Apache ou LiteSpeed + PHP-FPM/mod_php + MySQL) usando o **"Pipe to a Program" do
Exim** para receber os e-mails e **cron** para processar a fila.

> Comandos de exemplo usam `/home/USUARIO/app` como diretório da aplicação e
> `mail.seudominio.com` como domínio público. Ajuste aos seus dados.

---

## 1. Pré-requisitos

- PHP **8.2+** com `pdo_mysql`, `mbstring`, `sodium`, `openssl` (confira em
  cPanel → *PHP Selector* → *Extensions*, ou `php -m` no Terminal).
- Banco MySQL/MariaDB (cPanel → *MySQL® Databases*).
- Acesso a **Terminal** (ou SSH) altamente recomendável para os crons e testes.
- O horário do servidor correto (senão CSRF/sessão e agendamentos falham).

---

## 2. Upload da aplicação

Envie a pasta do projeto **inteira** para fora da raiz web, por exemplo:

```
/home/USUARIO/app/          ← aplicação (NESTED aqui: bin, src, config, views…)
/home/USUARIO/public_html/  ← raiz web padrão do domínio
```

Confirme a estrutura:

```
/home/USUARIO/app/bin/pipe.php
/home/USUARIO/app/config/config.php
/home/USUARIO/app/public/index.php
/home/USUARIO/app/public/.htaccess
/home/USUARIO/app/storage/{attachments,logs,unprocessed}
```

Permissões:

```bash
chmod 755 /home/USUARIO/app/bin/*.php
chmod 640 /home/USUARIO/app/config/config.php
chmod -R 755 /home/USUARIO/app/storage     # PHP precisa escrever aqui
```

### 2.1 Fazer o site apontar para `public/`

**Opção A (recomendada) — trocar o Document Root do domínio:**
cPanel → *Domains* → gerenciar o domínio → *Document Root* →
`/home/USUARIO/app/public` (no cPanel moderno o campo é editável; se não for,
peça ao suporte da hospedagem ou use a Opção B).

**Opção B — `public_html` como fachada** (não exige mudar Document Root).
Coloque este `/home/USUARIO/public_html/.htaccess`:

```apache
Options -Indexes
DirectoryIndex index.php
RewriteEngine On

# assets da aplicação
RewriteRule ^assets/(.*)$ app/public/assets/$1 [L]
RewriteRule ^favicon\.ico$ app/public/favicon.ico [L]

# demais requisições → front controller
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^ app/public/index.php [L]
```

Com a aplicação em `public_html/app/`. O `app.url` do config continua sendo
`https://mail.seudominio.com` (sem `/app`).

---

## 3. Banco de dados

1. cPanel → *MySQL® Databases* → crie o banco `seuusuario_sharedinbox`
   e um usuário (anote a senha).
2. Associe o usuário ao banco com **ALL PRIVILEGES**.
3. cPanel → *phpMyAdmin* → selecione o banco → *Import* →
   `database/schema.sql` → *Go*.

Alternativa via Terminal (mesmo arquivo é idempotente: `CREATE TABLE IF NOT EXISTS`):

```bash
mysql -u seuusuario -p seuusuario_sharedinbox < /home/USUARIO/app/database/schema.sql
```

---

## 4. Configuração (`config/config.php`)

```bash
cd /home/USUARIO/app
cp config/config.example.php config/config.php
php bin/generate-key.php --write     # gera e grava o app.key
```

Edite `config/config.php`:

```php
'app' => [
    'url'  => 'https://mail.seudominio.com',  // SEM barra no final
    'env'  => 'production',
    'debug'=> false,                          // true só para depurar
],
'db' => [
    'host' => 'localhost', 'port' => 3306,
    'name' => 'seuusuario_sharedinbox',
    'user' => 'seuusuario_app',
    'password' => '********',
],
'security' => [
    'cookie_secure' => true,   // true quando o site usa HTTPS
],
```

Pontos importantes:

- `app.url` é usada para gerar links absolutos (assets, e-mails, SSE): tem que ser
  exatamente a URL pública (com `https://` se houver SSL).
- Nunca publique `config/config.php` (está fora da raiz web por definição).

---

## 5. Instalação (schema + super admin)

```bash
cd /home/USUARIO/app
php bin/install.php --org="Minha Empresa" \
                    --email=admin@seudominio.com \
                    --password='S3nh@Forte' --name="Administrador"
```

O instalador:

1. importa/verifica `database/schema.sql`;
2. confere/gera a `app.key`;
3. cria (ou atualiza) o super admin e, com `--org`, a primeira organização.

Sem acesso CLI, importe o schema pelo phpMyAdmin e crie o super admin editando o
caminho do script em *Terminal* ou pedindo execução ao suporte.

Depois, entre em `https://mail.seudominio.com/login` e cadastre em
**Administração → Caixas Postais** os endereços que serão atendidos
(host/porta SMTP e IMAP, usuário e senha — as senhas ficam cifradas).

---

## 6. Receber e-mails: pipe do Exim

### 6.1 Por endereço (recomendado no cPanel)

cPanel → *Email* → *Forwarders* → **Add Forwarder** → escolha o endereço
(ex.: `suporte@seudominio.com`) → *Advanced Options* → **Pipe to a Program**:

```
|/usr/bin/php -q /home/USUARIO/app/bin/pipe.php
```

Repita para cada caixa postal cadastrada no painel do aplicativo.

Confirme o caminho do PHP: `which php` (em algumas hospedagens é
`/usr/local/bin/php` ou `/opt/cpanel/.../php`). Torna o script executável:

```bash
chmod 755 /home/USUARIO/app/bin/pipe.php
```

### 6.2 Passando o destinatário explicitamente (opcional)

Se a hospedagem permitir filtro customizado que repassa o destinatário como
argumento, o `pipe.php` aceita e usa esse endereço como pista de resolução:

```
|/usr/bin/php -q /home/USUARIO/app/bin/pipe.php $0
```

A resolução da caixa usa, nesta ordem: **argumento do filtro → cabeçalhos
(`Delivered-To`/`Envelope-To`/`To`/`Cc`) → domínio (se houver uma única caixa
ativa) → fallback global**. Se nenhuma casa com a mensagem, ela é gravada em
`storage/unprocessed/` e registrada no log (nunca faz bounce: o pipe sai com `0`).

### 6.3 Fallback: sincronização por IMAP

Quando a hospedagem **não** permite "Pipe to a Program":

1. Ative `imap_enabled` em **Administração → Caixas Postais** e informe
   host (ex.: `mail.seudominio.com`), porta `993`, usuário e senha;
2. Agende o cron (seção 7).

O cliente IMAP é próprio (socket + TLS), não depende de `ext-imap`.

### 6.4 Regras de ouro do pipe

- `bin/pipe.php` **nunca escreve na STDOUT** (qualquer saída vira *bounce* para o
  remetente) e sempre termina com código `0` (aceita a mensagem).
- Logs de tudo ficam em `storage/logs/app.log` (rotação diária em `cleanup.php`).

---

## 7. Cron jobs

cPanel → *Cron Jobs*. Use o caminho real do PHP (`which php`) e o caminho absoluto
do script. Sugestão:

```cron
# fila de envio/tarefas — a cada minuto (worker encerra sozinho em ~55 s)
* * * * * /usr/bin/php -q /home/USUARIO/app/bin/queue_worker.php

# fallback IMAP — só se NÃO houver pipe (a cada 5 minutos)
*/5 * * * * /usr/bin/php -q /home/USUARIO/app/bin/imap_sync.php

# manutenção — diário às 03:30
30 3 * * * /usr/bin/php -q /home/USUARIO/app/bin/cleanup.php
```

Observações:

- `queue_worker.php` processa um lote e sai (`max_runtime` = 55 s, cabe no limite
  de 300 s do cPanel). Use `--once` para uma única leva sob demanda.
- Se a hospedagem cobrar por CPU, troque `* * * * *` por `*/2 * * * *` e use
  `bin/send_reply.php` (sem argumentos) para drenar a fila imediatamente.
- Os crons são a **única** forma de enviar e-mails: sem cron, as respostas ficam
  como `queued` (o painel mostra a fila em **Administração → Fila**).

---

## 8. HTTPS / SSL

1. cPanel → *SSL/TLS Status* (ou *AutoSSL*) → emitir certificado para o domínio.
2. Force HTTPS: em *Domains* ative *Force HTTPS Redirect*.
3. No config: `'url' => 'https://mail.seudominio.com'` e
   `'cookie_secure' => true`.
4. Reinicie o PHP (cPanel → *MultiPHP Manager* / *Select PHP Version* →
   *Kill Sessions* ou aguarde) se alterou `php.ini`.

---

## 9. Checklist pós-implantação

| # | Teste | Esperado |
| --- | --- | --- |
| 1 | Abrir `https://…/login` | Tela de login, sem erro 500 |
| 2 | Login do super admin | Redireciona para `/admin` |
| 3 | Criar usuário agente e caixa postal | Listados corretamente |
| 4 | *Caixas Postais* → **Testar** | `SMTP OK` / `IMAP OK` |
| 5 | Enviar e-mail de teste para a caixa | Conversa aparece em `/inbox` e badge sobe |
| 6 | Responder pela interface | `Resposta enviada` + `delivery_status = sent` na fila |
| 7 | Deixar `/inbox` aberto em 2 abas e receber e-mail | Atualiza sem recarregar (SSE "Ao vivo") |
| 8 | `tail -f storage/logs/app.log` | Sem `ERRO`/`CRITICAL` |
| 9 | Administração → Fila | Job concluído, sem `last_error` |

---

## 10. Solução de problemas

| Sintoma | Causa provável | Solução |
| --- | --- | --- |
| `419` ao entrar (CSRF) | `app.url` diferente da URL acessada, sem HTTPS com `cookie_secure=true`, ou relógio dessincronizado | Igual `app.url` à URL pública; libere HTTPS ou use `cookie_secure=false` apenas em HTTP; confira a data do servidor |
| E-mail enviado não chega | Forwarder/pipe não configurado, PHP errado ou sem execução | Reveja a seção 6; `which php`; `chmod 755 bin/pipe.php`; veja `storage/logs/app.log` filtrando `pipe:` |
| Chegou mas virou conversa errada/órfã | Caixa postal não resolveu (múltiplas caixas no mesmo domínio sem hint) | Use o filtro com destinatário explícito ou cadastre os endereços exatos no painel; confira `storage/unprocessed/` |
| Mensagens "travadas" em `queued` | Cron do `queue_worker` ausente ou falha de SMTP | Crie o cron; Administração → Fila → veja `last_error`; teste a caixa postal |
| Interface mostra "Reconectando" | Proxy/buffer do servidor segurando o SSE ou timeout de PHP | O app já envia `X-Accel-Buffering: no`; desative buffering no LiteSpeed/mod_deflate para `/api/stream`; aumente `max_execution_time` |
| `500` na página | Erro de configuração/banco | `app.debug => true` temporariamente, veja `storage/logs/app.log`, confira credenciais do MySQL |
| Anexo não sobe (`413`) | Limites do `php.ini` | Aumente `upload_max_filesize` e `post_max_size`; ajuste `storage.max_attachment_mb` |
| Sem permissão em `storage/` | Dono/permissão errados | `chmod -R 755 storage` e confira o usuário do PHP (`suPHP` exige dono = usuário da conta) |
| Imagens/scripts não carregam (404) | Document Root apontando para a pasta errada | Deve apontar para `public/` (ou a fachada da seção 2.1) e `app.url` correto |

---

## 11. Atualização e backup

**Backup:** banco (phpMyAdmin/`mysqldump`) + `config/config.php` + `storage/`.

**Atualização:** substitua `src/`, `views/`, `public/`, `bin/` e
`database/schema.sql`, mantendo `config/` e `storage/`. O `schema.sql` é
idempotente para criação de tabelas; se houver mudanças em tabelas existentes,
aplique os `ALTER` correspondentes manualmente antes de entrar no ar.

---

## 12. Segurança (resumo)

- Mantenha `config/config.php` com `chmod 640` e fora da raiz web (padrão).
- Não exponha `storage/`, `database/`, `src/` nem `bin/` — só `public/`.
- Troque as senhas iniciais criadas na instalação.
- Revise **Administração → Usuários** (papéis: `super_admin` global, `admin` da
  organização, `agent` operador).
- Ative AutoSSL/HTTPS e mantenha `cookie_secure => true`.
