diff --git a/EnvelopeGenerator.Server/EnvelopeGenerator.Server/Controllers/PdfRenderController.cs b/EnvelopeGenerator.Server/EnvelopeGenerator.Server/Controllers/PdfRenderController.cs index 0958384a..a85987bc 100644 --- a/EnvelopeGenerator.Server/EnvelopeGenerator.Server/Controllers/PdfRenderController.cs +++ b/EnvelopeGenerator.Server/EnvelopeGenerator.Server/Controllers/PdfRenderController.cs @@ -4,13 +4,17 @@ using EnvelopeGenerator.Application.Common.Interfaces.Doc; using EnvelopeGenerator.Domain.Constants; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Caching.Memory; +using System.Security.Cryptography; namespace EnvelopeGenerator.Server.Controllers; [ApiController] [Route("api/[controller]")] -public class PdfRenderController(IPdfPageImageRenderer renderer) : ControllerBase +public class PdfRenderController(IPdfPageImageRenderer renderer, IMemoryCache memoryCache) : ControllerBase { + private static readonly TimeSpan RenderCacheDuration = TimeSpan.FromMinutes(5); + // TODO(perf): Replace JSON/base64 transport for PdfBytes with application/octet-stream (or multipart) and stream-based request parsing to reduce payload size and allocation pressure for large PDFs. [HttpPost("pages")] [Authorize(AuthenticationSchemes = AuthScheme.Sender)] @@ -19,7 +23,19 @@ public class PdfRenderController(IPdfPageImageRenderer renderer) : ControllerBas if (request?.PdfBytes is not { Length: > 0 } bytes) return BadRequest("Missing PDF bytes."); + var cacheKey = BuildCacheKey(bytes); + if (memoryCache.TryGetValue(cacheKey, out IReadOnlyList? cachedPages) && cachedPages is not null) + return Ok(cachedPages); + IReadOnlyList pages = await renderer.RenderPagesAsImagesAsync(bytes, cancel); + memoryCache.Set(cacheKey, pages, RenderCacheDuration); + return Ok(pages); } + + private static string BuildCacheKey(byte[] pdfBytes) + { + var hash = Convert.ToHexString(SHA256.HashData(pdfBytes)); + return $"pdf-render:pages:v1:dpi110:{hash}"; + } }