# 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 self‑hosted) 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 (Self‑Hosted) — 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 self‑hosted). - 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 self‑hosted: - 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 (self‑hosted) 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, recomenda‑se: - 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 - Certifique‑se 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 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 bem‑sucedido 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, e‑mail e histórico (reinstalação) - Identificador imutável: o histórico (tickets, eventos) referencia o `userId` (imutável). O e‑mail é um atributo mutável. - Reinstalação do desktop para o mesmo colaborador: reutilize a mesma conta de usuário (mesmo `userId`); se o e‑mail mudou, atualize o e‑mail dessa conta no painel. O histórico permanece, pois o `userId` não muda. - Novo e‑mail 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 e‑mail 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 e‑mail. - 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, e‑mail, 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/e‑mail 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) - `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**: ```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`. - 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)