using System; using System.Net; using System.Net.Http; using System.Runtime.InteropServices; using System.Security.Cryptography; using System.Text; using System.Text.Json; using System.Threading; using System.Threading.Tasks; using StreamPlayer.Desktop.Models; namespace StreamPlayer.Desktop.Services; public sealed class DeviceRegistryService { private static readonly HttpClient HttpClient = new(new HttpClientHandler { AutomaticDecompression = DecompressionMethods.All }) { Timeout = TimeSpan.FromSeconds(20) }; private readonly string _deviceId = CreateDeviceId(); public async Task SyncAsync(CancellationToken cancellationToken) { if (string.IsNullOrWhiteSpace(AppVersion.DeviceRegistryUrl)) { return DeviceStatus.Allowed(); } var payload = new { deviceId = _deviceId, deviceName = Environment.MachineName, model = RuntimeInformation.OSDescription, manufacturer = "Microsoft", osVersion = Environment.OSVersion.VersionString, appVersionName = AppVersion.VersionName, appVersionCode = AppVersion.VersionCode }; string endpoint = $"{SanitizeBaseUrl(AppVersion.DeviceRegistryUrl)}/api/devices/register"; var content = new StringContent(JsonSerializer.Serialize(payload), Encoding.UTF8, "application/json"); using var request = new HttpRequestMessage(HttpMethod.Post, endpoint) { Content = content }; using var response = await HttpClient.SendAsync(request, cancellationToken).ConfigureAwait(false); response.EnsureSuccessStatusCode(); string body = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false); using var document = JsonDocument.Parse(body); var root = document.RootElement; bool blocked = root.GetPropertyOrDefaultBool("blocked", false); string reason = root.GetPropertyOrDefault("message"); if (root.TryGetProperty("device", out var deviceElement)) { if (string.IsNullOrWhiteSpace(reason)) { reason = deviceElement.GetPropertyOrDefault("notes"); } } string tokenPart = string.Empty; if (root.TryGetProperty("verification", out var verificationElement)) { bool verificationRequired = verificationElement.GetPropertyOrDefaultBool("required", false); blocked = blocked || verificationRequired; tokenPart = verificationElement.GetPropertyOrDefault("clientTokenPart"); } return new DeviceStatus(blocked, reason, tokenPart); } private static string SanitizeBaseUrl(string value) { if (string.IsNullOrWhiteSpace(value)) { return string.Empty; } return value.EndsWith("/", StringComparison.Ordinal) ? value.TrimEnd('/') : value; } private static string CreateDeviceId() { string raw = $"{Environment.MachineName}|{Environment.UserName}|{RuntimeInformation.OSDescription}"; byte[] hash = SHA256.HashData(Encoding.UTF8.GetBytes(raw)); return Convert.ToHexString(hash)[..24].ToLowerInvariant(); } }