using DMSBLL.ESSDocument; using FastEndpoints; using GB5Shared.DTO.Framework.Enum; using GB5Shared.DTO.Framework.Login; using GB5Shared.DTO.Framework.ResponseStandard; using Newtonsoft.Json; namespace DMSSL.EndPoints.ESSDocument { /// /// Downloads the file encrypted with the employee's personal secret key. /// POST is used (not GET) so the secret key never appears in the URL or server logs. /// Returns raw binary — NOT wrapped in ResponseStandardDTO. /// public class DownloadDocumentSecure : Endpoint { public readonly IESSDocumentBLL _ESSDocumentBLL; public DownloadDocumentSecure(IESSDocumentBLL essDocumentBLL) { _ESSDocumentBLL = essDocumentBLL; } public record DownloadDocumentSecureParameters( [property: FromHeader] string Login, [property: QueryParam] int AttachmentId, [property: FromBody] string SecretKey ); public override void Configure() { Post("/ESSDocument/DownloadDocumentSecure"); AllowAnonymous(); } public override async Task HandleAsync(DownloadDocumentSecureParameters req, CancellationToken ct) { if (string.IsNullOrWhiteSpace(req.SecretKey)) { await WriteJsonResponseAsync( 400, FrameworkEnumDTO.ResponseStatus.Failed, "SecretKey is required.", ct); return; } LoginDTO? loginDTO; try { loginDTO = JsonConvert.DeserializeObject(req.Login); if (loginDTO is null) throw new Exception(); } catch { await WriteJsonResponseAsync( 401, FrameworkEnumDTO.ResponseStatus.Unauthorized, "The login token is missing or invalid. Please sign in and try again.", ct); return; } try { var (stream, mimeType, fileName) = await _ESSDocumentBLL.DownloadDocumentSecure( req.AttachmentId, req.SecretKey, loginDTO, ct); HttpContext.Response.StatusCode = 200; HttpContext.Response.ContentType = mimeType; HttpContext.Response.Headers.Append("Content-Disposition", $"attachment; filename=\"{Uri.EscapeDataString(fileName)}\""); HttpContext.Response.Headers.Append("Cache-Control", "no-store"); await using (stream) await stream.CopyToAsync(HttpContext.Response.Body, ct); } catch (UnauthorizedAccessException) { await WriteJsonResponseAsync( 403, FrameworkEnumDTO.ResponseStatus.Forbidden, "You do not have permission to access this document.", ct); } catch (FileNotFoundException) { await WriteJsonResponseAsync( 404, FrameworkEnumDTO.ResponseStatus.NotFound, "The requested document was not found.", ct); } catch (ArgumentException ex) { await WriteJsonResponseAsync( 400, FrameworkEnumDTO.ResponseStatus.Failed, ex.Message, ct); } } private Task WriteJsonResponseAsync( int httpStatus, FrameworkEnumDTO.ResponseStatus responseStatus, string message, CancellationToken ct) { var body = new ResponseStandardDTO { Status = responseStatus, Body = message, ErrorBody = message }; HttpContext.Response.StatusCode = httpStatus; HttpContext.Response.ContentType = "application/json"; return HttpContext.Response.WriteAsync(JsonConvert.SerializeObject(body), ct); } } }