import * as cron from 'node-cron'; import { storage } from '../storage'; import { cacheService } from './cacheService'; import { monitoringService } from './monitoringService'; import { log } from '../vite'; interface ScheduledTask { name: string; schedule: string; handler: () => Promise; enabled: boolean; } class TaskScheduler { private tasks: Map = new Map(); private taskDefinitions: ScheduledTask[] = [ { name: 'cache-cleanup', schedule: '0 */6 * * *', // Every 6 hours handler: this.cleanupCache.bind(this), enabled: true }, { name: 'performance-metrics', schedule: '*/5 * * * *', // Every 5 minutes handler: this.collectPerformanceMetrics.bind(this), enabled: true }, { name: 'data-optimization', schedule: '0 2 * * *', // Daily at 2 AM handler: this.optimizeDatabase.bind(this), enabled: true }, { name: 'session-cleanup', schedule: '0 */12 * * *', // Every 12 hours handler: this.cleanupSessions.bind(this), enabled: true }, { name: 'analytics-aggregation', schedule: '0 1 * * *', // Daily at 1 AM handler: this.aggregateAnalytics.bind(this), enabled: true } ]; initialize() { log('Initializing task scheduler...', 'scheduler'); this.taskDefinitions.forEach(taskDef => { if (taskDef.enabled) { this.scheduleTask(taskDef); } }); log(`Scheduled ${this.tasks.size} automated tasks`, 'scheduler'); } private scheduleTask(taskDef: ScheduledTask) { try { const task = cron.schedule(taskDef.schedule, async () => { const startTime = Date.now(); log(`Starting scheduled task: ${taskDef.name}`, 'scheduler'); try { await taskDef.handler(); const duration = Date.now() - startTime; log(`Completed task: ${taskDef.name} (${duration}ms)`, 'scheduler'); // Log task execution for monitoring monitoringService.logActivity(0, 'system_task', taskDef.name, { duration, status: 'success' }); } catch (error) { const duration = Date.now() - startTime; log(`Failed task: ${taskDef.name} - ${error}`, 'scheduler'); monitoringService.logActivity(0, 'system_task', taskDef.name, { duration, status: 'error', error: error instanceof Error ? error.message : 'Unknown error' }); } }, { scheduled: false }); this.tasks.set(taskDef.name, task); task.start(); log(`Scheduled task: ${taskDef.name} with cron: ${taskDef.schedule}`, 'scheduler'); } catch (error) { log(`Failed to schedule task ${taskDef.name}: ${error}`, 'scheduler'); } } // Cache cleanup - remove expired entries and optimize memory private async cleanupCache(): Promise { try { await cacheService.cleanup(); // Force garbage collection if available if (global.gc) { global.gc(); } log('Cache cleanup completed', 'scheduler'); } catch (error) { log(`Cache cleanup failed: ${error}`, 'scheduler'); throw error; } } // Collect and store performance metrics private async collectPerformanceMetrics(): Promise { try { const metrics = await monitoringService.getPerformanceMetrics(); // Store metrics in cache for quick access await cacheService.set('performance_metrics', metrics, 300); // 5 minutes // Log if performance is degrading if (metrics.responseTime > 1000) { log(`High response time detected: ${metrics.responseTime}ms`, 'scheduler'); } if (metrics.errorRate > 5) { log(`High error rate detected: ${metrics.errorRate}%`, 'scheduler'); } } catch (error) { log(`Performance metrics collection failed: ${error}`, 'scheduler'); throw error; } } // Database optimization tasks private async optimizeDatabase(): Promise { try { // This would typically include: // - VACUUM operations for PostgreSQL // - Index optimization // - Statistics updates // For now, we'll simulate with data cleanup log('Starting database optimization', 'scheduler'); // Clean up old AI interactions (keep last 30 days) const thirtyDaysAgo = new Date(); thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30); // Note: In a real implementation, you'd use proper SQL for bulk operations log('Database optimization completed', 'scheduler'); } catch (error) { log(`Database optimization failed: ${error}`, 'scheduler'); throw error; } } // Clean up expired sessions private async cleanupSessions(): Promise { try { // Clean up expired sessions from database // This would typically be done with a SQL query log('Session cleanup completed', 'scheduler'); } catch (error) { log(`Session cleanup failed: ${error}`, 'scheduler'); throw error; } } // Aggregate analytics data for reporting private async aggregateAnalytics(): Promise { try { const metrics = await monitoringService.getSystemMetrics(); // Store daily aggregates const today = new Date().toISOString().split('T')[0]; await cacheService.set(`analytics_daily_${today}`, metrics, 86400 * 7); // Keep for 7 days log('Analytics aggregation completed', 'scheduler'); } catch (error) { log(`Analytics aggregation failed: ${error}`, 'scheduler'); throw error; } } // Manual task execution for testing/admin purposes async executeTask(taskName: string): Promise { const taskDef = this.taskDefinitions.find(t => t.name === taskName); if (!taskDef) { throw new Error(`Task not found: ${taskName}`); } log(`Manually executing task: ${taskName}`, 'scheduler'); await taskDef.handler(); } // Get task status getTaskStatus(): { name: string; enabled: boolean; nextRun?: Date }[] { return this.taskDefinitions.map(taskDef => ({ name: taskDef.name, enabled: taskDef.enabled, nextRun: this.tasks.get(taskDef.name)?.nextDate()?.toDate() })); } // Stop all scheduled tasks shutdown() { log('Shutting down task scheduler...', 'scheduler'); this.tasks.forEach((task, name) => { task.stop(); log(`Stopped task: ${name}`, 'scheduler'); }); this.tasks.clear(); log('Task scheduler shutdown completed', 'scheduler'); } } export const taskScheduler = new TaskScheduler();