sistema-de-chamados/docs/OPERATIONS.md
2025-12-10 14:43:13 -03:00

522 lines
26 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Operações — Sistema de Chamados (Prod)
Este documento consolida as mudanças recentes, o racional por trás delas e o procedimento de operação/deploy (Web e Convex selfhosted) do ambiente em produção.
## 1) Mudanças Funcionais (Front + Server)
- Empresas (admin)
- “Slug” renomeado para “Apelido” (mensagens e placeholders ajustados).
- Fila padrão ao criar tickets
- Todo novo ticket entra na fila “Chamados”.
- Implementado no backend (fallback) e préseleção na UI.
- Status de tickets e interações
- Nomes/cores atualizados:
- Pendente (cinza), Em andamento (azul), Pausado (amarelo), Resolvido (verde).
- Transições automáticas:
- Iniciar (play) → status “Em andamento”.
- Pausar → status “Pausado”.
- “Encerrar” permanece manual. O dropdown foi substituído por badge + botão “Encerrar”.
- Diálogo de encerramento: botão “Cancelar” adicionado (além de “Limpar mensagem”/“Encerrar ticket”).
- Dashboard — Últimos chamados
- Prioridade: sem responsável primeiro (dos mais antigos para os mais recentes), depois demais chamados.
- Filtros de tickets
- Filtro por “Responsável” (agente/admin) adicionado.
- Salvar filtro como padrão por usuário (localStorage) + “Limpar padrão”.
- Empresas no filtro: lista completa via API admin (não só empresas presentes em tickets).
- Produção: filtro “Responsável” agora é feito no servidor (assigneeId); o front não envia mais parâmetros inválidos.
- Editor de comentários (Tiptap)
- Correção: reativar edição quando um responsável é atribuído (o editor agora reflete mudanças em `disabled` via `setEditable`).
## 2) Convex (SelfHosted) — Ajustes e Motivo
- Problema observado: deploy do Convex falhava no CI por:
- Ausência de `convex.json` (link do projeto) no servidor.
- Uso incorreto de `CONVEX_DEPLOYMENT` junto a `CONVEX_SELF_HOSTED_URL` + `CONVEX_SELF_HOSTED_ADMIN_KEY` (não suportado pelo CLI ao usar selfhosted).
- Divergência de schema (campo `provisioningCode` já existente nos dados, mas ausente no schema do Convex no servidor).
- Medidas aplicadas:
- Atualização do schema do Convex no servidor: inclusão de `provisioningCode?: string` na tabela `companies` (e índice opcional `by_provisioning_code`).
- Criação do link de projeto (`convex.json`) no servidor via wizard do CLI (ver Passo a passo abaixo).
- Ajustes no workflow do GitHub Actions para selfhosted:
- Adicionado passo “Acquire Convex admin key” no job de deploy do Convex.
- Removido `CONVEX_DEPLOYMENT` quando `CONVEX_SELF_HOSTED_URL` + `CONVEX_SELF_HOSTED_ADMIN_KEY` estão definidos.
- Cópia automática do `convex.json` de `/srv/apps/sistema` para o diretório de build temporário.
- Forçar redeploy das funções: tocar arquivos sob `convex/**` para acionar o filtro do job “Deploy Convex functions”.
## 3) CI/CD — Visão Geral
- Pipeline “CI/CD Web + Desktop” (GitHub Actions)
- Job “Detect changes” usa filtros por paths.
- Job “Deploy (VPS Linux)” cuida do Web (Next.js) e stack do Swarm.
- Job “Deploy Convex functions” roda quando há mudanças em `convex/**` ou via `workflow_dispatch`.
- Passos relevantes:
- “Acquire Convex admin key” (via container `sistema_convex_backend`).
- “Bring convex.json from live app if present” (usa o arquivo de link do projeto em `/srv/apps/sistema`).
- “convex env list” e “convex deploy” com `CONVEX_SELF_HOSTED_URL` + `CONVEX_SELF_HOSTED_ADMIN_KEY`.
## 4) Troca de colaborador / reaproveitamento de dispositivo
Quando um computador muda de dono (ex.: João entrega o equipamento antigo para Maria e recebe uma dispositivo nova), siga este checklist para manter o inventário consistente:
1. **No painel (Admin → Dispositivos)**
- Abra os detalhes da dispositivo que será reaproveitada (ex.: a “amarela” que passará da TI/João para a Maria).
- Clique em **Resetar agente**. Isso revoga todos os tokens gerados para aquele equipamento; ele precisará ser reprovisionado antes de voltar a reportar dados.
- Abra **Ajustar acesso** e altere o e-mail para o do novo usuário (Maria). Assim, quando o agente se registrar novamente, o painel já mostrará a responsável correta.
2. **Na dispositivo física que ficará com o novo colaborador**
- Desinstale o desktop agent (Painel de Controle → remover programas).
- Instale novamente o desktop agent. Use o mesmo **código da empresa/tenant** e informe o **e-mail do novo usuário** (Maria). O backend emite um token novo e reaproveita o registro da dispositivo, mantendo o histórico.
3. **Dispositivo nova para o colaborador antigo**
- Instale o desktop agent do zero na dispositivo que o João vai usar (ex.: a “azul”). Utilize o mesmo código da empresa e o e-mail do João.
- A dispositivo azul aparecerá como um **novo registro** no painel (inventário/tickets começarão do zero). Renomeie/associe conforme necessário.
4. **Verificação final**
- A dispositivo antiga (amarela) continua listada, agora vinculada à Maria, com seus tickets históricos.
- A dispositivo nova (azul) aparece como um segundo registro para o João. Ajuste hostname/descrição para facilitar a identificação.
> Não é necessário excluir registros. Cada dispositivo mantém seu histórico; o reset garante apenas que o token antigo não volte a sobrescrever dados quando o hardware mudar de mãos.
- Importante: não usar `CONVEX_DEPLOYMENT` em conjunto com URL + ADMIN_KEY.
- Como forçar o deploy do Convex
- Faça uma alteração mínima em `convex/**` (ex.: comentário em `convex/tickets.ts`) ou rode o workflow em “Run workflow” (workflow_dispatch).
## 4) Convex — Provisionamento inicial (selfhosted)
Executar apenas 1x na VPS para criar o link do projeto (`convex.json`):
```bash
cd /srv/apps/sistema
export CONVEX_SELF_HOSTED_URL="https://convex.esdrasrenan.com.br"
CID=$(docker ps --format '{{.ID}} {{.Names}}' | awk '/sistema_convex_backend/{print $1; exit}')
export CONVEX_SELF_HOSTED_ADMIN_KEY="$(docker exec -i "$CID" /bin/sh -lc './generate_admin_key.sh' | tr -d '\r' | grep -o 'convex-self-hosted|[^ ]*' | tail -n1)"
npx convex dev --once --configure=new
# Siga o wizard (self-hosted) e vincule/crie o projeto/deployment (ex.: "sistema" / "default").
# Isso gera /srv/apps/sistema/convex.json
```
Depois disso, o job “Deploy Convex functions” funciona em modo não interativo.
## 5) VPS — Acesso e Servicos
- Acesso
- Host: `154.12.253.40`
- Usuario: `root`
- Chave SSH (repo raiz): `./codex_ed25519` (Atencao: manter permissoes 600)
- Exemplo (Git Bash/Linux):
```bash
# Preparar chave (copiar e ajustar permissoes)
cp "./codex_ed25519" ~/.ssh/codex_ed25519
sed -i 's/\r$//' ~/.ssh/codex_ed25519
chmod 600 ~/.ssh/codex_ed25519
# Conectar
ssh -i ~/.ssh/codex_ed25519 root@154.12.253.40
```
- Opcional (endurecimento): desabilitar login por senha após validar a chave.
- Diretórios principais
- Código do app: `/srv/apps/sistema`
- Arquivo do projeto Convex: `/srv/apps/sistema/convex.json`
- Stack do Swarm: `stack.yml` (no repositório; aplicado no servidor via CI).
- Serviços (Docker Swarm + Traefik)
- Web (Next.js): serviço `sistema_web`, exposto em `tickets.esdrasrenan.com.br`.
- Convex backend: serviço `sistema_convex_backend`, exposto em `convex.esdrasrenan.com.br`.
- Convex dashboard: `convex-admin.esdrasrenan.com.br`.
- Comandos úteis:
- `docker service ls`
- `docker service ps sistema_web`
- `docker service update --force sistema_web` (reiniciar)
- `docker service update --force sistema_convex_backend` (reiniciar Convex)
- Convex admin key (diagnóstico)
- `docker exec -i $(docker ps | awk '/sistema_convex_backend/{print $1; exit}') /bin/sh -lc './generate_admin_key.sh'`
- Usada no CI para `convex env list` e `convex deploy`.
## 6) Notas de Segurança
- A chave privada `codex_ed25519` está na raiz do repo (ambiente atual). Em produção, recomendase:
- Remover a chave do repositório ou armazenála em Secrets/Deploy keys do provedor.
- Desabilitar login por senha no SSH (apenas chave).
- Manter permissões: `chmod 600 ./codex_ed25519`.
## 7) Testes, Build e Lint
- Local
- `bun run build:bun` (Next + typecheck)
- `bun run lint`
- `bun test`
- CI garante build, lint e testes antes do deploy.
## 8) Troubleshooting Rápido
- “No CONVEX_DEPLOYMENT set” durante o deploy do Convex
- Certifiquese de que `/srv/apps/sistema/convex.json` existe (rodar wizard `npx convex dev --once --configure=new`).
- Não usar `CONVEX_DEPLOYMENT` com `CONVEX_SELF_HOSTED_URL` + `CONVEX_SELF_HOSTED_ADMIN_KEY`.
- Login quebrando com erro de `better_sqlite3` (NODE_MODULE_VERSION)
- Sintoma: HTTP 500 em `/api/auth/sign-in/email` e logs do `sistema_web` com “Could not locate the bindings file” ou “compiled against NODE_MODULE_VERSION 115”.
- Causa: o `better-sqlite3` precisa ser recompilado para a versão de Node em uso (rodamos Node 22 dentro do container).
- Correção (uma vez, após trocar versão de Node/Bun ou limpar `node_modules`):
```
docker run --rm -v /apps/sistema:/app sistema_web:node22-bun \
bash -lc "cd /app && npm rebuild better-sqlite3"
docker service update --force sistema_web
```
Isso grava o binário em `/apps/sistema/node_modules/.bun/better-sqlite3@11.10.0/...` (path montado pelo serviço).
- Flags no serviço (stack.yml):
- `SKIP_APT_BOOTSTRAP=true` evita apt-get no boot (imagem já tem toolchain).
- `SKIP_SQLITE_REBUILD=true` deixa o boot rápido; deixe como `true` depois de recompilar manualmente como acima.
- “Schema validation failed” (campo extra `provisioningCode`)
- Atualize o schema do Convex no servidor para incluir `provisioningCode?: string` em `companies`.
- Refaça o deploy.
- Filtro “Responsável” não funciona
- Front envia `assigneeId` e o backend Convex deve aceitar esse parâmetro (função `tickets.list`).
- Se necessário, forçar redeploy das funções (`convex/**`).
- Admin ▸ Dispositivos travado em skeleton infinito
- Abra o DevTools (console) e filtre por `admin-machine-details`. Se o log mostrar `machineId: undefined`, o componente não recebeu o parâmetro da rota.
- Verifique se o `page.tsx` está passando `params.id` corretamente ou se o componente client-side usa `useParams()` / `machineId` opcional.
- Deploys antigos antes de `fix(machines): derive machine id from router params` precisam desse patch; sem ele o fallback nunca dispara e o skeleton permanece.
- Export do Convex em loop (`application::exports::worker`)
- Sintoma: WebSocket `1006`, logs com `Caught error ... shape_inference`, `Export <id> beginning...` repetindo.
- Solução completa documentada em `docs/convex-export-worker-loop.md` (backup do SQLite, limpeza dos registros defeituosos e rerun do export).
---
Última atualização: sincronizado após o deploy bemsucedido do Convex e do Front (20/10/2025).
## 9) Admin ▸ Usuários e Dispositivos — Unificação e UX
Resumo das mudanças aplicadas no painel administrativo para simplificar “Usuários” e “Agentes de dispositivo” e melhorar o filtro em Dispositivos:
- Unificação de “Usuários” e “Agentes de dispositivo”
- Antes: abas separadas “Usuários” (pessoas) e “Agentes de dispositivo”.
- Agora: uma só aba “Usuários” com filtro de tipo (Todos | Pessoas | Dispositivos).
- Onde: `src/components/admin/admin-users-manager.tsx:923`, aba `value="users"` em `:1147`.
- Motivo: evitar confusão entre “usuário” e “agente”; agentes são um tipo especial de usuário (role=machine). A unificação torna “Convites e Acessos” mais direta.
- Dispositivos ▸ Filtro por Empresa com busca e remoção do filtro de SO
- Adicionado dropdown de “Empresa” com busca (Popover + Input) e removido o filtro por Sistema Operacional.
- Onde: `src/components/admin/devices/admin-devices-overview.tsx:1038` e `:1084`.
- Motivo: fluxo real usa empresas com mais frequência; filtro por SO era menos útil agora.
- Windows ▸ Rótulo do sistema sem duplicidade do “major”
- Exemplo: “Windows 11 Pro (26100)” em vez de “Windows 11 Pro 11 (26100)”.
- Onde: `src/components/admin/devices/admin-devices-overview.tsx` (função `formatOsVersionDisplay`).
- Motivo: legibilidade e padronização em chips/cartões.
- Vínculos visuais entre dispositivos e pessoas
- Cards de dispositivos mostram “Usuário vinculado” quando disponível (assignment/metadata): `src/components/admin/devices/admin-devices-overview.tsx:3198`.
- Editor de usuário exibe “Dispositivos vinculadas” (derivado de assign/metadata): `src/components/admin/admin-users-manager.tsx` (seção “Dispositivos vinculadas” no sheet de edição).
- Observação: por ora é leitura; ajustes detalhados de vínculo permanecem em Admin ▸ Dispositivos.
### Identidade, email e histórico (reinstalação)
- Identificador imutável: o histórico (tickets, eventos) referencia o `userId` (imutável). O email é um atributo mutável.
- Reinstalação do desktop para o mesmo colaborador: reutilize a mesma conta de usuário (mesmo `userId`); se o email mudou, atualize o email dessa conta no painel. O histórico permanece, pois o `userId` não muda.
- Novo email como nova conta: se criar um usuário novo (novo `userId`), será considerado um colaborador distinto e não herdará o histórico.
- Caso precise migrar histórico entre contas diferentes (merge), recomendamos endpoint/rotina de “fusão de contas” (remapear `userId` antigo → novo). Não é necessário para a troca de email da mesma conta.
### Vínculos múltiplos de usuários por dispositivo (Fase 2)
- Estrutura (Convex):
- `machines.linkedUserIds: Id<"users">[]` — lista de vínculos adicionais além do `assignedUserId` (principal).
- Mutations: `machines.linkUser(machineId, email)`, `machines.unlinkUser(machineId, userId)`.
- APIs admin: `POST /api/admin/devices/links` (body: `{ machineId, email }`), `DELETE /api/admin/devices/links?machineId=..&userId=..`.
- UI:
- Detalhes da dispositivo mostram “Usuários vinculados” com remoção por item e campo para adicionar por email.
- Editor de usuário mostra “Dispositivos vinculadas” consolidando assignment, metadata e `linkedUserIds`.
- Racional: permitir que uma dispositivo tenha mais de um colaborador/gestor associado, mantendo um “principal” (persona) para políticas e contexto.
### Onde editar
- Usuários (pessoas): editar nome, email, papel, tenant e empresa; redefinir senha pelo painel. Arquivo: `src/components/admin/admin-users-manager.tsx`.
- Agentes (dispositivos): provisionamento automático; edição detalhada/vínculo principal em Admin ▸ Dispositivos. Arquivo: `src/components/admin/devices/admin-devices-overview.tsx`.
> Observação operacional: mantivemos o provisionamento de dispositivos inalterado (token/email técnico), e o acesso web segue apenas para pessoas. A unificação é de UX/gestão.
## 10) Convex Self-Hosted — Problema de Memoria OOM (Historico Completo)
> **STATUS: RESOLVIDO DEFINITIVAMENTE em 10/12/2025**
### Cronologia do Problema
| Data | Evento | Memoria |
|------|--------|---------|
| Nov/2025 | Problema detectado - exports em loop | ~10GB |
| 09/12/2025 | Tentativa 1: separar heartbeats em tabela dedicada | ~8GB |
| 10/12/2025 | 12 OOM kills em 12 horas | 19-20GB (limite) |
| 10/12/2025 | **Solucao definitiva aplicada** | **395MB** |
### Causa Raiz Identificada (10/12/2025)
O Convex self-hosted carrega **TODAS as versoes de TODOS os documentos** em memoria (SQLite in-memory para OCC - Optimistic Concurrency Control). Isso significa:
1. **Cada `patch()` cria uma nova versao** do documento (nao substitui)
2. **Versoes antigas permanecem** no banco e na memoria
3. **Heartbeats a cada 5 min** com inventory de ~92KB por maquina
4. **6 maquinas x 288 heartbeats/dia = 1.728 versoes/dia**
5. **Banco de 450MB expandia para 19GB+ em RAM** (~42x)
### Solucao Definitiva (10/12/2025)
#### Parte 1: Mover Crons para Linux (evitar acumulo de `_scheduled_job_logs`)
```bash
# Crons do Convex COMENTADOS em convex/crons.ts
# Substituidos por endpoints HTTP + crontab Linux
```
Ver secao 12 para detalhes.
#### Parte 2: Limpeza Manual do Banco (remover versoes antigas)
```bash
# 1. Parar Convex
docker service scale sistema_convex_backend=0
# 2. Backup
cd /var/lib/docker/volumes/sistema_convex_data/_data
cp db.sqlite3 db.sqlite3.backup-$(date +%Y%m%d-%H%M%S)
# 3. Limpar versoes antigas (manter apenas a mais recente de cada documento)
sqlite3 db.sqlite3 "DELETE FROM documents WHERE (id, ts) NOT IN (SELECT id, MAX(ts) FROM documents GROUP BY id);"
# 4. Compactar banco
sqlite3 db.sqlite3 "VACUUM;"
# 5. Reiniciar
docker service scale sistema_convex_backend=1
```
#### Parte 3: Corrigir Codigo do Heartbeat (evitar novas versoes desnecessarias)
**Arquivo:** `convex/machines.ts` (linhas 857-895)
**Problema:** O codigo original adicionava inventory/metrics ao patch se eles EXISTIAM, nao se MUDARAM:
```typescript
// ANTES (criava versao mesmo com dados identicos)
if (args.inventory) {
metadataPatch.inventory = mergeInventory(...)
}
```
**Solucao:** Comparar dados antes de incluir no patch:
```typescript
// DEPOIS (so cria versao se dados MUDARAM)
if (args.inventory && typeof args.inventory === "object") {
const currentInventory = currentMetadata.inventory
const newInventoryStr = JSON.stringify(args.inventory)
const currentInventoryStr = JSON.stringify(currentInventory ?? {})
if (newInventoryStr !== currentInventoryStr) {
metadataPatch.inventory = mergeInventory(currentInventory, args.inventory)
}
}
```
### Resultado Final
| Metrica | ANTES (10/12 08:00) | DEPOIS (10/12 14:30) | Reducao |
|---------|---------------------|----------------------|---------|
| Banco SQLite | 450MB | **17MB** | **96%** |
| Memoria Convex | 19GB+ (OOM) | **395MB** | **98%** |
| RAM livre servidor | 404MB | **15GB** | +15GB |
| Registros no banco | 56.247 | 21.001 | -63% |
| OOM kills/dia | 12+ | **0** | -100% |
### Por que a Solucao Funciona
1. **Crons movidos**: Zero novos registros em `_scheduled_job_logs`
2. **Versoes antigas removidas**: Banco limpo, sem historico desnecessario
3. **Codigo corrigido**: Heartbeats com dados identicos = **0 versoes novas**
**Estimativa de crescimento futuro:**
- Antes: ~1.728 versoes/dia (6 maquinas x 288 heartbeats)
- Depois: ~30 versoes/dia (so quando inventory/metrics MUDA de verdade)
- **Reducao de 98%** no crescimento do banco
## 11) Convex Self-Hosted — Erros de shape_inference (RESOLVIDO)
> **STATUS: RESOLVIDO em 10/12/2025 pela limpeza do banco**
### Problema Original
O Convex usa **shape inference** para otimizar queries. Quando documentos da mesma tabela tem campos com tipos incompativeis, o sistema falha.
**Causa:** A tabela `_scheduled_job_logs` tinha registros com:
- `logLines: []` (Array<never>)
- `logLines: ["mensagem"]` (Array<string>)
O shape inference nao consegue unificar esses tipos.
### Solucao Aplicada
A limpeza completa do banco (secao 10, Parte 2) **removeu todos os registros problematicos**:
```sql
-- Isso removeu 4.227 registros de _scheduled_job_logs
DELETE FROM documents WHERE (id, ts) NOT IN (SELECT id, MAX(ts) FROM documents GROUP BY id);
```
### Status Atual
- **Zero erros de shape_inference** nos logs desde 10/12/2025
- **Crons movidos para Linux** = nenhum novo registro em `_scheduled_job_logs`
- Problema **nao deve recorrer** com a arquitetura atual
### Nota Historica
Os `console.log()` adicionados em 09/12/2025 nos handlers de cron (para garantir `logLines` nao vazio) **nao sao mais necessarios** pois os crons foram movidos para Linux. Porem, foram mantidos no codigo por seguranca caso os crons sejam reativados no futuro.
## 12) Cron Jobs — Arquitetura Final
> **STATUS: MIGRADO para Linux crontab em 10/12/2025**
### Por que Migrar
Os cron jobs do Convex criam registros em `_scheduled_job_logs` que:
1. Acumulam versoes em memoria (nunca sao removidos automaticamente)
2. Cron de 1 minuto = 1.440 registros/dia = 43.200/mes
3. Cada registro ocupa espaco no SQLite in-memory
### Arquitetura Atual
```
┌─────────────────┐ ┌──────────────────────┐ ┌─────────────────┐
│ Linux crontab │────▶│ Next.js API Route │────▶│ Convex Mutation│
│ (VPS) │ │ /api/cron/* │ │ (funcao real) │
└─────────────────┘ └──────────────────────┘ └─────────────────┘
│ │
│ curl a cada X min │ ConvexHttpClient
▼ ▼
Nao cria logs Executa a logica
no Convex sem criar _scheduled_job_logs
```
### Mapeamento de Crons
| Funcao Original | Endpoint HTTP | Frequencia | Status |
|-----------------|---------------|------------|--------|
| `auto-end-inactive-chat-sessions` | `/api/cron/chat-cleanup` | 1 min | **ATIVO** |
| `cleanup-stale-usb-policies` | `/api/cron/usb-cleanup` | 30 min | **ATIVO** |
| `report-export-runner` | - | 15 min | DESABILITADO (flag) |
| `auto-pause-internal-lunch` | - | diario | DESABILITADO (flag) |
### Configuracao na VPS
```bash
# Crontab atual (root@154.12.253.40)
CRON_SECRET="reports-cron-938fa2e0f663b43bb43203413783d6415e6d0cdfb58c25a749b4c90241c4017b"
# Chat cleanup - a cada minuto
* * * * * curl -s "https://tickets.esdrasrenan.com.br/api/cron/chat-cleanup" -H "x-cron-secret: $CRON_SECRET" >/dev/null 2>&1
# USB policy cleanup - a cada 30 minutos
*/30 * * * * curl -s "https://tickets.esdrasrenan.com.br/api/cron/usb-cleanup" -H "x-cron-secret: $CRON_SECRET" >/dev/null 2>&1
```
### Arquivos Relacionados
- `src/app/api/cron/chat-cleanup/route.ts` - Endpoint para encerrar chats inativos
- `src/app/api/cron/usb-cleanup/route.ts` - Endpoint para limpar policies USB
- `convex/crons.ts` - Crons originais (COMENTADOS, mantidos para referencia)
## 13) Monitoramento e Manutencao — Guia Pos-Correcao (10/12/2025)
### Status Esperado (Sistema Saudavel)
| Metrica | Valor Esperado | Alerta Se |
|---------|----------------|-----------|
| Memoria Convex | < 1GB | > 5GB |
| Banco SQLite | < 50MB | > 200MB |
| OOM kills/dia | 0 | > 0 |
| Erros shape_inference | 0 | > 0 |
### Comandos de Verificacao
```bash
# Verificar memoria do Convex
ssh -i ~/.ssh/codex_ed25519 root@154.12.253.40 \
"docker stats --no-stream --format 'table {{.Name}}\t{{.MemUsage}}\t{{.MemPerc}}' | grep convex"
# Verificar tamanho do banco
ssh -i ~/.ssh/codex_ed25519 root@154.12.253.40 \
"ls -lh /var/lib/docker/volumes/sistema_convex_data/_data/db.sqlite3"
# Verificar OOM kills (desde meia-noite)
ssh -i ~/.ssh/codex_ed25519 root@154.12.253.40 \
"journalctl --since '00:00:00' -k | grep -c 'Killed process.*convex'"
# Verificar erros nos logs
ssh -i ~/.ssh/codex_ed25519 root@154.12.253.40 \
"docker service logs sistema_convex_backend --tail 50 2>&1 | grep -E 'ERROR|shape_inference'"
```
### Quando Fazer Limpeza Manual
**NAO e necessario fazer limpeza periodica** com a correcao de codigo aplicada. O sistema deve se manter estavel indefinidamente.
Fazer limpeza manual apenas se:
1. Banco SQLite ultrapassar 200MB
2. Memoria Convex ultrapassar 5GB
3. Ocorrerem OOM kills
### Procedimento de Limpeza (Se Necessario)
```bash
# 1. Verificar estado atual
ssh root@154.12.253.40 "ls -lh /var/lib/docker/volumes/sistema_convex_data/_data/db.sqlite3"
# 2. Se > 200MB, fazer limpeza:
ssh root@154.12.253.40 << 'EOF'
docker service scale sistema_convex_backend=0
cd /var/lib/docker/volumes/sistema_convex_data/_data
cp db.sqlite3 db.sqlite3.backup-$(date +%Y%m%d-%H%M%S)
sqlite3 db.sqlite3 "DELETE FROM documents WHERE (id, ts) NOT IN (SELECT id, MAX(ts) FROM documents GROUP BY id); VACUUM;"
docker service scale sistema_convex_backend=1
EOF
```
### Resumo das Correcoes Aplicadas
| Problema | Solucao | Arquivo | Commit |
|----------|---------|---------|--------|
| Crons acumulando logs | Mover para Linux crontab | `convex/crons.ts`, `src/app/api/cron/*` | 178c7d7 |
| Heartbeat criando versoes | Comparar dados antes de patch | `convex/machines.ts:857-895` | b6f69d7 |
| Versoes antigas no banco | Limpeza manual + VACUUM | N/A (operacao manual) | N/A |
### Backups Disponiveis (VPS)
```
/var/lib/docker/volumes/sistema_convex_data/_data/
├── db.sqlite3 17MB (atual, limpo)
├── db.sqlite3.backup-20251210-084225 452MB (antes dos crons)
├── db.sqlite3.backup-pre-cleanup-20251210-100336 450MB (antes da limpeza final)
├── db.sqlite3.backup-20251209-234720 447MB (09/12)
└── db.sqlite3.pre-vacuum-20251209 449MB (antes do primeiro vacuum)
```
## 14) Saude e retencao (dashboard interno)
- Dashboard staff: `/admin/health` mostra tickets, cadastros, estado de heartbeat e resumo de retencao. Usa Prisma + Convex; se o Convex nao responder, exibe aviso.
- Token interno: defina `INTERNAL_HEALTH_TOKEN` (ou reutilize `REPORTS_CRON_SECRET`) no Convex e no Next para a query `ops.healthSnapshot`.
- Politica alvo: tickets sem expiracao; telemetria inventory/metrics 90 dias; alertas 180 dias; runs/artefatos de export 30 dias. Detalhes em `docs/RETENTION-HEALTH.md`.
- Sem cron de limpeza ligado. Monitorar tamanho do SQLite e memoria; so limpar/arquivar em janela de manutencao com backup.
- Backup local de tickets: `POST /api/admin/tickets/archive-local` (staff) exporta tickets resolvidos mais antigos que N dias para JSONL em `ARCHIVE_DIR` (padrao `./archives`). Protegido por `INTERNAL_HEALTH_TOKEN`/`REPORTS_CRON_SECRET`.
---
Ultima atualizacao: **10/12/2025** - Problema de OOM resolvido definitivamente. Sistema estavel com 395MB de memoria (1.93% do limite de 20GB)