# Deploy do TechDesk Pro em Windows Server (Apache/XAMPP + Node)

Guia passo a passo para publicar o TechDesk Pro num Windows Server que **já hospeda o
PostgreSQL usado pelo sistema** (banco local, sem precisar de VPN) e que **já roda um
outro sistema em produção atrás do Apache do XAMPP**. Por isso o proxy reverso aqui é
feito pelo Apache (via VirtualHost novo, nomeado, sem tocar no que já existe) — não pelo
IIS. A arquitetura:

```
Internet ──HTTP/HTTPS──▶ Apache/XAMPP (porta 80/443, VirtualHost novo por ServerName)
                             │  (site de produção existente continua intocado,
                             │   em outro VirtualHost)
                             ▼
                      Node/Express (porta 3001, serviço Windows via NSSM)
                             │  serve a API (/api/*), o WebSocket (/socket.io) e
                             │  os arquivos estáticos do frontend já buildado
                             ▼
                      PostgreSQL (localhost:5432, no mesmo servidor)
```

O backend já sabe servir o frontend buildado quando `NODE_ENV=production` — por isso o
Apache não precisa de regras diferentes por caminho, só encaminha **tudo** (menos o
outro VirtualHost já existente) para o Node.

## 0. Pré-requisitos a confirmar/instalar no servidor

- **Node.js LTS** — já confirmado instalado (`node -v`)
- **Apache do XAMPP já instalado e em uso** (confirmado) — precisa ter estes módulos
  habilitados em `C:\xampp\apache\conf\httpd.conf` (linha sem `#` na frente):
  ```
  LoadModule proxy_module modules/mod_proxy.so
  LoadModule proxy_http_module modules/mod_proxy_http.so
  LoadModule proxy_wstunnel_module modules/mod_proxy_wstunnel.so
  LoadModule rewrite_module modules/mod_rewrite.so
  LoadModule ssl_module modules/mod_ssl.so
  ```
  E o Apache precisa estar carregando `httpd-vhosts.conf` — confirme em `httpd.conf` que
  esta linha existe e está sem `#`:
  ```
  Include conf/extra/httpd-vhosts.conf
  ```
- **NSSM** (para rodar o Node como serviço) — https://nssm.cc/download
- **Git** — para clonar/atualizar o repositório (`git --version` para confirmar)

## 1. Confirmar que a porta 3001 está livre

Como este servidor já roda outros serviços, confira antes de seguir:
```powershell
netstat -ano | findstr :3001
```
Sem nenhuma linha na saída = porta livre, pode seguir. Se aparecer algo, me avise antes
de continuar — trocamos a porta em todos os arquivos (é só um número em poucos lugares).

## 2. Copiar o projeto para o servidor

```powershell
mkdir C:\xampp\htdocs\techdesk.ravapi.com -Force
cd C:\xampp\htdocs\techdesk.ravapi.com
git clone https://github.com/rvpinho/techdesk.git techdesk-pro
```

## 3. Configurar e instalar o backend

```powershell
cd C:\xampp\htdocs\techdesk.ravapi.com\techdesk-pro\backend
npm install --omit=dev
copy .env.production.example .env
notepad .env
```

No `.env`, confira/ajuste principalmente:
- `NEON_HOST=127.0.0.1` e `NEON_PASS` com a senha real do Postgres local
- `JWT_SECRET` — gere um valor novo, não reaproveite o de desenvolvimento:
  ```powershell
  [Convert]::ToBase64String((1..64 | ForEach-Object { Get-Random -Maximum 256 }))
  ```
- `FRONTEND_URL` e `QRCODE_BASE_URL` com o domínio público real (ex: `https://techdesk.ravapi.com`)

Se o banco de dados ainda não tiver o schema (banco novo, não o que já está em uso):

```powershell
psql -U postgres -h 127.0.0.1 -d techdesk -f ..\backend\database\schema.sql
psql -U postgres -h 127.0.0.1 -d techdesk -f ..\backend\database\seed.sql
```

Se o banco já é o mesmo usado em desenvolvimento (caso do RAVAPI), **pule este passo** —
o schema já está aplicado e os dados já existem.

## 4. Buildar o frontend

```powershell
cd C:\xampp\htdocs\techdesk.ravapi.com\techdesk-pro\frontend
npm install
copy .env.production.example .env.production
npm run build
```

