# Deploy — EC2 (Apache reverse proxy → Node)

Site 100% independente da Lovable em runtime. O Lovable é utilizado apenas como editor. O runtime em produção é um servidor Node gerado pelo Nitro (`preset: node-server`).

O Apache permanece na frente, cuidando de TLS, HTTP/2 e proxy reverso para o processo Node local.

---

# Pré-requisitos

* Ubuntu/Debian
* Node.js 20 LTS (recomendado)
* npm
* Apache 2.4
* PM2
* Security Group liberando apenas:

  * 22/TCP — **restrito à sub-rede privada da VPN Pritunl** (nunca 0.0.0.0/0)
  * 80/TCP
  * 443/TCP

A porta `3000` deve permanecer acessível apenas localmente.

⚠️ SSH nunca deve ser exposto à internet pública. O acesso administrativo (humano) e o deploy automático (CI/CD) só chegam à porta 22 através da VPN Pritunl.

Instalação:

```bash
sudo apt update
sudo apt install -y apache2

sudo a2enmod proxy proxy_http headers ssl rewrite

sudo npm install -g pm2
```

---

# Build

```bash
cd /var/www/html/investplus/investplus-v2-cc02e8cb

git pull
```

Primeira instalação:

```bash
rm -rf node_modules package-lock.json
npm install --no-audit --no-fund
```

Build:

```bash
npm run build:ec2
```

⚠️ Use sempre `build:ec2` (não `npm run build`).

O script `build` puro usa `vite.config.ts` com preset Nitro `cloudflare-module` (para Cloudflare Workers/Lovable) e **não funciona sob PM2/Node** — vai gerar uma build que aparenta funcionar mas quebra em rotas com chunks lazy-loaded (erro `ENOENT` em `.output/public/assets/*.js`).

Só `build:ec2` (`build:ec2-node`) usa `vite.config.ec2.ts`, que injeta `EC2_BUILD=1` e `preset: "node-server"`.

---

# Estrutura esperada

```text
.output/
├── nitro.json
├── public/
└── server/
    └── index.mjs
```

Verificação:

```bash
cat .output/nitro.json
```

Resultado esperado:

```json
{
  "preset": "node-server"
}
```

---

# Teste manual

```bash
PORT=3000 NODE_ENV=production node .output/server/index.mjs
```

Em outro terminal:

```bash
curl http://127.0.0.1:3000
```

Resultado esperado:

```text
HTTP/1.1 200 OK
```

---

# PM2

Crie o arquivo:

`ecosystem.config.cjs`

```js
module.exports = {
  apps: [
    {
      name: "investplus",
      script: ".output/server/index.mjs",
      cwd: "/var/www/html/investplus/investplus-v2-cc02e8cb",
      interpreter: "node",
      env: {
        NODE_ENV: "production",
        PORT: 3000
      }
    }
  ]
};
```

⚠️ O arquivo deve ser `.cjs`, pois o projeto utiliza:

```json
"type": "module"
```

e o PM2 não consegue carregar `module.exports` em `.js`.

Subida:

```bash
pm2 start ecosystem.config.cjs
pm2 save
pm2 startup
```

Verificação:

```bash
pm2 status
pm2 logs investplus
curl http://127.0.0.1:3000
```

---

# Apache Reverse Proxy

Arquivo:

```bash
sudo nano /etc/apache2/sites-available/investplus.conf
```

Conteúdo:

```apache
<VirtualHost *:80>

    ServerName investplus.exemplo.com

    ProxyPreserveHost On
    ProxyRequests Off

    ProxyPass        / http://127.0.0.1:3000/
    ProxyPassReverse / http://127.0.0.1:3000/

    RequestHeader set X-Forwarded-Proto "http"
    RequestHeader set X-Forwarded-For "%{REMOTE_ADDR}s"

    ErrorLog ${APACHE_LOG_DIR}/investplus_error.log
    CustomLog ${APACHE_LOG_DIR}/investplus_access.log combined

</VirtualHost>
```

Habilitar:

```bash
sudo a2ensite investplus
sudo apachectl configtest
sudo systemctl reload apache2
```

---

# HTTPS

```bash
sudo apt install -y certbot python3-certbot-apache

sudo certbot --apache \
    -d investplus.exemplo.com
```

---

# Deploy automático (CI/CD)

O deploy é automatizado via GitHub Actions (`.github/workflows/deploy.yml`).

Disparo:

* push/merge na `main`
* manual, em Actions → "Deploy to EC2" → "Run workflow"

Fluxo do workflow:

1. Runner instala OpenVPN e conecta na VPN Pritunl usando um profile dedicado ao CI (`.ovpn` com certificado embutido, sem prompt — secret `PRITUNL_OVPN_CONFIG`).
2. Com o túnel ativo, conecta via SSH no **IP privado** da EC2 (secret `EC2_HOST`), como o usuário dedicado `deploy` (secret `EC2_USERNAME`, chave em `EC2_SSH_KEY`).
3. Executa `sudo /usr/local/bin/deploy-investplus.sh` — o usuário `deploy` só tem permissão de sudo para esse script específico (`/etc/sudoers.d/deploy-investplus`, `NOPASSWD`), nunca sudo irrestrito.

O script `/usr/local/bin/deploy-investplus.sh` (dono `root`, `chmod 700`) roda exatamente:

```bash
#!/bin/bash
set -e

cd /var/www/html/investplus/investplus-v2-cc02e8cb/

git pull

rm -rf node_modules package-lock.json
npm install --no-audit --no-fund

rm -rf .output .nuxt dist

npm run build:ec2

pm2 restart investplus
```

Secrets necessários no repositório GitHub:

| Secret | Valor |
| --- | --- |
| `PRITUNL_OVPN_CONFIG` | conteúdo do `.ovpn` do profile Pritunl dedicado ao CI |
| `EC2_HOST` | IP **privado** da EC2 (alcançável só via VPN) |
| `EC2_USERNAME` | `deploy` |
| `EC2_SSH_KEY` | chave privada do usuário `deploy` (autorizada em `~deploy/.ssh/authorized_keys`) |
| `EC2_PORT` | opcional, só se não for 22 |

⚠️ Por que o usuário `deploy` existe: o processo/PM2 roda como `root`, mas o GitHub Actions nunca deve logar como `root` via SSH. `deploy` é um usuário isolado, sem senha, com sudo restrito a um único script — se a chave vazar, o dano possível é limitado a rodar esse script, nada mais.

---

# Deploy manual (fallback)

Use apenas se o CI/CD estiver indisponível. Requer acesso SSH via VPN como usuário com privilégio suficiente.

```bash
cd /var/www/html/investplus/investplus-v2-cc02e8cb

git pull

rm -rf node_modules package-lock.json
npm install --no-audit --no-fund

rm -rf .output .nuxt dist

npm run build:ec2

pm2 restart investplus
```

ou:

```bash
pm2 restart ecosystem.config.cjs
```

---

# Variáveis de ambiente

Crie um `.env`:

```env
NODE_ENV=production
PORT=3000
```

Nunca exponha segredos usando prefixo:

```text
VITE_
```

Tudo que começa com `VITE_` é enviado para o navegador.

---

# Troubleshooting

## ERR_MODULE_NOT_FOUND

Exemplo:

```text
Cannot find module:
politica-de-cookies-XXXX.mjs
router-XXXX.mjs
```

Causa:

* build incompleto
* mistura de artefatos antigos
* deploy parcial

Correção:

```bash
rm -rf .output
rm -rf .nuxt

npm run build:ec2

pm2 restart investplus
```

---

## Script not found

```text
Script not found:
.output/server/index.mjs
```

O build falhou.

Verifique:

```bash
npm run build:ec2
cat .output/nitro.json
```

---

## 502 Bad Gateway

```bash
pm2 logs investplus
pm2 status
curl http://127.0.0.1:3000
```

Se o curl falhar, o problema está no Node e não no Apache.

---

## Cannot find native binding

Exemplo:

```text
@rolldown/binding-linux-arm64-gnu
```

Problema conhecido do npm em ARM64.

Correção:

```bash
rm -rf node_modules package-lock.json
npm install
```

---

## Refresh gera 404

O Apache está servindo arquivos estáticos.

Este projeto é SSR.

O Apache deve fazer proxy para:

```text
http://127.0.0.1:3000
```

e nunca apontar para:

```text
.output/public
```

---

# Build Cloudflare (opcional)

Caso seja necessário gerar novamente para Cloudflare/Lovable:

```bash
npm run build:cloudflare
```

Esse build não deve ser utilizado na EC2.

---

# Convivência com o Lovable (branch única `main`)

O repositório usa **uma única branch** (`main`): o Lovable sincroniza nela e o CI/CD do EC2 dispara dela. Regras para que os pushes externos nunca quebrem o build nem o preview do Lovable:

1. **Nunca commitar artefatos de build.** `dist/`, `.output/`, `.nitro/`, `.nuxt/`, `.wrangler/`, `tsconfig.tsbuildinfo` e `src/tailwind.config.lov.json` estão no `.gitignore`. O build do EC2 gera tudo no servidor.
2. **O deploy é read-only no Git.** O `deploy.sh` só faz `git fetch` / `git checkout` / `git reset --hard` / `git clean`. Ele **nunca** faz `git add`, `git commit` ou `git push`.
3. **Dependências só são alteradas pelo Lovable.** O CI roda `npm ci` (não `npm install`) e não deve commitar `package-lock.json` alterado. Assim `bun.lock` (usado pelo Lovable) e `package-lock.json` (usado no EC2) não divergem.
4. **Não alterar os scripts do `package.json` pelo CI.** O script `build` copia `.output/public` para `dist/` para satisfazer a validação da plataforma Lovable; remover esse `cp` quebra o preview.
5. **Preview "out of date" após push externo é normal.** Todo commit que não vem do Lovable marca o preview como desatualizado até o próximo build. Para visualizar as atualizações imediatamente, faça qualquer ajuste pelo Lovable (ou peça um rebuild) — o preview recompila e volta a servir todas as rotas.
6. **O workflow não roda em push.** `.github/workflows/deploy.yml` usa apenas `workflow_dispatch`, então commits do Lovable não disparam deploy acidental para a EC2.

## Validação do build (executada)

```bash
npm run build       # build Lovable/Cloudflare — OK
npm run build:dev   # build de preview — OK
npm run build:ec2   # build Node/EC2 — OK
```

Servidor Node de produção testado localmente: `/`, `/ofertas`, `/fcj-group`, `/vertice-blue`, `/vox-epower` respondem **200**.
