# Runbook de Operação — Produção (Traefik + Convex Self‑Hosted) — Arquivo

Nota: este documento foi substituído por `docs/operations.md` e permanece aqui como histórico.

> Documento vivo. Guia completo para (1) preparar a VPS, (2) fazer deploy com Traefik/Swarm, (3) publicar o backend Convex self‑hosted, (4) popular seeds e (5) operar/atualizar com ou sem CI/CD. Tudo em PT‑BR.

## Visão geral
- Frontend (Next.js) público em `tickets.esdrasrenan.com.br`.
- Backend Convex self‑hosted em `convex.esdrasrenan.com.br` (imagem oficial Convex).
- Traefik no Docker Swarm (rede `traefik_public`) roteando por hostname (sem conflito de portas).
- Banco Prisma (SQLite) persistente via volume `sistema_db` (mapeado em `/app/data`).
- Estado do Convex persistente via volume `convex_data`.
- Seeds prontos (Better Auth e dados demo Convex).
- Seeds Better Auth automáticos: o container do web executa `bun run auth:seed` após `prisma migrate deploy`, garantindo usuários padrão em toda inicialização (sem resetar senha existente por padrão).

### Sessão de dispositivo (Desktop/Tauri)
- A rota `GET /machines/handshake?token=...&redirect=/portal|/dashboard` é pública no middleware para permitir a criação da sessão "machine" a partir do agente desktop, mesmo sem login prévio.
- Após o handshake, o servidor grava cookies de sessão (Better Auth) e um cookie `machine_ctx` com o vínculo (persona, assignedUser*, etc.). Em seguida, o App carrega `/api/machines/session` para preencher o contexto no cliente (`machineContext`).
- Com esse contexto, o Portal exibe corretamente o nome/e‑mail do colaborador/gestor no cabeçalho e permite abrir chamados em nome do usuário vinculado.
- Em sessões de dispositivo, o botão "Encerrar sessão" no menu do usuário é ocultado por padrão na UI interna.

#### Detalhes importantes (aprendidos em produção)
- CORS com credenciais: as rotas `POST /api/machines/sessions` e `GET /machines/handshake` precisam enviar `Access-Control-Allow-Credentials: true` para que os cookies do Better Auth sejam aceitos na WebView.
- Vários `Set-Cookie`: alguns navegadores/ambientes colapsam cabeçalhos. Para confiabilidade, usamos `NextResponse.cookies.set(...)` para cada cookie, em vez de repassar o cabeçalho bruto.
- Top-level navigation: mesmo tentando criar a sessão via `POST /api/machines/sessions`, mantemos a navegação final pelo `GET /machines/handshake` (primeira parte) para maximizar a aceitação de cookies no WebView.
- Front tolerante: o portal preenche `machineContext` mesmo quando `/api/auth/get-session` retorna `null` na WebView e deriva a role "machine" do contexto — assim o colaborador consegue abrir tickets normalmente.
- Página de diagnóstico: `GET /portal/debug` exibe o status/JSON de `/api/auth/get-session` e `/api/machines/session` com os mesmos cookies da aba.

#### Troubleshooting rápido
1. Abra o app desktop e deixe ele redirecionar para `/portal/debug`.
2. Se `machines/session` for 200 e `get-session` for `null`, está OK — o portal usa `machineContext` assim mesmo.
3. Se `machines/session` for 401/403:
   - Verifique CORS/credenciais (`Access-Control-Allow-Credentials: true`).
   - Garante que estamos usando `cookies.set` para aplicar todos os cookies da Better Auth.
   - Refaça o handshake (feche reabra o desktop). Opcional: renomeie `EBWebView` para limpar cookies no Windows.
4. Se o app desktop exibir `Falha no registro (500)` com `attempt to write a readonly database`, o volume `sistema_sistema_db` ficou com permissões incorretas (SQLite não consegue gravar). Rode `docker run --rm -v sistema_sistema_db:/data alpine:3 sh -lc 'chown -R 1000:1000 /data && chmod -R ug+rwX /data'` ou simplesmente dispare um deploy (o workflow CI agora corrige as permissões antes de publicar).

