using System.Threading; using API.Entities.Progress; using API.Entities.User; namespace API.Services.Reading; #nullable enable /// /// Provides access to client information for the current request. /// This service captures details about the client making the request including /// browser info, device type, authentication method, etc. /// public interface IClientInfoAccessor { /// /// Gets the client information for the current request. /// Returns null if called outside an HTTP request context (e.g., background jobs). /// ClientInfoData? Current { get; } string? CurrentUiFingerprint { get; } /// /// Client Device PK /// int? CurrentDeviceId { get; } } /// /// Thread-safe accessor for client information using AsyncLocal storage. /// Client info is set by middleware at the start of each request and automatically /// cleared when the request completes. /// public class ClientInfoAccessor : IClientInfoAccessor { private static readonly AsyncLocal ClientInfo = new(); private static readonly AsyncLocal UiFingerprint = new(); private static readonly AsyncLocal DeviceId = new(); public ClientInfoData? Current => ClientInfo.Value; public string? CurrentUiFingerprint => UiFingerprint.Value; public int? CurrentDeviceId => DeviceId.Value; /// /// Sets the client info for the current async context. /// Should only be called by middleware. /// internal static void SetClientInfo(ClientInfoData? info) { ClientInfo.Value = info; } /// /// Sets the client fingerprint for the current async context. /// Should only be called by middleware. /// internal static void SetUiFingerprint(string uiFingerprint) { UiFingerprint.Value = uiFingerprint; } /// /// Sets the for the current async context. /// Should only be called by middleware. /// internal static void SetDeviceId(int clientDeviceId) { DeviceId.Value = clientDeviceId; } }