using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.SignalR; using Microsoft.Extensions.Logging; namespace PowderCoating.Web.Hubs; /// /// SignalR hub that delivers "StartIntake" push events to the front-desk tablet. /// Deliberately [AllowAnonymous] — the tablet runs without a logged-in user. /// Security is enforced at the kiosk route level via the KioskActivationToken cookie. /// /// On connect the tablet passes ?companyId=N in the hub URL query string; this hub /// places that connection in the company-scoped group "kiosk-{companyId}" so that /// KioskController.StartSession can push to exactly that company's tablet. /// [AllowAnonymous] public class KioskHub : Hub { private readonly ILogger _logger; /// Initialises the hub with the required logger. public KioskHub(ILogger logger) { _logger = logger; } /// /// Joins the connection to the company-scoped kiosk group on connect. /// companyId is read from the ?companyId query param embedded in the hub URL by the Welcome view. /// public override async Task OnConnectedAsync() { try { var companyId = Context.GetHttpContext()?.Request.Query["companyId"].FirstOrDefault(); if (!string.IsNullOrEmpty(companyId)) await Groups.AddToGroupAsync(Context.ConnectionId, $"kiosk-{companyId}"); } catch (Exception ex) { _logger.LogError(ex, "Error in KioskHub.OnConnectedAsync for connection {ConnectionId}", Context.ConnectionId); } await base.OnConnectedAsync(); } /// Logs unexpected disconnects (e.g. tablet going to sleep). public override async Task OnDisconnectedAsync(Exception? exception) { if (exception != null) _logger.LogWarning(exception, "KioskHub client disconnected with error: {ConnectionId}", Context.ConnectionId); await base.OnDisconnectedAsync(exception); } }