Problema: - Cron jobs do Convex criam registros em _scheduled_job_logs - Convex self-hosted carrega TODAS as versoes em memoria - 1488 execucoes/dia = ~45k registros/mes acumulando - Uso de memoria chegando a 19GB, causando 12 OOM kills/dia Solucao: - Criar endpoints HTTP em /api/cron/* para substituir crons - Desabilitar crons no Convex (comentados em crons.ts) - Chamar endpoints via crontab do Linux Novos arquivos: - src/app/api/cron/chat-cleanup/route.ts - src/app/api/cron/usb-cleanup/route.ts - scripts-static/* (copiado da VPS para versionamento) Documentacao: - docs/OPERATIONS.md secao 12 com instrucoes do crontab 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
459 lines
22 KiB
Markdown
459 lines
22 KiB
Markdown
# 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 <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 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 — Otimizacao de Memoria (OOM)
|
||
|
||
### Problema
|
||
|
||
O Convex self-hosted carrega **todas as versoes de todos os documentos** em memoria (SQLite in-memory). Com heartbeats de dispositivos a cada 30s, cada `patch()` criava uma nova versao do documento `machines`, causando:
|
||
|
||
- Acumulo exponencial: ~3500 versoes para ~65 maquinas
|
||
- Uso de memoria crescente: 8-10 GiB para poucos dispositivos
|
||
- Crashes por OOM e desconexoes WebSocket (codigo 1006)
|
||
|
||
### Solucao Implementada (2025-12-09)
|
||
|
||
**1. Separacao de heartbeats em tabela dedicada**
|
||
|
||
Nova tabela `machineHeartbeats` armazena apenas `lastHeartbeatAt`:
|
||
|
||
```typescript
|
||
// convex/schema.ts
|
||
machineHeartbeats: defineTable({
|
||
machineId: v.id("machines"),
|
||
lastHeartbeatAt: v.number(),
|
||
}).index("by_machine", ["machineId"]),
|
||
```
|
||
|
||
**2. Heartbeat inteligente**
|
||
|
||
A funcao `heartbeat` agora:
|
||
- SEMPRE atualiza `machineHeartbeats` (documento pequeno, upsert)
|
||
- SO atualiza `machines` quando ha mudancas reais (hostname, OS, metadata, status)
|
||
|
||
```typescript
|
||
// Verificar se ha mudancas reais nos dados
|
||
const hasMetadataChanges = Object.keys(metadataPatch).length > 0
|
||
const hasHostnameChange = args.hostname && args.hostname !== machine.hostname
|
||
const needsMachineUpdate = hasMetadataChanges || hasHostnameChange || ...
|
||
|
||
// So atualizar machines se houver mudancas reais
|
||
if (needsMachineUpdate) {
|
||
await ctx.db.patch(machine._id, { ...patch }) // SEM lastHeartbeatAt
|
||
}
|
||
```
|
||
|
||
**3. Filtragem de campos volumetricos**
|
||
|
||
`INVENTORY_BLOCKLIST` remove campos que mudam frequentemente ou sao muito grandes:
|
||
- `software` (lista de programas instalados)
|
||
- `extended` (dados estendidos)
|
||
|
||
Hardware (CPU, memoria, placa-mae) **permanece visivel** em `/admin/devices`.
|
||
|
||
### Resultado
|
||
|
||
| Metrica | Antes | Depois |
|
||
|---------|-------|--------|
|
||
| Uso de memoria | 8-10 GiB | 4-5 GiB |
|
||
| Versoes por maquina | ~50/dia | ~2-3/dia |
|
||
| Percentual RAM (20 GiB) | 40-50% | 20-25% |
|
||
|
||
### Monitoramento
|
||
|
||
```bash
|
||
# Verificar uso de memoria
|
||
docker stats --no-stream --format 'table {{.Name}}\t{{.MemUsage}}\t{{.MemPerc}}' | grep convex
|
||
|
||
# Verificar OOM kills recentes
|
||
journalctl -k | grep -i 'oom\|killed' | tail -20
|
||
|
||
# Restart do servico se necessario
|
||
docker service update --force sistema_convex_backend
|
||
```
|
||
|
||
### Limites de Memoria (Docker Swarm)
|
||
|
||
Configurado em `stack.yml`:
|
||
|
||
```yaml
|
||
services:
|
||
convex_backend:
|
||
deploy:
|
||
resources:
|
||
limits:
|
||
memory: 20G
|
||
reservations:
|
||
memory: 8G
|
||
```
|
||
|
||
Para alterar via CLI:
|
||
```bash
|
||
docker service update --limit-memory 20G --reserve-memory 8G sistema_convex_backend
|
||
```
|
||
|
||
## 11) Convex Self-Hosted — Erros de shape_inference (WebSocket 1006)
|
||
|
||
### Problema
|
||
|
||
O Convex self-hosted usa um sistema de **shape inference** para otimizar queries e exports. Quando documentos da mesma tabela tem campos com tipos incompativeis, o sistema falha ao unificar os "shapes".
|
||
|
||
**Sintoma observado:**
|
||
- WebSocket desconectando com codigo `1006` (abnormal closure)
|
||
- Logs com erro `shape_inference` repetidos
|
||
- Export em loop: `Export <id> beginning...` repetindo indefinidamente
|
||
|
||
**Causa raiz:**
|
||
A tabela `_scheduled_job_logs` acumulou milhares de registros de cron jobs. Alguns tinham:
|
||
- `logLines: []` (array vazio)
|
||
- `logLines: ["mensagem de log"]` (array com strings)
|
||
|
||
O shape inference nao consegue unificar `Array<never>` com `Array<string>`, causando falha continua.
|
||
|
||
### Solucao Implementada (2025-12-09)
|
||
|
||
**1. Garantir que cron jobs sempre produzam logs**
|
||
|
||
Adicionamos `console.log()` obrigatorio no inicio de cada handler de cron:
|
||
|
||
```typescript
|
||
// convex/liveChat.ts - autoEndInactiveSessions
|
||
handler: async (ctx) => {
|
||
console.log("cron: autoEndInactiveSessions iniciado")
|
||
// ... resto do codigo
|
||
}
|
||
|
||
// convex/usbPolicy.ts - cleanupStalePendingPolicies
|
||
handler: async (ctx, args) => {
|
||
console.log("cron: cleanupStalePendingPolicies iniciado")
|
||
// ... resto do codigo
|
||
}
|
||
```
|
||
|
||
**2. Por que funciona**
|
||
|
||
Com o log obrigatorio, **todos** os registros de `_scheduled_job_logs` terao:
|
||
- `logLines: ["cron: <nome> iniciado", ...]`
|
||
|
||
Isso garante consistencia de tipos (sempre `Array<string>`), evitando o conflito de shapes.
|
||
|
||
### Arquivos Modificados
|
||
|
||
- `convex/liveChat.ts:751` — log no inicio de `autoEndInactiveSessions`
|
||
- `convex/usbPolicy.ts:313` — log no inicio de `cleanupStalePendingPolicies`
|
||
|
||
### Monitoramento
|
||
|
||
```bash
|
||
# Verificar se ha erros de shape_inference nos logs
|
||
ssh root@154.12.253.40 "docker service logs sistema_convex_backend 2>&1 | grep -i 'shape_inference' | tail -10"
|
||
|
||
# Verificar status dos cron jobs no dashboard
|
||
# Acesse: convex-admin.esdrasrenan.com.br > Scheduled Jobs
|
||
```
|
||
|
||
### Notas
|
||
|
||
- Este e um bug interno do Convex self-hosted que pode ser corrigido em versoes futuras
|
||
- A solucao de adicionar logs obrigatorios e um workaround que nao afeta performance
|
||
- Se novos cron jobs forem adicionados, **sempre incluir um console.log no inicio**
|
||
|
||
## 12) Cron Jobs — Movidos para Linux crontab
|
||
|
||
### Problema
|
||
|
||
Os cron jobs do Convex criam registros em `_scheduled_job_logs` que acumulam versoes em memoria. O Convex self-hosted carrega **todas as versoes de todos os documentos** em RAM, causando uso excessivo de memoria (~19GB para 60k docs).
|
||
|
||
### Solucao Implementada (2025-12-10)
|
||
|
||
Os cron jobs foram movidos do Convex para endpoints HTTP no Next.js, chamados via crontab do Linux:
|
||
|
||
| Cron Job Original | Endpoint HTTP | Frequencia |
|
||
|-------------------|---------------|------------|
|
||
| `auto-end-inactive-chat-sessions` | `/api/cron/chat-cleanup` | A cada 1 min |
|
||
| `cleanup-stale-usb-policies` | `/api/cron/usb-cleanup` | A cada 30 min |
|
||
|
||
### Configuracao do Crontab na VPS
|
||
|
||
```bash
|
||
# Acessar a VPS
|
||
ssh -i ~/.ssh/codex_ed25519 root@154.12.253.40
|
||
|
||
# Editar crontab
|
||
crontab -e
|
||
|
||
# Adicionar as linhas:
|
||
CRON_SECRET="seu_token_secreto_aqui"
|
||
|
||
# Encerrar sessoes de chat inativas (a cada minuto)
|
||
* * * * * curl -s "https://tickets.esdrasrenan.com.br/api/cron/chat-cleanup" -H "x-cron-secret: $CRON_SECRET" >/dev/null 2>&1
|
||
|
||
# Limpar policies USB pendentes (a cada 30 min)
|
||
*/30 * * * * curl -s "https://tickets.esdrasrenan.com.br/api/cron/usb-cleanup" -H "x-cron-secret: $CRON_SECRET" >/dev/null 2>&1
|
||
```
|
||
|
||
### Autenticacao
|
||
|
||
Os endpoints usam o header `x-cron-secret` para autenticacao. O valor deve ser igual a variavel de ambiente `CRON_SECRET` ou `REPORTS_CRON_SECRET` configurada no Next.js.
|
||
|
||
### Verificar se esta funcionando
|
||
|
||
```bash
|
||
# Testar endpoint manualmente
|
||
curl -s "https://tickets.esdrasrenan.com.br/api/cron/chat-cleanup" \
|
||
-H "x-cron-secret: SEU_TOKEN" | jq
|
||
|
||
# Verificar logs do cron
|
||
grep CRON /var/log/syslog | tail -20
|
||
```
|
||
|
||
### Codigo-fonte
|
||
|
||
- `src/app/api/cron/chat-cleanup/route.ts`
|
||
- `src/app/api/cron/usb-cleanup/route.ts`
|
||
- `convex/crons.ts` (crons originais comentados)
|