using System.Text; using AutoMapper; using MediatR; using mws.backend.dotnet.application.Audit; using mws.backend.dotnet.application.Common; using mws.backend.dotnet.application.Permissions; using mws.backend.dotnet.domain.Documents; namespace mws.backend.dotnet.application.Documents; public record UpdateDocumentCommand(Guid UserId, Guid DocumentId, UpdateDocumentRequest Request) : IRequest, IAuditableRequest { public string Action => "Document.Update"; public string EntityType => "Document"; public Guid? EntityId => DocumentId; public string? EntityName => Request.Title; } public class UpdateDocumentHandler(IUnitOfWork uow, IMapper mapper, IDocumentContentStore contentStore) : IRequestHandler { public async Task Handle(UpdateDocumentCommand command, CancellationToken ct) { var doc = await DocumentAccess.GetDocumentForUserAsync(uow, command.UserId, command.DocumentId, ct); await DocumentAccess.EnsureDocumentPermissionAsync(uow, command.UserId, doc.ProjectId, PermissionAction.Edit, ct); var request = command.Request; if (string.IsNullOrWhiteSpace(request.Title)) { throw new BadRequestException("Title is required"); } doc.Title = request.Title.Trim(); doc.UpdatedAt = DateTime.UtcNow; doc.UpdatedBy = command.UserId; await uow.SaveChangesAsync(ct); if (doc.Type == DocumentType.Document) { var previousKey = doc.StorageKey; var bytes = Encoding.UTF8.GetBytes(request.Content ?? ""); using (var ms = new MemoryStream(bytes)) { doc.StorageKey = await contentStore.UploadAsync(doc.ProjectId, doc.Title, "text/html; charset=utf-8", ms, ct); } if (!string.IsNullOrEmpty(previousKey) && previousKey != doc.StorageKey) { await contentStore.DeleteAsync(previousKey, ct); } doc.ContentSize = bytes.Length; doc.UpdatedContentAt = DateTime.UtcNow; doc.ContentHash = null; await uow.SaveChangesAsync(ct); } var dto = mapper.Map(doc); if (doc.Type == DocumentType.Document) { dto.Content = request.Content; } return dto; } }