From fb155fe7063aef8508a8a2cd2b468673ffe7d4b8 Mon Sep 17 00:00:00 2001 From: TekH Date: Thu, 22 Oct 2026 01:35:22 +0200 Subject: [PATCH] Wire notification service and finalization worker into server host --- .../EnvelopeGenerator.Server.csproj | 1 + .../EnvelopeGenerator.Server/Program.cs | 49 ++++++++------- .../Services/EnvelopeFinalizationWorker.cs | 63 +++++++++++++++++++ EnvelopeGenerator.sln | 9 ++- 4 files changed, 98 insertions(+), 24 deletions(-) create mode 100644 EnvelopeGenerator.Server/EnvelopeGenerator.Server/Services/EnvelopeFinalizationWorker.cs diff --git a/EnvelopeGenerator.Server/EnvelopeGenerator.Server/EnvelopeGenerator.Server.csproj b/EnvelopeGenerator.Server/EnvelopeGenerator.Server/EnvelopeGenerator.Server.csproj index ea0d6042..7ab7881f 100644 --- a/EnvelopeGenerator.Server/EnvelopeGenerator.Server/EnvelopeGenerator.Server.csproj +++ b/EnvelopeGenerator.Server/EnvelopeGenerator.Server/EnvelopeGenerator.Server.csproj @@ -18,6 +18,7 @@ + diff --git a/EnvelopeGenerator.Server/EnvelopeGenerator.Server/Program.cs b/EnvelopeGenerator.Server/EnvelopeGenerator.Server/Program.cs index 66212b58..0a59afa3 100644 --- a/EnvelopeGenerator.Server/EnvelopeGenerator.Server/Program.cs +++ b/EnvelopeGenerator.Server/EnvelopeGenerator.Server/Program.cs @@ -1,34 +1,35 @@ +using DevExpress.Blazor; +using DigitalData.Auth.Claims; +using DigitalData.Auth.Client; +using DigitalData.Core.Abstractions; +using DigitalData.Core.Abstractions.Security.Extensions; +using DigitalData.Core.API; +using DigitalData.Core.Application; +using DigitalData.EmailProfilerDispatcher; +using DigitalData.UserManager.DependencyInjection; +using EnvelopeGenerator.Application; +using EnvelopeGenerator.Application.Common.Interfaces.Doc; +using EnvelopeGenerator.Domain.Constants; +using EnvelopeGenerator.Infrastructure; +using EnvelopeGenerator.Infrastructure.Crypt.DependencyInjection; +using EnvelopeGenerator.Infrastructure.Doc; +using EnvelopeGenerator.Infrastructure.Notification; +using EnvelopeGenerator.Server; +using EnvelopeGenerator.Server.Client.Services; using EnvelopeGenerator.Server.Components; using EnvelopeGenerator.Server.Models; using EnvelopeGenerator.Server.Options; -using DevExpress.Blazor; -using EnvelopeGenerator.Server.Client.Services; -using DigitalData.Core.API; -using DigitalData.Core.Application; -using EnvelopeGenerator.Infrastructure; -using EnvelopeGenerator.Domain.Constants; using Microsoft.AspNetCore.Authentication.Cookies; +using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Localization; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Options; -using System.Globalization; -using Scalar.AspNetCore; -using Microsoft.OpenApi.Models; -using DigitalData.UserManager.DependencyInjection; -using EnvelopeGenerator.Application; -using DigitalData.Auth.Client; -using DigitalData.Core.Abstractions; -using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.IdentityModel.Tokens; -using DigitalData.Core.Abstractions.Security.Extensions; -using NLog.Web; +using Microsoft.OpenApi.Models; using NLog; -using DigitalData.Auth.Claims; -using EnvelopeGenerator.Server; -using DigitalData.EmailProfilerDispatcher; -using EnvelopeGenerator.Application.Common.Interfaces.Doc; -using EnvelopeGenerator.Infrastructure.Doc; -using EnvelopeGenerator.Infrastructure.Crypt.DependencyInjection; +using NLog.Web; +using Scalar.AspNetCore; +using System.Globalization; var logger = LogManager.Setup().LoadConfigurationFromAppSettings().GetCurrentClassLogger(); logger.Info("EnvelopeGenerator.Server logging initialized!"); @@ -325,7 +326,8 @@ try }); opt.AddSQLExecutor(executor => executor.ConnectionString = connStr); }) - .AddEnvelopeGeneratorServices(config); + .AddEnvelopeGeneratorServices(config) + .AddNotificationInfrastructure(config); #pragma warning restore CS0618 builder.Services.AddDispatcher(); @@ -373,6 +375,7 @@ try builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); + builder.Services.AddHostedService(); // DevExpress Server-Side Services (CRITICAL for DxPdfViewer) builder.Services.AddDevExpressBlazor(); diff --git a/EnvelopeGenerator.Server/EnvelopeGenerator.Server/Services/EnvelopeFinalizationWorker.cs b/EnvelopeGenerator.Server/EnvelopeGenerator.Server/Services/EnvelopeFinalizationWorker.cs new file mode 100644 index 00000000..bbffbcfd --- /dev/null +++ b/EnvelopeGenerator.Server/EnvelopeGenerator.Server/Services/EnvelopeFinalizationWorker.cs @@ -0,0 +1,63 @@ +using EnvelopeGenerator.Application.PdfSigning; +using EnvelopeGenerator.Domain.Constants; +using EnvelopeGenerator.Infrastructure; +using MediatR; +using Microsoft.EntityFrameworkCore; + +namespace EnvelopeGenerator.Server.Services; + +public sealed class EnvelopeFinalizationWorker( + IServiceScopeFactory scopeFactory, + ILogger logger) : BackgroundService +{ + private static readonly TimeSpan PollInterval = TimeSpan.FromMinutes(1); + + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + while (!stoppingToken.IsCancellationRequested) + { + try + { + using var scope = scopeFactory.CreateScope(); + var dbContext = scope.ServiceProvider.GetRequiredService(); + var sender = scope.ServiceProvider.GetRequiredService(); + + var envelopeIds = await dbContext.Envelopes + .AsNoTracking() + .Where(e => e.Status == EnvelopeStatus.EnvelopeCompletelySigned && e.DocResult == null) + .Select(e => e.Id) + .ToListAsync(stoppingToken); + + foreach(var envelopeId in envelopeIds) + { + try + { + await sender.Send(new FinalizeEnvelopeCommand() + { + Id = envelopeId, + BurnedAllowed = false, + SaveDocResult = true, + }, stoppingToken); + } + catch(Exception ex) + { + logger.LogError(ex, "Unexpected exception happened during finalization of envelope. Envelope ID: {EnvelopeId}", envelopeId); + } + } + + // continue here + logger.LogDebug("EnvelopeFinalizationWorker found {Count} signed envelope(s) without DOC_RESULT.", envelopeIds.Count); + } + catch (OperationCanceledException) when (stoppingToken.IsCancellationRequested) + { + break; + } + catch (Exception ex) + { + logger.LogError(ex, "EnvelopeFinalizationWorker failed while reading envelopes."); + } + + await Task.Delay(PollInterval, stoppingToken); + } + } +} diff --git a/EnvelopeGenerator.sln b/EnvelopeGenerator.sln index 8a0e8459..2545096c 100644 --- a/EnvelopeGenerator.sln +++ b/EnvelopeGenerator.sln @@ -40,9 +40,9 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EnvelopeGenerator.Dependenc EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "EnvelopeGenerator.Server", "EnvelopeGenerator.Server", "{BF1700D5-592E-4FFA-84E8-5480E289A1F0}" ProjectSection(SolutionItems) = preProject + EnvelopeGenerator.Server\cnn_sftp_soft_hsm.bat = EnvelopeGenerator.Server\cnn_sftp_soft_hsm.bat EnvelopeGenerator.Server\cnn_ssh_soft_hsm.bat = EnvelopeGenerator.Server\cnn_ssh_soft_hsm.bat EnvelopeGenerator.Server\publish.bat = EnvelopeGenerator.Server\publish.bat - EnvelopeGenerator.Server\cnn_sftp_soft_hsm.bat = EnvelopeGenerator.Server\cnn_sftp_soft_hsm.bat SENDER_SAVE_EDIT_MIGRATION_PLAN.md = SENDER_SAVE_EDIT_MIGRATION_PLAN.md EndProjectSection EndProject @@ -58,6 +58,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EnvelopeGenerator.Infrastru EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EnvelopeGenerator.Infrastructure.Crypt", "EnvelopeGenerator.Infrastructure.Crypt\EnvelopeGenerator.Infrastructure.Crypt.csproj", "{B33A3B17-A4F8-4307-9C51-DDFE1B292DE7}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EnvelopeGenerator.Infrastructure.Notification", "EnvelopeGenerator.Infrastructure.Notification\EnvelopeGenerator.Infrastructure.Notification.csproj", "{6F328677-0E90-40A7-9CA9-CE050182D7FC}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -128,6 +130,10 @@ Global {B33A3B17-A4F8-4307-9C51-DDFE1B292DE7}.Debug|Any CPU.Build.0 = Debug|Any CPU {B33A3B17-A4F8-4307-9C51-DDFE1B292DE7}.Release|Any CPU.ActiveCfg = Release|Any CPU {B33A3B17-A4F8-4307-9C51-DDFE1B292DE7}.Release|Any CPU.Build.0 = Release|Any CPU + {6F328677-0E90-40A7-9CA9-CE050182D7FC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6F328677-0E90-40A7-9CA9-CE050182D7FC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6F328677-0E90-40A7-9CA9-CE050182D7FC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6F328677-0E90-40A7-9CA9-CE050182D7FC}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -153,6 +159,7 @@ Global {6B87FB5F-F4EA-45BE-A218-385DC2CE8E80} = {E3C758DC-914D-4B7E-8457-0813F1FDB0CB} {3B6EFE8D-F1EE-4666-87A0-9F90149CC61A} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} {B33A3B17-A4F8-4307-9C51-DDFE1B292DE7} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} + {6F328677-0E90-40A7-9CA9-CE050182D7FC} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {73E60370-756D-45AD-A19A-C40A02DACCC7}