using System.Text; using Microsoft.Extensions.Logging; using PatchProbe.Cli.Auth; using PatchProbe.Shared.Contracts; using PatchProbe.Shared.Models; using PatchProbe.Shared.Serialization; namespace PatchProbe.Cli.Services; internal sealed class PayloadUploader( ILogger logger, IHttpClientFactory httpClientFactory, IRequestAuthenticator authenticator) : IPayloadUploader { public async Task UploadAsync(PatchProbePayload payload, string? serverUrl = null, CancellationToken cancellationToken = default) { if (!string.IsNullOrEmpty(serverUrl)) { await PostToServerAsync(payload, serverUrl, cancellationToken); } else { await WriteLocalAsync(payload, cancellationToken); } } Task IPayloadUploader.UploadAsync(PatchProbePayload payload, CancellationToken cancellationToken) => UploadAsync(payload, null, cancellationToken); private async Task PostToServerAsync(PatchProbePayload payload, string serverUrl, CancellationToken cancellationToken) { var url = serverUrl.TrimEnd('/') + "/api/scans"; logger.LogInformation("Uploading scan to {Url}", url); var json = PayloadSerializer.Serialize(payload); using var request = new HttpRequestMessage(HttpMethod.Post, url); request.Content = new StringContent(json, Encoding.UTF8, "application/json"); await authenticator.AuthenticateAsync(request, json, cancellationToken); var client = httpClientFactory.CreateClient(); var response = await client.SendAsync(request, cancellationToken); response.EnsureSuccessStatusCode(); logger.LogInformation("Scan uploaded successfully (HTTP {Status})", (int)response.StatusCode); } private async Task WriteLocalAsync(PatchProbePayload payload, CancellationToken cancellationToken) { var outputDir = Path.Combine(AppContext.BaseDirectory, "output"); Directory.CreateDirectory(outputDir); var fileName = $"patchprobe_{payload.Collector.CollectedAt:yyyyMMdd_HHmmss}_{payload.Collector.MachineName}.json"; var filePath = Path.Combine(outputDir, fileName); await PayloadSerializer.SerializeToFileAsync(payload, filePath, cancellationToken); logger.LogInformation("Payload written to {FilePath}", filePath); } }