using Dapper; using KArtSell.BuildingBlocks.Data; using KArtSell.Modules.IdentityAccess.ManageIdentityAndRoles.Events; using Microsoft.Extensions.Logging; using Npgsql; namespace KArtSell.Host.Consumers; /// /// Logs identity creation events to audit trail. /// Appends immutable record to audit.identity_audit_log for compliance. /// Idempotent: Upserts based on (event_id, event_type) to prevent duplicates. /// public sealed class IdentityAuditConsumer( IDbConnectionFactory connectionFactory, ILogger logger) : IInboxConsumer { public async Task HandleAsync(IdentityCreated message, CancellationToken cancellationToken = default) { try { var conn = await connectionFactory.OpenAsync(cancellationToken) as NpgsqlConnection ?? throw new InvalidOperationException("Failed to open connection"); await using (conn) { const string sql = """ INSERT INTO public.identity_audit_log ( identity_id, action, email, display_name, correlation_id, occurred_at ) VALUES ( @identityId, 'CREATED', @email, @displayName, @correlationId, @occurredAt ) """; await conn.ExecuteAsync(sql, new { identityId = message.IdentityId, email = message.Email, displayName = message.DisplayName, correlationId = message.CorrelationId, occurredAt = message.OccurredAt }); logger.LogInformation( "Identity {IdentityId} ({Email}) creation logged to audit trail (CorrelationId: {CorrelationId})", message.IdentityId, message.Email, message.CorrelationId); } } catch (Exception ex) { logger.LogError(ex, "Failed to log identity creation to audit trail for {IdentityId}", message.IdentityId); throw; } } }