namespace JobsMedical.Web.Services.Scraping; /// /// Periodically runs the ingestion engine when the admin has turned auto-ingest ON /// (AppSetting.AutoIngestEnabled) — read fresh from the DB each cycle, so it can be toggled at /// runtime from the admin panel with no redeploy. When off, it idles and re-checks. /// public class IngestionWorker : BackgroundService { private readonly IServiceScopeFactory _scopes; private readonly ILogger _log; public IngestionWorker(IServiceScopeFactory scopes, ILogger log) { _scopes = scopes; _log = log; } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { // Small startup delay so the DB/migrations are ready. try { await Task.Delay(TimeSpan.FromSeconds(20), stoppingToken); } catch (OperationCanceledException) { return; } while (!stoppingToken.IsCancellationRequested) { var idleMinutes = 10; try { using var scope = _scopes.CreateScope(); // Always archive stale listings (independent of ingestion being on). await scope.ServiceProvider.GetRequiredService().ArchiveStaleAsync(stoppingToken); var settings = await scope.ServiceProvider .GetRequiredService().GetAsync(); if (settings.AutoIngestEnabled) { var svc = scope.ServiceProvider.GetRequiredService(); var summary = await svc.RunAsync(stoppingToken); _log.LogInformation("Auto-ingest: queued={Q} published={P} flagged={F} spam={S} dupes={D}", summary.TotalQueued, summary.TotalPublished, summary.TotalFlagged, summary.TotalSpam, summary.TotalDuplicates); idleMinutes = Math.Max(1, settings.IngestIntervalMinutes); } } catch (Exception ex) when (ex is not OperationCanceledException) { _log.LogError(ex, "Auto-ingest cycle failed"); } try { await Task.Delay(TimeSpan.FromMinutes(idleMinutes), stoppingToken); } catch (OperationCanceledException) { break; } } } }