fix(convex): mover cron jobs para API HTTP + crontab do Linux

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>
This commit is contained in:
rever-tecnologia 2025-12-10 08:51:32 -03:00
parent e2dde8510a
commit 178c7d7341
10 changed files with 1357 additions and 19 deletions

View file

@ -0,0 +1,53 @@
import { NextRequest, NextResponse } from "next/server"
import { createConvexClient } from "@/server/convex-client"
import { api } from "@/convex/_generated/api"
export const runtime = "nodejs"
export const dynamic = "force-dynamic"
// Endpoint para encerrar sessoes de chat inativas
// Substitui o cron job do Convex para evitar acumulo de registros em _scheduled_job_logs
// Chamado via crontab do Linux:
// * * * * * curl -s "https://tickets.esdrasrenan.com.br/api/cron/chat-cleanup" -H "x-cron-secret: $CRON_SECRET" >/dev/null 2>&1
export async function GET(request: NextRequest) {
// Verificar secret no header
const authHeader = request.headers.get("x-cron-secret")
const expectedSecret = process.env.CRON_SECRET || process.env.REPORTS_CRON_SECRET
if (!expectedSecret) {
return NextResponse.json(
{ error: "CRON_SECRET nao configurado no servidor" },
{ status: 500 }
)
}
if (authHeader !== expectedSecret) {
return NextResponse.json(
{ error: "Nao autorizado" },
{ status: 401 }
)
}
try {
const convex = createConvexClient()
const result = await convex.mutation(api.liveChat.autoEndInactiveSessions, {})
return NextResponse.json({
success: true,
message: "Sessoes de chat inativas encerradas",
result,
timestamp: new Date().toISOString(),
})
} catch (error) {
console.error("Erro ao executar chat-cleanup:", error)
return NextResponse.json(
{
success: false,
error: error instanceof Error ? error.message : String(error),
},
{ status: 500 }
)
}
}

View file

@ -0,0 +1,53 @@
import { NextRequest, NextResponse } from "next/server"
import { createConvexClient } from "@/server/convex-client"
import { api } from "@/convex/_generated/api"
export const runtime = "nodejs"
export const dynamic = "force-dynamic"
// Endpoint para limpar policies USB pendentes
// Substitui o cron job do Convex para evitar acumulo de registros em _scheduled_job_logs
// Chamado via crontab do Linux:
// */30 * * * * curl -s "https://tickets.esdrasrenan.com.br/api/cron/usb-cleanup" -H "x-cron-secret: $CRON_SECRET" >/dev/null 2>&1
export async function GET(request: NextRequest) {
// Verificar secret no header
const authHeader = request.headers.get("x-cron-secret")
const expectedSecret = process.env.CRON_SECRET || process.env.REPORTS_CRON_SECRET
if (!expectedSecret) {
return NextResponse.json(
{ error: "CRON_SECRET nao configurado no servidor" },
{ status: 500 }
)
}
if (authHeader !== expectedSecret) {
return NextResponse.json(
{ error: "Nao autorizado" },
{ status: 401 }
)
}
try {
const convex = createConvexClient()
const result = await convex.mutation(api.usbPolicy.cleanupStalePendingPolicies, {})
return NextResponse.json({
success: true,
message: "Policies USB pendentes limpas",
result,
timestamp: new Date().toISOString(),
})
} catch (error) {
console.error("Erro ao executar usb-cleanup:", error)
return NextResponse.json(
{
success: false,
error: error instanceof Error ? error.message : String(error),
},
{ status: 500 }
)
}
}