using HanaToolbox.Config; using HanaToolbox.Logging; using HanaToolbox.Services; using HanaToolbox.Services.Interfaces; namespace HanaToolbox.Scheduling; /// /// Called every minute by the system cron job. /// Always runs Monitor. /// Runs Backup/Cleaner/Aurora/Firewall once a day at their configured time. /// Uses date-based state files to prevent double-firing. /// public sealed class CronOrchestrator( IMonitorService monitor, IBackupService backup, ICleanerService cleaner, IAuroraService aurora, IFirewallService firewall, IMonitorStateService stateService, AppLogger logger) { public async Task RunAsync(AppConfig config, string sid, CancellationToken ct = default) { var now = DateTime.Now; // Monitor runs every tick if (config.Monitor.Enabled) { logger.Step("Running monitor check..."); try { await monitor.RunAsync(config.Monitor, config.Hana, sid, ct); } catch (Exception ex) { logger.Error($"Monitor error: {ex.Message}"); } } // Once-a-day tasks await RunIfScheduled("cron_backup", config.Backup.Enabled, config.Backup.ScheduleHour, config.Backup.ScheduleMinute, now, async () => await backup.RunAsync(config.Backup, config.Hana, sid, ct), ct); await RunIfScheduled("cron_cleaner", config.Cleaner.Enabled, config.Cleaner.ScheduleHour, config.Cleaner.ScheduleMinute, now, async () => await cleaner.RunAsync(config.Cleaner, ct), ct); await RunIfScheduled("cron_aurora", config.Aurora.Enabled, config.Aurora.ScheduleHour, config.Aurora.ScheduleMinute, now, async () => await aurora.RunAsync(config.Aurora, config.Hana, sid, ct), ct); await RunIfScheduled("cron_firewall", config.Firewall.Enabled, config.Firewall.ScheduleHour, config.Firewall.ScheduleMinute, now, async () => await firewall.ApplyAsync(config.Firewall, ct), ct); } private async Task RunIfScheduled( string key, bool enabled, int hour, int minute, DateTime now, Func work, CancellationToken ct) { if (!enabled) return; if (now.Hour != hour || now.Minute != minute) return; var today = now.ToString("yyyy-MM-dd"); var lastRun = stateService.GetState(key); if (lastRun == today) { logger.Info($"Skipping '{key}' — already ran today ({today})."); return; } logger.Step($"Running scheduled task '{key}'..."); try { await work(); stateService.SetState(key, today); } catch (Exception ex) { logger.Error($"Scheduled task '{key}' failed: {ex.Message}"); } } }