using Api.SeaHavenIndustries.Options; using Microsoft.Extensions.Options; using SeaHaven.Services.Interfaces; namespace Api.SeaHavenIndustries.HostedServices { public class PastDueCacheHostedService : BackgroundService { private readonly IServiceScopeFactory _scopeFactory; private readonly ILogger _logger; private readonly PastDueCacheJobOptions _options; private DateOnly? _lastRunDate; public PastDueCacheHostedService( IServiceScopeFactory scopeFactory, IOptions options, ILogger logger) { _scopeFactory = scopeFactory; _logger = logger; _options = options.Value.PastDueCache; } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { while (!stoppingToken.IsCancellationRequested) { if (_options.Enabled && ShouldRunNow()) { var today = DateOnly.FromDateTime(DateTime.UtcNow); if (_lastRunDate != today) { _lastRunDate = today; await RunJobAsync(stoppingToken); } } await Task.Delay(TimeSpan.FromMinutes(1), stoppingToken); } } private async Task RunJobAsync(CancellationToken stoppingToken) { try { using var scope = _scopeFactory.CreateScope(); var service = scope.ServiceProvider.GetRequiredService(); await service.RefreshAsync(stoppingToken); } catch (Exception ex) when (ex is not OperationCanceledException) { _logger.LogError(ex, "PastDue cache hosted job failed."); } } private bool ShouldRunNow() { var now = DateTime.UtcNow; if (!TimeOnly.TryParse(_options.RunAtUtc, out var runAt)) runAt = new TimeOnly(0, 10); var current = TimeOnly.FromDateTime(now); return current >= runAt && current < runAt.AddMinutes(1); } } }