namespace sh.actions.package_cleanup.Service; using System.Net.Http.Json; using Microsoft.Extensions.Logging; using sh.actions.package_cleanup.Models; public class GiteaPackageService( ILogger logger, IConfiguration configuration, HttpClient httpClient) : IGiteaPackageService { private string GetBaseUrl() => $"{configuration["URL"]?.TrimEnd('/')}/packages/{configuration["OWNER"]}"; private void AddAuthorizationHeader(HttpRequestMessage request) { var token = configuration["API_TOKEN"]; if (!string.IsNullOrEmpty(token)) { request.Headers.Add("Authorization", $"token {token}"); } } public async Task> GetPackagesByNameAsync(string name, CancellationToken cancellationToken = default) { try { var baseUrl = GetBaseUrl(); var packages = new List(); // Parse comma-separated types var types = (configuration["TYPES"] ?? "") .Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); if (types.Length == 0) { logger.LogWarning("No package types configured"); return packages; } foreach (var type in types) { var page = 1; while (true) { var url = $"{baseUrl}/{type}/{name}?page={page}"; logger.LogInformation("Fetching packages from Gitea: {Url}", url); var request = new HttpRequestMessage(HttpMethod.Get, url); AddAuthorizationHeader(request); var response = await httpClient.SendAsync(request, cancellationToken); response.EnsureSuccessStatusCode(); var fetchedPackages = await response.Content.ReadFromJsonAsync>(cancellationToken: cancellationToken) ?? []; if (fetchedPackages.Count == 0) { break; } packages.AddRange(fetchedPackages); logger.LogInformation("Fetched {Count} packages from page {Page}", fetchedPackages.Count, page); page++; } } logger.LogInformation("Successfully fetched {Count} packages for name '{Name}'", packages.Count, name); return packages; } catch (HttpRequestException ex) { logger.LogError(ex, "Error fetching packages from Gitea for name '{Name}'", name); throw; } catch (Exception ex) { logger.LogError(ex, "Unexpected error fetching packages for name '{Name}'", name); throw; } } public async Task> GetPackagesByOwnerAsync(CancellationToken cancellationToken = default) { try { var allPackages = new List(); var names = (configuration["NAMES"] ?? "") .Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); if (names.Length == 0) { logger.LogWarning("No package names configured"); return allPackages; } foreach (var name in names) { var packages = await GetPackagesByNameAsync(name, cancellationToken); allPackages.AddRange(packages); } logger.LogInformation("Successfully fetched {Count} packages in total", allPackages.Count); return allPackages; } catch (HttpRequestException ex) { logger.LogError(ex, "Error fetching packages from Gitea"); throw; } catch (Exception ex) { logger.LogError(ex, "Unexpected error fetching packages"); throw; } } public async Task DeletePackage(GiteaPackage package, CancellationToken cancellationToken = default) { try { var baseUrl = GetBaseUrl(); var url = $"{baseUrl}/{package.Type}/{package.Name}/{package.Version}"; logger.LogInformation("Deleting package {PackageName} (ID: {PackageId}) from Gitea", package.Name, package.Id); var request = new HttpRequestMessage(HttpMethod.Delete, url); AddAuthorizationHeader(request); var response = await httpClient.SendAsync(request, cancellationToken); response.EnsureSuccessStatusCode(); logger.LogInformation("Successfully deleted package {PackageName} (ID: {PackageId})", package.Name, package.Id); } catch (HttpRequestException ex) { logger.LogError(ex, "Error deleting package {PackageName} (ID: {PackageId}) from Gitea", package.Name, package.Id); throw; } catch (Exception ex) { logger.LogError(ex, "Unexpected error deleting package {PackageName} (ID: {PackageId})", package.Name, package.Id); throw; } } }