Isso gera `frontend\dist\`, que o backend serve automaticamente em produção. Confirme
que `.env.production` tem `VITE_API_URL=` (vazio) — é assim que o navegador chama a
própria origem (mesmo domínio) em vez de `localhost:3001`.

## 5. Instalar o backend como Serviço do Windows

```powershell
cd C:\xampp\htdocs\techdesk.ravapi.com\techdesk-pro\deploy
.\install-service.ps1 -AppDir "C:\xampp\htdocs\techdesk.ravapi.com\techdesk-pro\backend" -NssmPath "C:\tools\nssm.exe"
```

Ajuste `-NssmPath` para onde você baixou o `nssm.exe` (ou adicione essa pasta ao PATH do
sistema para poder chamar só `nssm` nos comandos dos passos seguintes). O script:
- Registra o serviço `TechDeskProBackend` (Node rodando `server.js`)
- Configura início automático e reinício sozinho em caso de falha
- Grava logs em `backend\logs\stdout.log` / `stderr.log`

Confirme que subiu:
```powershell
curl http://localhost:3001/api/health
```

## 6. Adicionar o VirtualHost no Apache (porta 80)

1. Abra `C:\xampp\apache\conf\extra\httpd-vhosts.conf`
2. Cole o conteúdo de `deploy\techdesk-apache-vhost.conf` **no final do arquivo**,
   abaixo de tudo que já existe — não mexe nos VirtualHosts atuais
3. Ajuste `ServerName` no bloco colado se o domínio final não for
   `techdesk.ravapi.com`
4. Reinicie o Apache — se ele estiver instalado como Serviço do Windows (confirme com
   `Get-Service | Where-Object {$_.Name -like "*apache*"}`), o mais limpo é
   `Restart-Service Apache2.4` (ajuste o nome do serviço se for diferente); senão, use o
   **XAMPP Control Panel** (Stop, depois Start)
5. Confirme que o site de produção existente continua respondendo normalmente antes de
   prosseguir

**Antes de reiniciar, sempre rode `C:\xampp\apache\bin\httpd.exe -t`** — se vier
`Syntax OK`, é seguro reiniciar sem risco de derrubar o Apache (e o outro site junto).
Se o Apache não subir mesmo assim, o log de erro (`C:\xampp\apache\logs\error.log`, ou o
`ErrorLog` específico do VirtualHost) aponta a causa.

### Pegadinha comum: módulos "padrão" vêm desabilitados

Mesmo que `mod_proxy.so` e outros existam em `C:\xampp\apache\modules\`, é comum o
XAMPP vir com `proxy_http_module` e/ou `proxy_wstunnel_module` **comentados**
(`#LoadModule ...`) em `httpd.conf`, mesmo com `proxy_module` já habilitado. Sintoma:
proxy retorna **500 Internal Server Error**, e o log mostra `AH01144: No protocol
handler was valid for the URL (scheme 'http')`. Sempre confirme os quatro módulos
individualmente (não assuma que "proxy" habilitado implica os outros também estarem):

```powershell
Select-String -Path "C:\xampp\apache\conf\httpd.conf" -Pattern "LoadModule.*(proxy|rewrite|ssl)"
```

Para habilitar um módulo comentado (faça backup do `httpd.conf` antes de editar arquivos
que também servem outros sites):
```powershell
copy "C:\xampp\apache\conf\httpd.conf" "C:\xampp\apache\conf\httpd.conf.bak-techdesk"
(Get-Content "C:\xampp\apache\conf\httpd.conf") -replace '^#(LoadModule NOME_DO_MODULO)', '$1' | Set-Content "C:\xampp\apache\conf\httpd.conf"
```

## 7. HTTPS (porta 443)

