diff --git a/lib/services/sync-scheduler.ts b/lib/services/sync-scheduler.ts index cd0dac2..48a0048 100644 --- a/lib/services/sync-scheduler.ts +++ b/lib/services/sync-scheduler.ts @@ -22,7 +22,7 @@ export interface ScheduleConfig { name: string; description: string; cron_expression: string; - sync_type: 'incremental' | 'full' | 'veeam-incremental' | 'veeam-full' | 'veeam-rpo-check' | 'contract-services' | 'engagement-daily' | 'zoom-daily' | 'morning-summary' | 'ticket-digest-daily' | 'ticket-digest-weekly' | 'ticket-digest-monthly' | 'device-link-reconcile' | 'integration-health' | 'qbo' | 'appgate-sessions' | 'appgate-daily' | 'tickets-reconcile' | 'pax8-daily'; + sync_type: 'incremental' | 'full' | 'veeam-incremental' | 'veeam-full' | 'veeam-rpo-check' | 'contract-services' | 'engagement-daily' | 'zoom-daily' | 'morning-summary' | 'ticket-digest-daily' | 'ticket-digest-weekly' | 'ticket-digest-monthly' | 'device-link-reconcile' | 'integration-health' | 'qbo' | 'appgate-sessions' | 'appgate-daily' | 'tickets-reconcile' | 'pax8-daily' | 'phishing-sweep'; years_back?: number; is_enabled: boolean; last_run?: Date; @@ -299,6 +299,14 @@ class SyncScheduler { sync_type: 'tickets-reconcile', is_enabled: false, }, + { + id: 'phishing-sweep', + name: 'Phishing Detection Sweep', + description: 'Reconciles recently-modified tickets through the phishing detector to catch reports missed by the webhook path (bounded to 500 tickets, idempotent on content hash).', + cron_expression: '0 5 * * *', + sync_type: 'phishing-sweep', + is_enabled: false, + }, ]; for (const schedule of defaultSchedules) { @@ -461,6 +469,12 @@ class SyncScheduler { console.log( `[SCHEDULER] tickets-reconcile: scanned=${result.scanned} updated=${result.updated} flippedComplete=${result.statusFlippedToComplete} softDeleted=${result.softDeleted} errors=${result.errors}` ); + } else if (config.sync_type === 'phishing-sweep') { + const { sweepPhishingTickets } = await import('@/lib/services/phishing-sweep-service'); + const result = await sweepPhishingTickets(); + console.log( + `[SCHEDULER] phishing-sweep: scanned=${result.scanned} flagged=${result.flagged} skippedUnchanged=${result.skippedUnchanged} errors=${result.errors}` + ); } else if (config.sync_type === 'pax8-daily') { const { isPax8Configured } = await import('@/lib/services/pax8-factory'); if (!isPax8Configured()) { diff --git a/migrations/098_phishing_sweep_schedule.sql b/migrations/098_phishing_sweep_schedule.sql new file mode 100644 index 0000000..845c3a8 --- /dev/null +++ b/migrations/098_phishing_sweep_schedule.sql @@ -0,0 +1,25 @@ +-- Migration 098: Seed the phishing-sweep sync schedule. +-- +-- The sync_scheduler.createDefaultSchedules() path only seeds defaults on a +-- virgin sync_schedules table; this migration covers existing installs. +-- Idempotent via ON CONFLICT (id) DO NOTHING. Disabled by default +-- (is_enabled=false) until an admin opts in via /admin. + +INSERT INTO sync_schedules ( + id, + name, + description, + cron_expression, + sync_type, + years_back, + is_enabled +) VALUES ( + 'phishing-sweep', + 'Phishing Detection Sweep', + 'Reconciles recently-modified tickets through the phishing detector to catch reports missed by the webhook path (bounded to 500 tickets, idempotent on content hash).', + '0 5 * * *', + 'phishing-sweep', + NULL, + false +) +ON CONFLICT (id) DO NOTHING;