using MediatR; using mws.backend.dotnet.application.Common; using mws.backend.dotnet.domain.Audit; namespace mws.backend.dotnet.application.Audit; public class AuditLogBehavior( IUnitOfWork uow, ICurrentUser currentUser, IAuditContext auditContext) : IPipelineBehavior where TRequest : notnull { public async Task Handle(TRequest request, RequestHandlerDelegate next, CancellationToken ct) { if (request is not IAuditableRequest auditable) { return await next(); } auditContext.EntityId = null; auditContext.EntityName = null; var response = await next(); uow.ActionLogs.Add(new ActionLog { Id = Guid.NewGuid(), ActorUserId = currentUser.UserId ?? Guid.Empty, ActorUsername = TruncateRequired(currentUser.Username ?? "unknown", 100), Action = auditable.Action, EntityType = auditable.EntityType, EntityId = auditable.EntityId ?? auditContext.EntityId ?? (response as IHasEntityId)?.EntityId, EntityName = Truncate(auditable.EntityName ?? auditContext.EntityName, 255), IpAddress = currentUser.IpAddress, UserAgent = currentUser.UserAgent, CreatedAt = DateTimeOffset.UtcNow, }); await uow.SaveChangesAsync(ct); return response; } private static string TruncateRequired(string value, int max) => value.Length <= max ? value : value[..max]; private static string? Truncate(string? value, int max) => value is null || value.Length <= max ? value : value[..max]; }