-- ============================================================================= -- Migration: 20260419000013_cron_retention_jobs -- V#52 — retention automática de logs/challenges via pg_cron. -- -- Jobs: -- • document_access_logs_cleanup — diário, retém 1 ano (CFP típico) -- • math_challenges_cleanup — horário, remove expirados há >1h -- • public_submission_attempts_cleanup — diário, retém 90 dias -- ============================================================================= -- Garante extensão (idempotente em ambientes que não têm) CREATE EXTENSION IF NOT EXISTS pg_cron; -- ───────────────────────────────────────────────────────────────────────── -- document_access_logs: retém 1 ano (suficiente pra auditoria CFP) -- ----------------------------------------------------------------------------- SELECT cron.unschedule('document_access_logs_cleanup') WHERE EXISTS (SELECT 1 FROM cron.job WHERE jobname = 'document_access_logs_cleanup'); SELECT cron.schedule( 'document_access_logs_cleanup', '0 3 * * *', -- todo dia às 03:00 $$DELETE FROM public.document_access_logs WHERE created_at < now() - interval '1 year'$$ ); -- ───────────────────────────────────────────────────────────────────────── -- math_challenges: remove expirados (> 1h após expiração) -- (RPC cleanup_expired_math_challenges já existe desde 20260419000007) -- ----------------------------------------------------------------------------- SELECT cron.unschedule('math_challenges_cleanup') WHERE EXISTS (SELECT 1 FROM cron.job WHERE jobname = 'math_challenges_cleanup'); SELECT cron.schedule( 'math_challenges_cleanup', '0 * * * *', -- toda hora $$SELECT public.cleanup_expired_math_challenges()$$ ); -- ───────────────────────────────────────────────────────────────────────── -- public_submission_attempts: retém 90 dias (analytics + alertas) -- ----------------------------------------------------------------------------- SELECT cron.unschedule('public_submission_attempts_cleanup') WHERE EXISTS (SELECT 1 FROM cron.job WHERE jobname = 'public_submission_attempts_cleanup'); SELECT cron.schedule( 'public_submission_attempts_cleanup', '15 3 * * *', -- todo dia 03:15 (após o de docs) $$DELETE FROM public.public_submission_attempts WHERE created_at < now() - interval '90 days'$$ ); -- ───────────────────────────────────────────────────────────────────────── -- submission_rate_limits: limpa entradas antigas (>30 dias sem atividade) -- (estados expirados não fazem mal, mas tabela cresce sem limite) -- ----------------------------------------------------------------------------- SELECT cron.unschedule('submission_rate_limits_cleanup') WHERE EXISTS (SELECT 1 FROM cron.job WHERE jobname = 'submission_rate_limits_cleanup'); SELECT cron.schedule( 'submission_rate_limits_cleanup', '30 3 * * *', -- todo dia 03:30 $$DELETE FROM public.submission_rate_limits WHERE last_attempt_at < now() - interval '30 days' AND (blocked_until IS NULL OR blocked_until < now()) AND (requires_captcha_until IS NULL OR requires_captcha_until < now())$$ );