using KArtSell.Modules.IdentityAccess.ManageIdentityAndRoles.Events; using Microsoft.AspNetCore.SignalR; using Microsoft.Extensions.Logging; namespace KArtSell.Host.Consumers; /// /// Pushes identity creation notifications via SignalR. /// Targets group: identity-notifications so all admins tracking new identities are notified. /// Idempotent: SignalR deduplication via idempotency key. /// public sealed class IdentityCreatedConsumer : IInboxConsumer { private readonly IHubContext? _hubContext; private readonly ILogger _logger; private static readonly Action LogNotification = LoggerMessage.Define( LogLevel.Information, new EventId(1, nameof(LogNotification)), "Identity {IdentityId} ({Email}) created notification sent"); private static readonly Action LogHubNotConfigured = LoggerMessage.Define( LogLevel.Warning, new EventId(2, nameof(LogHubNotConfigured)), "SignalR hub not configured, skipping notification"); public IdentityCreatedConsumer( IHubContext? hubContext, ILogger logger) { _hubContext = hubContext; _logger = logger; } public async Task HandleAsync(IdentityCreated message, CancellationToken cancellationToken = default) { try { LogNotification(_logger, message.IdentityId, message.Email, null); if (_hubContext == null) { LogHubNotConfigured(_logger, null); return; } var notification = new { message.IdentityId, message.Email, message.DisplayName, message.OccurredAt }; await _hubContext.Clients .Group("identity-notifications") .SendAsync("IdentityCreated", notification, cancellationToken); } catch (Exception ex) { _logger.LogError(ex, "Failed to send identity creation notification for {IdentityId}", message.IdentityId); throw; } } } /// /// SignalR hub for identity notifications. /// Clients subscribe to group: identity-notifications /// public sealed class IdentityNotificationHub : Hub { private readonly ILogger _logger; public IdentityNotificationHub(ILogger logger) { _logger = logger; } public override async Task OnConnectedAsync() { _logger.LogInformation("Client {ConnectionId} connected to IdentityNotificationHub", Context.ConnectionId); await base.OnConnectedAsync(); } public async Task SubscribeToIdentityNotifications() { await Groups.AddToGroupAsync(Context.ConnectionId, "identity-notifications"); _logger.LogInformation( "Client {ConnectionId} subscribed to identity-notifications", Context.ConnectionId); } public async Task UnsubscribeFromIdentityNotifications() { await Groups.RemoveFromGroupAsync(Context.ConnectionId, "identity-notifications"); _logger.LogInformation( "Client {ConnectionId} unsubscribed from identity-notifications", Context.ConnectionId); } }