using Microsoft.Extensions.Options; namespace Campaign_Tracker.Server.ExtensionData; public sealed class LegacyLinkIntegrityOptions { public bool Enabled { get; init; } = true; public TimeSpan RunTimeLocal { get; init; } = new(2, 0, 0); } public sealed class LegacyLinkIntegrityHostedService : BackgroundService { private readonly IServiceScopeFactory _scopeFactory; private readonly ILogger _logger; private readonly LegacyLinkIntegrityOptions _options; public LegacyLinkIntegrityHostedService( IServiceScopeFactory scopeFactory, ILogger logger, IOptions options) { _scopeFactory = scopeFactory; _logger = logger; _options = options.Value; } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { if (!_options.Enabled) { _logger.LogInformation("Legacy link nightly integrity check scheduler is disabled."); return; } while (!stoppingToken.IsCancellationRequested) { await Task.Delay(GetDelayUntilNextRun(DateTimeOffset.Now, _options.RunTimeLocal), stoppingToken); await RunOnceAsync(stoppingToken); } } private async Task RunOnceAsync(CancellationToken cancellationToken) { try { using var scope = _scopeFactory.CreateScope(); var check = scope.ServiceProvider.GetRequiredService(); var report = await check.CheckAsync(cancellationToken); if (report.IsConsistent) { _logger.LogInformation( "Legacy link nightly integrity check passed: {Consistent}/{Total} records ({Percentage:F2}%).", report.ConsistentRecords, report.TotalRecords, report.ConsistencyPercentage); } else { _logger.LogError( "Legacy link nightly integrity check failed: {Failures}/{Total} records failed ({Percentage:F2}%).", report.FailedRecords, report.TotalRecords, report.ConsistencyPercentage); } } catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested) { } catch (Exception ex) { _logger.LogError(ex, "Legacy link nightly integrity check could not complete."); } } private static TimeSpan GetDelayUntilNextRun(DateTimeOffset now, TimeSpan runTimeLocal) { var next = new DateTimeOffset(now.Date + runTimeLocal, now.Offset); if (next <= now) { next = next.AddDays(1); } return next - now; } }