## Requisitos
- VPS com Docker/Swarm e Traefik já em execução na rede externa `traefik_public`.
- Portainer opcional (para gerenciar a stack “sistema”).
- Domínios apontando para a VPS:
  - `tickets.esdrasrenan.com.br` (frontend)
  - `convex.esdrasrenan.com.br` (Convex backend)

## Layout do servidor
- Código do projeto: `/srv/apps/sistema` (bind no stack).
- Volumes Swarm:
  - `sistema_db` → `/app/data` (SQLite / Prisma)
  - `convex_data` → `/convex/data` (Convex backend)

## .env (produção)
Arquivo base: `.env` na raiz do projeto. Exemplo mínimo (substitua domínios/segredos):

```
NEXT_PUBLIC_APP_URL=https://tickets.esdrasrenan.com.br
BETTER_AUTH_URL=https://tickets.esdrasrenan.com.br
NEXT_PUBLIC_CONVEX_URL=https://convex.esdrasrenan.com.br
CONVEX_INTERNAL_URL=http://convex_backend:3210
BETTER_AUTH_SECRET=<hex forte gerado por `openssl rand -hex 32`>
DATABASE_URL=file:./prisma/db.sqlite

# Seeds automáticos (Better Auth)
# Garante usuários padrão sem resetar senhas existentes
SEED_ENSURE_ONLY=true

# SMTP
SMTP_ADDRESS=<smtp.host>
SMTP_PORT=465
SMTP_DOMAIN=<seu-dominio>
SMTP_USERNAME=<usuario>
SMTP_PASSWORD=<senha>
SMTP_AUTHENTICATION=login
SMTP_ENABLE_STARTTLS_AUTO=false
SMTP_TLS=true
MAILER_SENDER_EMAIL="Nome <no-reply@seu-dominio.com>"

# Dispositivo/inventário
MACHINE_PROVISIONING_SECRET=<hex forte>
MACHINE_TOKEN_TTL_MS=2592000000
FLEET_SYNC_SECRET=<hex forte ou igual ao de provisionamento>

# Conexões internas (Next.js -> Convex)
# CONVEX_INTERNAL_URL deve apontar para o hostname/porta do serviço no Swarm.

# Self-heal do Raven (opcional)
REMOTE_ACCESS_TOKEN_GRACE_MS=900000 # 15 minutos para aceitar tokens recém-revogados

# Outros
CONVEX_SYNC_SECRET=dev-sync-secret
ALERTS_LOCAL_HOUR=8
SYNC_TENANT_ID=tenant-atlas
SEED_TENANT_ID=tenant-atlas

# Importante para self-hosted: comentar se existir
# CONVEX_DEPLOYMENT=...
```

Atenção
- `MAILER_SENDER_EMAIL` precisa de aspas se contiver espaços.
- Em self‑hosted, NÃO usar `CONVEX_DEPLOYMENT`.
- O Raven armazena o código de provisionamento localmente. Se o token for revogado (reset/reinstalação), ele reprovisiona sozinho e reenfileira o acesso remoto do RustDesk — mantenha `REMOTE_ACCESS_TOKEN_GRACE_MS` alinhado ao seu SLA.

## Stack (Docker Swarm + Traefik)
O arquivo do stack está versionado em `stack.yml`. Ele sobe:
- `web` (Next.js) — builda e roda na porta interna 3000 (roteada pelo Traefik por hostname).
- `convex_backend` — imagem oficial do Convex self‑hosted (porta interna 3210, roteada pelo Traefik).

Bind dos volumes (absolutos, compatível com Portainer/Swarm):
- `/srv/apps/sistema:/app` → código do projeto dentro do container web.
- volume `sistema_db` → `/app/data` (SQLite do Prisma).
- volume `convex_data` → `/convex/data` (estado do Convex backend).

Rótulos Traefik (labels) no stack mapeiam:
- `tickets.esdrasrenan.com.br` → serviço `web` porta 3000.
- `convex.esdrasrenan.com.br` → serviço `convex_backend` porta 3210.

## Deploy da stack
Via Portainer (recomendado)
1. Abra o Portainer → Stacks → Add/Update e cole o conteúdo de `stack.yml` (ou vincule ao repositório para “Pull/Deploy”).
2. Clique em “Deploy the stack” (ou “Update the stack”).

