using GB5Shared.DTO.Framework.Login; using Microsoft.AspNetCore.SignalR; using Microsoft.Extensions.Logging; using System.Text.Json; namespace FrameworkSL.Hubs.DataSync { public class DataSyncHub : Hub { private readonly ILogger _logger; public DataSyncHub(ILogger logger) { _logger = logger; } // ── Connection lifecycle ────────────────────────────────────────────── public override async Task OnConnectedAsync() { _logger.LogInformation("DataSyncHub connected: {ConnectionId}", Context.ConnectionId); await base.OnConnectedAsync(); } public override async Task OnDisconnectedAsync(Exception? exception) { _logger.LogInformation("DataSyncHub disconnected: {ConnectionId}", Context.ConnectionId); if (exception is not null) _logger.LogWarning(exception, "DataSyncHub disconnected with error: {ConnectionId}", Context.ConnectionId); await base.OnDisconnectedAsync(exception); } // ── Hub methods ─────────────────────────────────────────────────────── // Client calls this to subscribe to progress for a specific run. // Group name: "datasync:{runId}" public async Task JoinRunGroup(int runId) { var group = RunGroup(runId); await Groups.AddToGroupAsync(Context.ConnectionId, group); _logger.LogInformation("Connection {ConnectionId} joined group {Group}", Context.ConnectionId, group); } public async Task LeaveRunGroup(int runId) { var group = RunGroup(runId); await Groups.RemoveFromGroupAsync(Context.ConnectionId, group); } // ── Helpers ─────────────────────────────────────────────────────────── public static string RunGroup(int runId) => $"datasync:{runId}"; private LoginDTO GetLoginDTO() { var http = Context.GetHttpContext() ?? throw new InvalidOperationException("Hub invoked outside HTTP context."); var raw = http.Request.Headers["Login"].FirstOrDefault() ?? throw new InvalidOperationException("Login header missing."); return JsonSerializer.Deserialize(raw) ?? throw new InvalidOperationException("Login header is invalid JSON."); } } }