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": {