Via CLI
```
docker stack deploy --with-registry-auth -c /srv/apps/sistema/stack.yml sistema
```

Verificação
- Serviços: `docker stack services sistema`
- Logs:
  - `docker service logs -f sistema_web`
  - `docker service logs -f sistema_convex_backend`

Acesso
- App: `https://tickets.esdrasrenan.com.br`
- Convex: `https://convex.esdrasrenan.com.br` (o importante é o WebSocket do cliente conectar; o path `/version` responde para sanity‑check)

## Zero‑downtime (sem queda durante deploy)

Para evitar interrupção perceptível no deploy, habilitamos rollout "start-first". Para este projeto, mantemos **1 réplica** (web e Convex) por segurança, pois:
- O web usa SQLite (Prisma); múltiplas réplicas concorrendo gravação no mesmo arquivo podem causar erros de lock/readonly.
- O Convex backend self‑hosted não é clusterizado.

O `stack.yml` já inclui:
- `replicas: 1` + `update_config.order: start-first` (Swarm sobe a nova task saudável antes de desligar a antiga).
- `failure_action: rollback`.
- `healthcheck` por porta local, garantindo que o Swarm só troque quando a nova task estiver OK.

Se quiser ajustar recursos/estratégia:
```
deploy:
  replicas: 2
  update_config:
    parallelism: 1
    order: start-first
    failure_action: rollback
  restart_policy:
    condition: any
healthcheck:
  # web
  test: ["CMD", "node", "-e", "fetch('http://localhost:3000').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"]
  interval: 10s
  timeout: 3s
  retries: 5
  start_period: 30s
```

Observação: o CI já força `docker service update --force` após `stack deploy` e passa `RELEASE_SHA` no ambiente para variar a spec em todo commit, assegurando rollout.

## App Desktop (Tauri)
- Build local por SO:
  - Linux: `bun run --cwd apps/desktop tauri build`
  - Windows/macOS: executar o mesmo comando no respectivo sistema (o Tauri gera .msi/.dmg/.app).
- Por padrão, o executável em modo release usa `https://tickets.esdrasrenan.com.br` como `APP_URL` e `API_BASE_URL`.
- Para customizar, crie `apps/desktop/.env` com `VITE_APP_URL` e `VITE_API_BASE_URL`.
- Saída dos pacotes: `apps/desktop/src-tauri/target/release/bundle/`.

### Alertas de postura (opcional)
- Variáveis de ambiente para geração automática de tickets em alertas de postura (CPU alta, serviço parado, SMART em falha):
  - `MACHINE_ALERTS_CREATE_TICKETS=true|false` (padrão: true)
  - `MACHINE_ALERTS_TICKET_REQUESTER_EMAIL=admin@sistema.dev` (usuário solicitante dos tickets automáticos)

### CI de Release do Desktop
- Workflow: `.github/workflows/desktop-release.yml` (build Linux/Windows/macOS).
- Preencha os Secrets no repositório (Settings > Secrets > Actions):
  - `TAURI_PRIVATE_KEY`
  - `TAURI_KEY_PASSWORD`
- Disparo: tag `desktop-v*` ou via `workflow_dispatch`.
  

### Dashboard (opcional)
Você pode expor o painel do Convex para inspeção em produção.

DNS
- Criar `convex-admin.esdrasrenan.com.br` apontando para a VPS.

Stack
- O serviço `convex_dashboard` já está definido em `stack.yml` com Traefik. Após atualizar a stack:
  - Acesse `https://convex-admin.esdrasrenan.com.br`.
  - Use a Admin Key gerada por `./generate_admin_key.sh` para autenticar.

## Convex self‑hosted — configuração inicial
1. Gerar Admin Key (uma vez, dentro do container do Convex):
```
# Console/exec no container sistema_convex_backend
./generate_admin_key.sh
# Copiar/guardar a chave: convex-self-hosted|...
```
2. Publicar o código Convex (deploy das functions) — sem instalar nada na VPS:
```
docker run --rm -it \
  -v /srv/apps/sistema:/app \
  -w /app \
  -e CONVEX_SELF_HOSTED_URL=https://convex.esdrasrenan.com.br \
  -e CONVEX_SELF_HOSTED_ADMIN_KEY='COLE_A_CHAVE_AQUI' \
  oven/bun:1 bash -lc "bun install --frozen-lockfile && bun x convex deploy"
```

