using System.Net.Http.Headers; using System.Text; using System.Text.Json; namespace ScadaLink.CLI; public class ManagementHttpClient : IDisposable { private readonly HttpClient _httpClient; public ManagementHttpClient(string baseUrl, string username, string password) { _httpClient = new HttpClient { BaseAddress = new Uri(baseUrl.TrimEnd('/') + "/") }; var credentials = Convert.ToBase64String(Encoding.UTF8.GetBytes($"{username}:{password}")); _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", credentials); } public async Task SendCommandAsync(string commandName, object payload, TimeSpan timeout) { using var cts = new CancellationTokenSource(timeout); var body = JsonSerializer.Serialize(new { command = commandName, payload }, new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }); var content = new StringContent(body, Encoding.UTF8, "application/json"); HttpResponseMessage httpResponse; try { httpResponse = await _httpClient.PostAsync("management", content, cts.Token); } catch (TaskCanceledException) { return new ManagementResponse(504, null, "Request timed out.", "TIMEOUT"); } catch (HttpRequestException ex) { return new ManagementResponse(0, null, $"Connection failed: {ex.Message}", "CONNECTION_FAILED"); } var responseBody = await httpResponse.Content.ReadAsStringAsync(cts.Token); if (httpResponse.IsSuccessStatusCode) { return new ManagementResponse((int)httpResponse.StatusCode, responseBody, null, null); } // Parse error response string? error = null; string? code = null; try { using var doc = JsonDocument.Parse(responseBody); error = doc.RootElement.TryGetProperty("error", out var e) ? e.GetString() : responseBody; code = doc.RootElement.TryGetProperty("code", out var c) ? c.GetString() : null; } catch { error = responseBody; } return new ManagementResponse((int)httpResponse.StatusCode, null, error, code); } public void Dispose() => _httpClient.Dispose(); } public record ManagementResponse(int StatusCode, string? JsonData, string? Error, string? ErrorCode);