using System.IO; using System.Net.Http; using System.Text.Json; namespace ClaudeDo.Releases; public sealed class ReleaseClient : IReleaseClient { public const string DefaultApiBase = "https://git.kuns.dev/api/v1/repos/releases/ClaudeDo"; private readonly HttpClient _http; private readonly string _apiBase; public ReleaseClient(HttpClient http, string apiBase = DefaultApiBase) { _http = http; _apiBase = apiBase.TrimEnd('/'); } public async Task GetLatestReleaseAsync(CancellationToken ct) { try { using var response = await _http.GetAsync($"{_apiBase}/releases/latest", ct); if (!response.IsSuccessStatusCode) return null; var json = await response.Content.ReadAsStringAsync(ct); return ParseRelease(json); } catch (HttpRequestException) { return null; } catch (TaskCanceledException) when (!ct.IsCancellationRequested) { return null; } } public async Task DownloadAsync(string url, string destPath, IProgress progress, CancellationToken ct) { using var response = await _http.GetAsync(url, HttpCompletionOption.ResponseHeadersRead, ct); response.EnsureSuccessStatusCode(); await using var input = await response.Content.ReadAsStreamAsync(ct); await using var output = File.Create(destPath); var buffer = new byte[81920]; long total = 0; int read; while ((read = await input.ReadAsync(buffer, ct)) > 0) { await output.WriteAsync(buffer.AsMemory(0, read), ct); total += read; progress.Report(total); } } private static GiteaRelease? ParseRelease(string json) { try { using var doc = JsonDocument.Parse(json); var root = doc.RootElement; if (!root.TryGetProperty("tag_name", out var tagEl)) return null; var tag = tagEl.GetString() ?? ""; var name = root.TryGetProperty("name", out var nameEl) ? (nameEl.GetString() ?? "") : ""; var assets = new List(); if (root.TryGetProperty("assets", out var arr) && arr.ValueKind == JsonValueKind.Array) { foreach (var item in arr.EnumerateArray()) { if (!item.TryGetProperty("name", out var nameField)) continue; if (!item.TryGetProperty("browser_download_url", out var urlField)) continue; var aName = nameField.GetString() ?? ""; var aUrl = urlField.GetString() ?? ""; var aSize = item.TryGetProperty("size", out var s) ? s.GetInt64() : 0L; assets.Add(new ReleaseAsset(aName, aUrl, aSize)); } } return new GiteaRelease(tag, name, assets); } catch (JsonException) { return null; } } }