.profile/src/ProfileManager/Services/GiteaApiClient.cs
NickiCloud c9b2a96efa
Some checks failed
Update Gitea Profile / update-profile (push) Has been cancelled
Initial profile setup
2026-09-02 20:52:52 +02:00

209 lines
8.2 KiB
C#

using System.Net.Http.Headers;
using System.Text.Json;
using ProfileManager.Models;
namespace ProfileManager.Services;
public sealed class GiteaApiClient
{
private readonly HttpClient _httpClient;
private readonly string _baseUrl;
private static readonly JsonSerializerOptions JsonOptions = new()
{
PropertyNameCaseInsensitive = true
};
public GiteaApiClient(string? baseUrl = null, string? apiToken = null)
{
_baseUrl = (baseUrl ?? Environment.GetEnvironmentVariable("GITEA_API_URL") ?? "https://git.webcore-consulting.dev/api/v1")
.TrimEnd('/');
var handler = new SocketsHttpHandler
{
PooledConnectionLifetime = TimeSpan.FromMinutes(2),
ConnectTimeout = TimeSpan.FromSeconds(10)
};
_httpClient = new HttpClient(handler)
{
Timeout = TimeSpan.FromSeconds(15)
};
_httpClient.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue("NickiProfileBot", "1.0"));
_httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var token = apiToken ?? Environment.GetEnvironmentVariable("GITEA_TOKEN");
if (!string.IsNullOrWhiteSpace(token))
{
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
}
}
public async Task<List<HeatmapItem>> GetHeatmapAsync(string username, CancellationToken cancellationToken = default)
{
var url = $"{_baseUrl}/users/{Uri.EscapeDataString(username)}/heatmap";
Console.WriteLine($"[API] Fetching heatmap from: {url}");
try
{
using var response = await _httpClient.GetAsync(url, cancellationToken);
if (response.IsSuccessStatusCode)
{
var json = await response.Content.ReadAsStringAsync(cancellationToken);
var items = JsonSerializer.Deserialize<List<HeatmapItem>>(json, JsonOptions);
if (items != null && items.Count > 0)
{
Console.WriteLine($"[API] Successfully retrieved {items.Count} heatmap data points.");
return items;
}
}
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine($"[API-WARN] Heatmap request failed (Status: {response.StatusCode}). Switching to mock fallback.");
Console.ResetColor();
}
catch (Exception ex)
{
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine($"[API-WARN] Network error connecting to Gitea ({ex.Message}). Using realistic fallback data.");
Console.ResetColor();
}
return GenerateMockHeatmap();
}
public async Task<List<GiteaRepo>> GetReposAsync(string username, CancellationToken cancellationToken = default)
{
var url = $"{_baseUrl}/users/{Uri.EscapeDataString(username)}/repos?limit=100&sort=updated";
Console.WriteLine($"[API] Fetching repositories from: {url}");
try
{
using var response = await _httpClient.GetAsync(url, cancellationToken);
if (response.IsSuccessStatusCode)
{
var json = await response.Content.ReadAsStringAsync(cancellationToken);
var repos = JsonSerializer.Deserialize<List<GiteaRepo>>(json, JsonOptions);
if (repos != null && repos.Count > 0)
{
Console.WriteLine($"[API] Successfully retrieved {repos.Count} repositories.");
return repos;
}
}
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine($"[API-WARN] Repositories request failed (Status: {response.StatusCode}). Switching to mock fallback.");
Console.ResetColor();
}
catch (Exception ex)
{
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine($"[API-WARN] Network error connecting to Gitea ({ex.Message}). Using realistic fallback data.");
Console.ResetColor();
}
return GenerateMockRepos();
}
private static List<HeatmapItem> GenerateMockHeatmap()
{
var items = new List<HeatmapItem>();
var now = DateTimeOffset.UtcNow;
var random = new Random(42); // Fixed seed for reproducible fallback
for (int i = 0; i < 365; i++)
{
var day = now.AddDays(-i);
// Simulate realistic developer habits (more activity on weekdays and active streaks)
bool isWeekend = day.DayOfWeek is DayOfWeek.Saturday or DayOfWeek.Sunday;
int chance = isWeekend ? 40 : 75;
if (random.Next(100) < chance)
{
int commits = random.Next(1, 14);
items.Add(new HeatmapItem
{
Timestamp = day.ToUnixTimeSeconds(),
Contributions = commits
});
}
}
return items;
}
private static List<GiteaRepo> GenerateMockRepos()
{
var now = DateTime.UtcNow;
return
[
new GiteaRepo
{
Name = "Markdown-Editor-Pro",
FullName = "NickiCloud/Markdown-Editor-Pro",
Description = "Moderner, performanter Markdown-Editor mit Live-Vorschau, Syntax-Highlighting & LaTeX-Support auf Avalonia UI.",
HtmlUrl = "https://git.webcore-consulting.dev/NickiCloud/Markdown-Editor-Pro",
Language = "C#",
StarsCount = 7,
ForksCount = 1,
UpdatedAt = now.AddHours(-3)
},
new GiteaRepo
{
Name = "CloudDrop",
FullName = "NickiCloud/CloudDrop",
Description = "Leichtgewichtiger, selbstgehosteter File-Sharing-Dienst mit temporären Download-Links und verschlüsselter Ablage.",
HtmlUrl = "https://git.webcore-consulting.dev/NickiCloud/CloudDrop",
Language = "C#",
StarsCount = 5,
ForksCount = 0,
UpdatedAt = now.AddDays(-2)
},
new GiteaRepo
{
Name = "KasseApp",
FullName = "NickiCloud/KasseApp",
Description = "Intuitive Kassen- und Abrechnungssoftware für Vereine und Events mit Offline-First SQLite-Datenbank.",
HtmlUrl = "https://git.webcore-consulting.dev/NickiCloud/KasseApp",
Language = "C#",
StarsCount = 4,
ForksCount = 0,
UpdatedAt = now.AddDays(-5)
},
new GiteaRepo
{
Name = "DrinkReminder",
FullName = "NickiCloud/DrinkReminder",
Description = "Smarter Tray-Assistent für Desktop & Mobile zur Erinnerung an regelmäßige Flüssigkeitszufuhr.",
HtmlUrl = "https://git.webcore-consulting.dev/NickiCloud/DrinkReminder",
Language = "C#",
StarsCount = 3,
ForksCount = 0,
UpdatedAt = now.AddDays(-12)
},
new GiteaRepo
{
Name = "Selfhosted-Infra",
FullName = "NickiCloud/Selfhosted-Infra",
Description = "Docker-Compose & Konfigurationen für Debian, Gitea, Mailcow, Nginx Reverse Proxy, UFW & Fail2ban.",
HtmlUrl = "https://git.webcore-consulting.dev/NickiCloud/Selfhosted-Infra",
Language = "Shell",
StarsCount = 6,
ForksCount = 0,
UpdatedAt = now.AddDays(-18)
},
new GiteaRepo
{
Name = "WebCore-Portal",
FullName = "NickiCloud/WebCore-Portal",
Description = "Interaktives Service-Portal mit MudBlazor Komponenten und JWT-Authentifizierung.",
HtmlUrl = "https://git.webcore-consulting.dev/NickiCloud/WebCore-Portal",
Language = "HTML",
StarsCount = 2,
ForksCount = 0,
UpdatedAt = now.AddDays(-25)
}
];
}
}