Files
agenciapsilmno/database-novo/migrations/20260419000013_cron_retention_jobs.sql
T
Leonardo d6eb992f71 Sessoes 6cont-10: hardening em 6 areas + scan completo do SaaS
Continuacao de 7c20b51. Esta etapa fechou TODA revisao senior do SaaS
(15 areas auditadas) + refator parcial de pacientes.

Ver commit.md para descricao completa por sessao.

# Estado final do projeto
- A# auditoria abertos: 1 (A#31 Deploy real)
- V# verificacoes abertos: 14 (todos medios/baixos adiados com plano)
- Criticos: 0
- Altos: 0
- Vitest: 208/208 (era 192, +16 nos novos composables)
- SQL integration: 33/33
- E2E (Playwright): 5/5
- Areas auditadas: 15

# Highlights
- Documentos 100% fechado (V#50/51/52: portal-paciente policy + content_sha256 + 4 cron jobs retention)
- Tenants V#1 P0: tenant_invites com RLS off + 0 policies (mesmo padrao A#30)
- Calendario 100% fechado: feriados WITH CHECK
- Addons V#1 P0 (dinheiro): addon_transactions WITH CHECK saas_admin
- Central SaaS V#1: faq write so saas_admin (era tenant_admin)
- Servicos/Prontuarios 100% fechado: services/medicos/insurance_plans + cascades
- Pacientes V#9: 2 composables novos (useCep, usePatientSupportContacts) + repo estendido + script extraido (template intocado, fica para quando houver E2E)

# 8 migrations novas neste commit
- 20260419000011_documents_portal_patient_policy.sql
- 20260419000012_documents_content_hash.sql
- 20260419000013_cron_retention_jobs.sql
- 20260419000014_financial_security_hardening.sql
- 20260419000015_communication_security_hardening.sql
- 20260419000016_tenants_calendario_hardening.sql
- 20260419000017_addons_central_saas_hardening.sql
- 20260419000018_servicos_prontuarios_hardening.sql

Total acumulado: 18 migrations (Sessoes 1-10).

# A#31 reformulado pra proxima sessao
"Deploy real" muda escopo: como nao ha cloud Supabase nem secrets reais
ainda (MVP), proxima sessao vira "Preparacao completa pra deploy" (DEPLOY.md,
validar migrations num container limpo, audit edge functions, listar env vars,
script db.cjs deploy-check).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 22:00:06 -03:00

66 lines
3.7 KiB
SQL

-- =============================================================================
-- 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())$$
);