80 lines
2.8 KiB
C#
80 lines
2.8 KiB
C#
using System;
|
|
using System.Collections.Concurrent;
|
|
using System.Threading.Tasks;
|
|
using Microsoft.AspNetCore.Authorization;
|
|
using Microsoft.AspNetCore.SignalR;
|
|
using Microsoft.Extensions.Logging;
|
|
|
|
namespace FinlyticBackend.Hubs;
|
|
|
|
/// <summary>
|
|
/// SignalR Hub streaming real-time stock prices & daily % growth updates for favorite assets every 10 seconds.
|
|
/// </summary>
|
|
[Authorize]
|
|
public class FavoritesPriceHub : Hub
|
|
{
|
|
private readonly ILogger<FavoritesPriceHub> _logger;
|
|
|
|
// Speichert thread-sicher, wie viele aktive Verbindungen ein User hat (UserId -> ConnectionCount)
|
|
private static readonly ConcurrentDictionary<string, int> ActiveUserConnections = new();
|
|
|
|
public FavoritesPriceHub(ILogger<FavoritesPriceHub> logger)
|
|
{
|
|
_logger = logger;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Liefert eine Übersicht aller aktuell mit dem Hub verbundenen User-IDs.
|
|
/// </summary>
|
|
public static string[] GetActiveUserIds() => [.. ActiveUserConnections.Keys];
|
|
|
|
public override async Task OnConnectedAsync()
|
|
{
|
|
var userId = Context.UserIdentifier;
|
|
|
|
if (!string.IsNullOrEmpty(userId))
|
|
{
|
|
// Füge die Verbindung der benutzerspezifischen Gruppe hinzu
|
|
await Groups.AddToGroupAsync(Context.ConnectionId, GetGroupName(userId));
|
|
|
|
ActiveUserConnections.AddOrUpdate(userId, 1, (_, count) => count + 1);
|
|
|
|
_logger.LogInformation("[FavoritesPriceHub] User '{UserId}' connected (ConnectionId={ConnectionId}). Active connections for user: {Count}",
|
|
userId, Context.ConnectionId, ActiveUserConnections[userId]);
|
|
}
|
|
else
|
|
{
|
|
_logger.LogWarning("[FavoritesPriceHub] Anonymous SignalR client connected without UserIdentifier: ConnectionId={ConnectionId}", Context.ConnectionId);
|
|
}
|
|
|
|
await base.OnConnectedAsync();
|
|
}
|
|
|
|
public override async Task OnDisconnectedAsync(Exception? exception)
|
|
{
|
|
var userId = Context.UserIdentifier;
|
|
|
|
if (!string.IsNullOrEmpty(userId))
|
|
{
|
|
await Groups.RemoveFromGroupAsync(Context.ConnectionId, GetGroupName(userId));
|
|
|
|
ActiveUserConnections.AddOrUpdate(userId, 0, (_, count) =>
|
|
{
|
|
var newCount = count - 1;
|
|
return newCount < 0 ? 0 : newCount;
|
|
});
|
|
|
|
// Wenn keine aktiven Verbindungen mehr bestehen, aus Dictionary entfernen
|
|
if (ActiveUserConnections.TryGetValue(userId, out var remainingCount) && remainingCount <= 0)
|
|
{
|
|
ActiveUserConnections.TryRemove(userId, out _);
|
|
}
|
|
|
|
_logger.LogInformation("[FavoritesPriceHub] User '{UserId}' disconnected (ConnectionId={ConnectionId})", userId, Context.ConnectionId);
|
|
}
|
|
|
|
await base.OnDisconnectedAsync(exception);
|
|
}
|
|
|
|
public static string GetGroupName(string userId) => $"User_{userId.Trim()}";
|
|
} |