Observação
- Sempre que alterar código em `convex/`, repita o comando acima para publicar as mudanças.

### Variáveis do Convex (importante)
As functions do Convex leem variáveis via `convex env`, não do `.env` do container.
No CI, defina os seguintes Secrets (Repo → Settings → Secrets and variables → Actions):

- `CONVEX_SELF_HOSTED_URL` — ex.: `https://convex.esdrasrenan.com.br`
- `CONVEX_SELF_HOSTED_ADMIN_KEY` — gerada por `./generate_admin_key.sh`
- `MACHINE_PROVISIONING_SECRET` — hex forte
- (opcional) `MACHINE_TOKEN_TTL_MS` — ex.: `2592000000`
- (opcional) `FLEET_SYNC_SECRET`

O job `convex_deploy` sempre roda `convex env set` com os Secrets acima antes do `convex deploy`.
Se preferir setar manualmente:

- `MACHINE_PROVISIONING_SECRET` — obrigatório para `/api/machines/register`
- (opcional) `MACHINE_TOKEN_TTL_MS`, `FLEET_SYNC_SECRET`

CLI manual (exemplo):
```
docker run --rm -it \
  -v /srv/apps/sistema:/app -w /app \
  -e CONVEX_SELF_HOSTED_URL=https://convex.esdrasrenan.com.br \
  -e CONVEX_SELF_HOSTED_ADMIN_KEY='COLE_A_CHAVE' \
  oven/bun:1 bash -lc "set -euo pipefail; bun install --frozen-lockfile; \
    unset CONVEX_DEPLOYMENT; \
    bun x convex env set MACHINE_PROVISIONING_SECRET 'seu-hex' -y; \
    bun x convex env list"
```

### Smoke test pós‑deploy (CI)
O pipeline executa um teste rápido após o deploy do Web:
- Registra uma dispositivo fake usando `MACHINE_PROVISIONING_SECRET` do `/srv/apps/sistema/.env`
- Espera `HTTP 201` e extrai `machineToken`
- Envia `heartbeat` e espera `HTTP 200`
- Se falhar, o job é marcado como erro (evita regressões silenciosas)

## Seeds
- Dados de demonstração Convex: acesse uma vez `https://tickets.esdrasrenan.com.br/dev/seed`.
- Usuários (Better Auth):
```
CONTAINER=$(docker ps --format '{{.ID}} {{.Names}}' | grep sistema_web | awk '{print $1}' | head -n1)
docker exec -it "$CONTAINER" bash -lc 'cd /app && bun run auth:seed'
```
- Apenas um admin (em produção):
```
CONTAINER=$(docker ps --format '{{.ID}} {{.Names}}' | grep sistema_web | awk '{print $1}' | head -n1)
docker exec -it "$CONTAINER" bash -lc 'cd /app && \
  SEED_USER_EMAIL="seu-email@dominio.com" \
  SEED_USER_PASSWORD="suaSenhaForte" \
  SEED_USER_NAME="Seu Nome" \
  SEED_USER_ROLE="admin" \
  bun run auth:seed'
```
- Filas padrão: `docker exec -it "$CONTAINER" bash -lc 'cd /app && bun run queues:ensure'`

## Atualizações (sem CI)
- App (Next.js):
```
cd /srv/apps/sistema
git pull
docker stack deploy --with-registry-auth -c stack.yml sistema
```
- Convex (functions): repetir o container `oven/bun:1` com `bun x convex deploy` (ver seção Convex).
- Reiniciar serviços sem alterar o stack: `docker service update --force sistema_web` (ou `sistema_convex_backend`).

## CI/CD (GitHub Actions + runner self‑hosted)
1. Registrar runner na VPS:
   - Repo → Settings → Actions → Runners → New self‑hosted → Linux
   - Labels: `self-hosted, linux, vps`