Sem um `VirtualHost *:443` dedicado, requisições HTTPS caem no site **padrão** do
Apache (o "Index of /" listando `C:\xampp\htdocs\`) em vez do TechDesk — o `:80` sozinho
não é suficiente quando o Cloudflare (ou qualquer proxy na frente) fala HTTPS com a
origem.

**Verifique o modo de criptografia no Cloudflare primeiro** (SSL/TLS > Visão geral, do
domínio): isso define se a origem precisa de um certificado válido/confiável.
- **Flexível**: Cloudflare fala HTTP puro com a origem — o `:80` já basta, pule esta seção.
- **Completo (Full)**: Cloudflare aceita **qualquer certificado na origem, incluindo
  autoassinado**, sem validar hostname/CA. É o caso mais comum em conta gratuita/free
  do Cloudflare. **Não precisa emitir certificado nenhum** — reaproveite o autoassinado
  que já vem com o XAMPP:
  ```
  C:\xampp\apache\conf\ssl.crt\server.crt
  C:\xampp\apache\conf\ssl.key\server.key
  ```
- **Completo (estrito) / Full (strict)**: a origem precisa de um certificado válido de
  verdade (emitido por uma CA confiável, ou um "Origin Certificate" gerado pelo próprio
  Cloudflare em SSL/TLS > Certificados de origem). Nesse caso use o **win-acme**
  (https://www.win-acme.com/) para emitir via Let's Encrypt, ou peça um Origin
  Certificate ao Cloudflare e aponte os caminhos abaixo pra ele.

Adicione o bloco `:443` no mesmo `httpd-vhosts.conf` (pode copiar de
`deploy\techdesk-apache-vhost.conf`, que já traz um modelo comentado no final):

```apache
<VirtualHost *:443>
    ServerName techdesk.ravapi.com
    SSLEngine on
    SSLCertificateFile    "C:/xampp/apache/conf/ssl.crt/server.crt"
    SSLCertificateKeyFile "C:/xampp/apache/conf/ssl.key/server.key"

    ProxyPass        /socket.io/ ws://127.0.0.1:3001/socket.io/
    ProxyPassReverse /socket.io/ ws://127.0.0.1:3001/socket.io/

    ProxyPreserveHost On
    ProxyPass        / http://127.0.0.1:3001/
    ProxyPassReverse / http://127.0.0.1:3001/

    LimitRequestBody 52428800
    ErrorLog  "logs/techdesk-ssl-error.log"
    CustomLog "logs/techdesk-ssl-access.log" combined
</VirtualHost>
```

### Pegadinha importante: todo domínio HTTPS na mesma origem precisa do próprio `:443`

Se **outro** site (ex: `crm.ravapi.com`) roda nesse mesmo Apache só com `:80` e não tem
`VirtualHost :443` próprio, ele vai parar de funcionar em HTTPS assim que você adicionar
o `:443` do TechDesk — o Apache passa a tratar o único `VirtualHost :443` nomeado que
existir como padrão para qualquer SNI que não bata exatamente, "roubando" o tráfego do
outro domínio. **Sintoma:** o outro site passa a redirecionar/mostrar o conteúdo do
TechDesk. **Solução:** dar a esse outro site um `VirtualHost :443` explícito também,
mesmo reaproveitando o mesmo certificado autoassinado (Cloudflare em modo Full aceita
qualquer um):

```apache
<VirtualHost *:443>
    ServerName crm.ravapi.com
    SSLEngine on
    SSLCertificateFile    "C:/xampp/apache/conf/ssl.crt/server.crt"
    SSLCertificateKeyFile "C:/xampp/apache/conf/ssl.key/server.key"
    ProxyPreserveHost On
    ProxyPass / http://127.0.0.1:3000/
    ProxyPassReverse / http://127.0.0.1:3000/
</VirtualHost>
```
(ajuste `ServerName`/porta pro site real). Depois de colar qualquer bloco novo, sempre
`httpd.exe -t` antes de reiniciar, e teste **todos** os domínios HTTPS existentes nesse
Apache, não só o que você acabou de adicionar.

## 8. Verificação final

- `https://techdesk.ravapi.com/api/health` → deve responder `{"status":"ok",...}`
- Acesse a raiz do domínio, de **fora** da rede local (dado o Cloudflare na frente, o
  teste de dentro da rede via `hosts` file pode não refletir o comportamento público) →
  deve carregar a tela de login do TechDesk Pro
- Faça login e abra o **Chat** ou observe o sino de notificações — se atualizar em
  tempo real, o proxy de WebSocket está funcionando também em `:443`. Se não, revise os
  módulos `mod_proxy_wstunnel` (passo 0) e a diretiva `ProxyPass /socket.io/ ws://...`
  em **ambos** os blocos (`:80` e `:443`)
- Confirme de novo que **todos** os outros sites em produção no mesmo Apache (HTTP e
  HTTPS) continuam funcionando normalmente

## Problemas comuns encontrados neste deploy (RAVAPI, ago/2026)

Guarde esta lista — se reinstalar do zero, provavelmente vai bater nos mesmos:

1. **`.env` salvo como `.env.txt` pelo Notepad** → dotenv não carrega nada, Postgres
   tenta autenticar com o usuário do Windows. Sempre confira com `dir .env*`.
2. **`NODE_ENV=development` esquecido no `.env` de produção** (copiado sem querer do
   ambiente local) → o backend nunca serve o `frontend/dist`, toda rota `/` dá
   `Cannot GET /`. Precisa ser `NODE_ENV=production`.
3. **Acentos/travessões em scripts `.ps1`** quebram o parser do PowerShell 5.1 (lê sem
   BOM na codepage do sistema, não UTF-8) → reescreva o script em ASCII puro.
4. **`Invoke-WebRequest`/`iwr` falha com erro de SSL/TLS** ao baixar algo (ex: NSSM) →
   rode `[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12`
   antes.
5. **`mod_proxy_http` e/ou `mod_proxy_wstunnel` comentados** mesmo com `mod_proxy`
   habilitado → confira os quatro módulos individualmente (seção 6).
6. **`:443` sem VirtualHost dedicado** cai no site padrão do Apache (Index of
   `htdocs`) → seção 7.
7. **Adicionar um `:443` novo "rouba" HTTPS de outro domínio sem `:443` próprio** →
   seção 7.

## Atualizando o sistema depois do primeiro deploy

```powershell
cd C:\xampp\htdocs\techdesk.ravapi.com\techdesk-pro
git pull

cd backend
npm install --omit=dev
nssm restart TechDeskProBackend

cd ..\frontend
npm install
npm run build
```

Não é necessário reiniciar o Apache — ele só faz proxy, o conteúdo novo já vem do Node
reiniciado.

## Rollback rápido

Se um deploy quebrar algo, o jeito mais rápido de voltar:
```powershell
cd C:\xampp\htdocs\techdesk.ravapi.com\techdesk-pro
git checkout <commit-anterior-que-funcionava>
cd backend && npm install --omit=dev && nssm restart TechDeskProBackend
cd ..\frontend && npm install && npm run build
```
