From 8925428e467c6952a8db7d10f65a6d324ca872ce Mon Sep 17 00:00:00 2001 From: TekH Date: Mon, 17 Aug 2026 16:05:30 +0200 Subject: [PATCH] feat(infrastructure): implement IEmailSyncService on EmailSyncWorker with rate-limited ForceTriggerSync via IMemoryCache; register as singleton in DI --- .../DependencyInjection.cs | 8 ++++++++ .../Services/Background/EmailSyncWorker.cs | 17 ++++++++++++++--- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/infrastructure/DigitalData.MessagingService.Infrastructure/DependencyInjection.cs b/src/infrastructure/DigitalData.MessagingService.Infrastructure/DependencyInjection.cs index 7958361..0b6faf3 100644 --- a/src/infrastructure/DigitalData.MessagingService.Infrastructure/DependencyInjection.cs +++ b/src/infrastructure/DigitalData.MessagingService.Infrastructure/DependencyInjection.cs @@ -13,6 +13,7 @@ using Microsoft.AspNetCore.DataProtection; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; namespace DigitalData.MessagingService.Infrastructure; @@ -71,6 +72,13 @@ public static class DependencyInjection services.AddHostedService(); services.AddHostedService(); + services.AddSingleton(p => + { + var hostedServices = p.GetRequiredService>(); + var emailSyncWorkers = hostedServices.OfType(); + return emailSyncWorkers.FirstOrDefault() ?? throw new InvalidOperationException("EmailSyncWorker is not registered."); + }); + services.AddMemoryCache(); // --- Database (InMemory) --- diff --git a/src/infrastructure/DigitalData.MessagingService.Infrastructure/Services/Background/EmailSyncWorker.cs b/src/infrastructure/DigitalData.MessagingService.Infrastructure/Services/Background/EmailSyncWorker.cs index e931de8..35c79ae 100644 --- a/src/infrastructure/DigitalData.MessagingService.Infrastructure/Services/Background/EmailSyncWorker.cs +++ b/src/infrastructure/DigitalData.MessagingService.Infrastructure/Services/Background/EmailSyncWorker.cs @@ -3,6 +3,7 @@ using DigitalData.MessagingService.Application.Common.Interfaces.Repositories; using DigitalData.MessagingService.Application.Common.Options; using DigitalData.MessagingService.Domain.Entities; using DigitalData.MessagingService.Domain.Enums; +using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; @@ -10,8 +11,10 @@ using Microsoft.Extensions.Options; namespace DigitalData.MessagingService.Infrastructure.Services.Background; -public class EmailSyncWorker(IOptions Options, IServiceProvider Provider, ILogger Logger) : BackgroundService +public class EmailSyncWorker(IOptions Options, IServiceProvider Provider, ILogger Logger, IMemoryCache Cache) : BackgroundService, IEmailSyncService { + private static string ForcedSyncDateCacheKey => $"{nameof(EmailSyncWorker)}_TriggerSync"; + private readonly string DefaultFolder = "INBOX"; /// @@ -26,7 +29,15 @@ public class EmailSyncWorker(IOptions Options, IServicePro /// Safe to call from any thread or HTTP request at any time. /// If a sync is already running, the trigger is ignored — the next cycle starts normally. /// - public void TriggerSync() => _syncTrigger.TrySetResult(true); + public DateTime ForceTriggerSync() + { + return Cache.GetOrCreate(ForcedSyncDateCacheKey, e => + { + e.SetAbsoluteExpiration(TimeSpan.FromSeconds(Options.Value.ForcedSyncIntervalSeconds)); + _syncTrigger.TrySetResult(true); + return DateTime.UtcNow; + }); + } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { @@ -98,7 +109,7 @@ public class EmailSyncWorker(IOptions Options, IServicePro } } - public async Task UpsertSeedEmailAccount(CancellationToken stoppingToken) + private async Task UpsertSeedEmailAccount(CancellationToken stoppingToken) { await using var scope = Provider.CreateAsyncScope();