2. Ajustar job `deploy` em `.github/workflows/ci-cd-web-desktop.yml` para:
   - `cd /srv/apps/sistema && git pull`
   - `docker stack deploy --with-registry-auth -c stack.yml sistema`
3. Adicionar job `convex_deploy` (opcional) no mesmo runner:
   - Executar container `oven/bun:1` com envs `CONVEX_SELF_HOSTED_URL` e `CONVEX_SELF_HOSTED_ADMIN_KEY` (secrets do GitHub)
   - Rodar `bun x convex deploy`

Secrets necessários no GitHub (Repo → Settings → Secrets and variables → Actions)
- `CONVEX_SELF_HOSTED_URL` = `https://convex.esdrasrenan.com.br`
- `CONVEX_SELF_HOSTED_ADMIN_KEY` = chave retornada por `./generate_admin_key.sh`
- (Desktop) `VPS_HOST`, `VPS_USER`, `VPS_SSH_KEY`, `TAURI_PRIVATE_KEY`, `TAURI_KEY_PASSWORD` — se usar o job de release desktop

Benefícios
- Push na `main` → pipeline atualiza app e (opcionalmente) publica mudanças no Convex.

## Trocar domínio ou VPS (checklist)
1. Criar DNS para novos domínios (app/convex) apontando para a VPS nova.
2. Copiar o projeto para `/srv/apps/sistema` na nova VPS.
3. Ajustar `.env` com novos domínios e SEGREDOS novos (gire novamente em produção).
4. Deploy da stack.
5. Convex: gerar Admin Key no novo container e `convex deploy` apontando para a nova URL.
6. Testar front + WS (sem erro 1006) e seeds conforme necessário.

## Problemas comuns e correções
- WebSocket 1006 no front:
  - Convex não está recebendo/concluindo handshake WS → ver logs `sistema_convex_backend`.
  - DNS/Traefik incorretos → confirmar labels/hostnames e DNS.
- `MAILER_SENDER_EMAIL` com erro de parsing:
  - Adicionar aspas no `.env`.
- Lockfile desatualizado:
  - Rode `bun install --frozen-lockfile` sempre que ajustar dependências para manter o `bun.lock` consistente em produção.
- Portainer erro de bind relativo:
  - Usar caminho absoluto `/srv/apps/sistema:/app` no stack (feito).
- Prisma CLI “not found”:
  - Execute `bun install` no container de build garantindo a instalação das devDependencies (Prisma CLI fica disponível via `bun x prisma ...`).
- Convex CLI pedindo interação:
  - Não usar CLI em produção; usamos imagem oficial `convex-backend` e `convex deploy` via container transitório com Admin Key.

## Comandos úteis
- Serviços: `docker stack services sistema`
- Logs: `docker service logs -f sistema_web` / `docker service logs -f sistema_convex_backend`
- Reiniciar: `docker service update --force <service>`
- Status Traefik (se exposto): `docker service logs -f traefik`

## Segurança
- Nunca commit `.env` com segredos reais.
- Guarde a `Admin Key` do Convex em local seguro; use secrets no GitHub.
- Gire segredos ao migrar de VPS/domínio.

## Referências
- Stack do projeto: `stack.yml`
- CI/CD (web + desktop): `.github/workflows/ci-cd-web-desktop.yml`
- Guia CI/CD Desktop: `apps/desktop/docs/guia-ci-cd-web-desktop.md`
- Docs Convex self‑hosted: imagem oficial `ghcr.io/get-convex/convex-backend`

## Bundlers (Next.js)
- Em desenvolvimento utilizamos Turbopack (`next dev --turbopack`) pela velocidade incremental e mantemos fallback webpack.
- Builds de produção rodam com `next build --turbopack`; use `next build --webpack` apenas se precisar depurar diferenças.
- Scripts principais (package.json):
  - `dev`: `next dev --turbopack`
  - `build`: `next build --turbopack`
  - `build:webpack`: `next build --webpack` (fallback)
- O workflow de CI executa `bun run build:bun` (roda `next build --turbopack` via Bun) e a stack continua a usar `bun run start:bun` sobre o artefato gerado.
