From 223bb88f54c1addbf55eb05e03e229a9fa065cd0 Mon Sep 17 00:00:00 2001 From: TekH Date: Mon, 8 Jun 2026 17:14:08 +0200 Subject: [PATCH] Add CacheController and caching support for signatures Introduced a new `CacheController` to manage cached data for receiver signatures using distributed caching. Added endpoints to save, retrieve, and delete cached signatures. Created a `SignatureCacheRequest` model for caching payloads and a `CacheOptions` class to configure cache settings, including optional expiration. Updated `Program.cs` to bind `CacheOptions` to the `Cache` section in `appsettings.json`. Added a new `Cache` section in `appsettings.json` with a `SignatureCacheExpiration` property, defaulting to `null` (no expiration). --- .../Controllers/CacheController.cs | 82 +++++++++++++++++++ EnvelopeGenerator.API/Options/CacheOptions.cs | 18 ++++ EnvelopeGenerator.API/Program.cs | 4 + EnvelopeGenerator.API/appsettings.json | 3 + 4 files changed, 107 insertions(+) create mode 100644 EnvelopeGenerator.API/Controllers/CacheController.cs create mode 100644 EnvelopeGenerator.API/Options/CacheOptions.cs diff --git a/EnvelopeGenerator.API/Controllers/CacheController.cs b/EnvelopeGenerator.API/Controllers/CacheController.cs new file mode 100644 index 00000000..e1da376a --- /dev/null +++ b/EnvelopeGenerator.API/Controllers/CacheController.cs @@ -0,0 +1,82 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Caching.Distributed; +using Microsoft.Extensions.Options; +using System.Text.Json; +using EnvelopeGenerator.API.Options; +using EnvelopeGenerator.Domain.Constants; + +namespace EnvelopeGenerator.API.Controllers; + +/// +/// Manages cached data for receivers using distributed cache. +/// +[ApiController] +[Route("api/[controller]")] +[Authorize(Policy = AuthPolicy.Receiver)] +public class CacheController( + IDistributedCache cache, + IOptions cacheOptions) : ControllerBase +{ + private const string SignatureCacheKeyPrefix = "signature:91751687-8ae6-4777-bf5f-b8846085e62e:"; + + /// + /// Stores a receiver's signature in cache for the specified envelope. + /// + [HttpPost("SignatureCapture/{envelopeKey}")] + public async Task SaveSignature( + string envelopeKey, + [FromBody] SignatureCacheRequest request, + CancellationToken cancel) + { + var cacheKey = $"{SignatureCacheKeyPrefix}{envelopeKey}"; + var json = JsonSerializer.Serialize(request); + + var options = cacheOptions.Value.SignatureCacheExpiration.HasValue + ? new DistributedCacheEntryOptions { AbsoluteExpirationRelativeToNow = cacheOptions.Value.SignatureCacheExpiration.Value } + : null; + + await cache.SetStringAsync(cacheKey, json, options ?? new DistributedCacheEntryOptions(), cancel); + + return Ok(); + } + + /// + /// Retrieves a cached signature for the specified envelope. + /// + [HttpGet("SignatureCapture/{envelopeKey}")] + public async Task GetSignature(string envelopeKey, CancellationToken cancel) + { + var cacheKey = $"{SignatureCacheKeyPrefix}{envelopeKey}"; + var json = await cache.GetStringAsync(cacheKey, cancel); + + if (json is null) + return NotFound(); + + var signature = JsonSerializer.Deserialize(json); + return Ok(signature); + } + + /// + /// Deletes a cached signature for the specified envelope. + /// + [HttpDelete("SignatureCapture/{envelopeKey}")] + public async Task DeleteSignature(string envelopeKey, CancellationToken cancel) + { + var cacheKey = $"{SignatureCacheKeyPrefix}{envelopeKey}"; + await cache.RemoveAsync(cacheKey, cancel); + + return Ok(); + } +} + +/// +/// Request model for caching signature data. +/// +public sealed record SignatureCacheRequest( + string? DataUrl, + string? FullName, + string? Position, + string? Place); + + diff --git a/EnvelopeGenerator.API/Options/CacheOptions.cs b/EnvelopeGenerator.API/Options/CacheOptions.cs new file mode 100644 index 00000000..c39da3d7 --- /dev/null +++ b/EnvelopeGenerator.API/Options/CacheOptions.cs @@ -0,0 +1,18 @@ +namespace EnvelopeGenerator.API.Options; + +/// +/// Configuration options for distributed caching. +/// +public sealed class CacheOptions +{ + /// + /// Configuration section name in appsettings.json. + /// + public const string SectionName = "Cache"; + + /// + /// Signature cache expiration time. + /// If null, signatures will not expire automatically. + /// + public TimeSpan? SignatureCacheExpiration { get; set; } +} diff --git a/EnvelopeGenerator.API/Program.cs b/EnvelopeGenerator.API/Program.cs index cc306a17..b01334b3 100644 --- a/EnvelopeGenerator.API/Program.cs +++ b/EnvelopeGenerator.API/Program.cs @@ -17,6 +17,7 @@ using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.IdentityModel.Tokens; using DigitalData.Core.Abstractions.Security.Extensions; using EnvelopeGenerator.API.Middleware; +using EnvelopeGenerator.API.Options; using NLog.Web; using NLog; using DigitalData.Auth.Claims; @@ -265,6 +266,9 @@ try // Localizer builder.Services.AddCookieBasedLocalizer(); + // Cache options + builder.Services.Configure(config.GetSection(CacheOptions.SectionName)); + // Envelope generator serives #pragma warning disable CS0618 // Type or member is obsolete builder.Services diff --git a/EnvelopeGenerator.API/appsettings.json b/EnvelopeGenerator.API/appsettings.json index 25bff075..2efa9a48 100644 --- a/EnvelopeGenerator.API/appsettings.json +++ b/EnvelopeGenerator.API/appsettings.json @@ -174,6 +174,9 @@ "Receiver": [], "EmailTemplate": [ "TBSIG_EMAIL_TEMPLATE_AFT_UPD" ] }, + "Cache": { + "SignatureCacheExpiration": null + }, "MainPageTitle": null, "AnnotationParams": { "Background": {