refactor: save current workspace state including FinlyticAnalyzer fixes, FinlyticApp trade route alignment, and DTO audit documentation

This commit is contained in:
2026-08-12 18:30:42 +02:00
parent a9553e9fbf
commit 3d8af3940b
163 changed files with 3421 additions and 1751 deletions
+52 -3
View File
@@ -1,5 +1,7 @@
using System;
using System.Collections.Concurrent;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.SignalR;
using Microsoft.Extensions.Logging;
@@ -8,24 +10,71 @@ 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()
{
_logger.LogInformation("[FavoritesPriceHub] SignalR client connected: ConnectionId={ConnectionId}", Context.ConnectionId);
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)
{
_logger.LogInformation("[FavoritesPriceHub] SignalR client disconnected: ConnectionId={ConnectionId}", Context.ConnectionId);
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()}";
}