sistema-de-chamados/docs/OPERATIONS.md
2025-12-10 15:17:55 -03:00

26 KiB
Raw Permalink Blame History

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):

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):
        # 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)

# 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)

# 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:

// ANTES (criava versao mesmo com dados identicos)
if (args.inventory) {
  metadataPatch.inventory = mergeInventory(...)
}

Solucao: Comparar dados antes de incluir no patch:

// 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)
  • logLines: ["mensagem"] (Array)

O shape inference nao consegue unificar esses tipos.

Solucao Aplicada

A limpeza completa do banco (secao 10, Parte 2) removeu todos os registros problematicos:

-- 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

# 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

# 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)

# 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.
  • Cron de backup local: na VPS, cron diario 03:30 executa /srv/apps/sistema/scripts/archive-cron.sh gravando JSONL em /app/archives com days=365 e limit=200. Log: /var/log/tickets-archive.log.

Ultima atualizacao: 10/12/2025 - Problema de OOM resolvido definitivamente. Sistema estavel com 395MB de memoria (1.93% do limite de 20GB)