using Microsoft.Extensions.Options; using Tiku.Application.Jobs; using Tiku.Application.Security; using Tiku.Application.Tenancy; namespace Tiku.Worker; public class Worker( IServiceScopeFactory scopeFactory, IOptions options, ILogger logger) : BackgroundService { private readonly string workerId = $"{Environment.MachineName}:{Guid.NewGuid():N}"; protected override async Task ExecuteAsync(CancellationToken stoppingToken) { while (!stoppingToken.IsCancellationRequested) { try { var domainCount = await ProcessTenantDomainsAsync(stoppingToken); var jobCount = await ProcessBackgroundJobsAsync(stoppingToken); if (domainCount > 0 || jobCount > 0) { logger.LogInformation( "Worker processed {DomainCount} pending tenant domains and {JobCount} background jobs.", domainCount, jobCount); } } catch (OperationCanceledException) when (stoppingToken.IsCancellationRequested) { break; } catch (Exception exception) { logger.LogError(exception, "Tenant domain lifecycle processing failed."); } await Task.Delay( TimeSpan.FromSeconds(Math.Clamp(options.Value.PollSeconds, 10, 3600)), stoppingToken); } } private async Task ProcessTenantDomainsAsync(CancellationToken stoppingToken) { await using var scope = scopeFactory.CreateAsyncScope(); scope.ServiceProvider.GetRequiredService() .InitializeSystem(null, "Tenant domain DNS and TLS lifecycle worker"); return await scope.ServiceProvider .GetRequiredService() .ProcessPendingAsync(stoppingToken); } private async Task ProcessBackgroundJobsAsync(CancellationToken stoppingToken) { await using var scope = scopeFactory.CreateAsyncScope(); scope.ServiceProvider.GetRequiredService() .InitializeSystem(null, "Background job lease worker"); return await scope.ServiceProvider .GetRequiredService() .ProcessPendingAsync(workerId, 20, stoppingToken); } }