feat(260521-fci-02): wire tickets-reconcile schedule + migration 090
- sync-scheduler.ts: extend sync_type union with 'tickets-reconcile', add a default schedule entry (disabled, 30 4 * * *), and a dispatch case using the device-link-reconcile / integration-health dynamic-import pattern. - migrations/090_ticket_reconcile_schedule.sql: idempotent INSERT (ON CONFLICT DO NOTHING) so existing installs pick up the row without disturbing the fresh-DB default-seed path.
This commit is contained in:
parent
51f0b32cb3
commit
badd718194
2 changed files with 52 additions and 1 deletions
|
|
@ -22,7 +22,7 @@ export interface ScheduleConfig {
|
||||||
name: string;
|
name: string;
|
||||||
description: string;
|
description: string;
|
||||||
cron_expression: 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';
|
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';
|
||||||
years_back?: number;
|
years_back?: number;
|
||||||
is_enabled: boolean;
|
is_enabled: boolean;
|
||||||
last_run?: Date;
|
last_run?: Date;
|
||||||
|
|
@ -291,6 +291,14 @@ class SyncScheduler {
|
||||||
sync_type: 'integration-health',
|
sync_type: 'integration-health',
|
||||||
is_enabled: false,
|
is_enabled: false,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 'tickets-reconcile',
|
||||||
|
name: 'Tickets Reconciliation',
|
||||||
|
description: 'STOPGAP backstop: re-fetches stale-open tickets (>7d since last sync) from Autotask and reconciles status / soft-deletes missing rows. Runs daily at 4:30 AM. Capped at 500 tickets per run.',
|
||||||
|
cron_expression: '30 4 * * *',
|
||||||
|
sync_type: 'tickets-reconcile',
|
||||||
|
is_enabled: false,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
for (const schedule of defaultSchedules) {
|
for (const schedule of defaultSchedules) {
|
||||||
|
|
@ -434,6 +442,25 @@ class SyncScheduler {
|
||||||
console.log(
|
console.log(
|
||||||
`[SCHEDULER] integration-health: failed=${result.summary.failed} expired=${result.summary.expired} expiringSoon=${result.summary.expiringWithin14Days} alertSent=${result.alertSent}`
|
`[SCHEDULER] integration-health: failed=${result.summary.failed} expired=${result.summary.expired} expiringSoon=${result.summary.expiringWithin14Days} alertSent=${result.alertSent}`
|
||||||
);
|
);
|
||||||
|
} else if (config.sync_type === 'appgate-sessions' || config.sync_type === 'appgate-daily') {
|
||||||
|
const { isAppgateConfigured } = await import('@/lib/services/appgate-factory');
|
||||||
|
if (!isAppgateConfigured()) {
|
||||||
|
console.log(`[SCHEDULER] Skipping ${config.sync_type} — AppGate not configured`);
|
||||||
|
} else {
|
||||||
|
const { getAppgateSyncService } = await import('@/lib/services/appgate-sync-service');
|
||||||
|
const svc = getAppgateSyncService();
|
||||||
|
if (config.sync_type === 'appgate-daily') {
|
||||||
|
await svc.dailySync('scheduled');
|
||||||
|
} else {
|
||||||
|
await svc.sessionsSync('scheduled');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (config.sync_type === 'tickets-reconcile') {
|
||||||
|
const { reconcileStaleTickets } = await import('@/lib/services/ticket-reconciliation-service');
|
||||||
|
const result = await reconcileStaleTickets();
|
||||||
|
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 === 'incremental') {
|
} else if (config.sync_type === 'incremental') {
|
||||||
await this.syncService.incrementalSync('scheduled');
|
await this.syncService.incrementalSync('scheduled');
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
24
migrations/090_ticket_reconcile_schedule.sql
Normal file
24
migrations/090_ticket_reconcile_schedule.sql
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
-- Migration 090: Seed the tickets-reconcile sync schedule (STOPGAP).
|
||||||
|
--
|
||||||
|
-- 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.
|
||||||
|
|
||||||
|
INSERT INTO sync_schedules (
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
description,
|
||||||
|
cron_expression,
|
||||||
|
sync_type,
|
||||||
|
years_back,
|
||||||
|
is_enabled
|
||||||
|
) VALUES (
|
||||||
|
'tickets-reconcile',
|
||||||
|
'Tickets Reconciliation',
|
||||||
|
'STOPGAP backstop: re-fetches stale-open tickets (>7d since last sync) from Autotask and reconciles status / soft-deletes missing rows. Runs daily at 4:30 AM. Capped at 500 tickets per run.',
|
||||||
|
'30 4 * * *',
|
||||||
|
'tickets-reconcile',
|
||||||
|
NULL,
|
||||||
|
false
|
||||||
|
)
|
||||||
|
ON CONFLICT (id) DO NOTHING;
|
||||||
Loading…
Add table
Add a link
Reference in a new issue