using System.Text.Json.Nodes; using System.Text.RegularExpressions; using Microsoft.Extensions.Options; using Moonlight.Api.Configuration; using Moonlight.Api.Models; namespace Moonlight.Api.Services; public partial class VersionService { private readonly IOptions Options; private readonly IHttpClientFactory HttpClientFactory; private const string VersionPath = "/app/version"; private const string GiteaServer = "https://git.battlestati.one"; private const string GiteaRepository = "Moonlight-Panel/Moonlight"; [GeneratedRegex(@"^v(?!1(\.|$))\d+\.[A-Za-z0-9]+(\.[A-Za-z0-9]+)*$")] private static partial Regex RegexFilter(); public VersionService( IOptions options, IHttpClientFactory httpClientFactory ) { Options = options; HttpClientFactory = httpClientFactory; } public async Task GetVersionsAsync() { if (Options.Value.OfflineMode) return []; var versions = new List(); var httpClient = HttpClientFactory.CreateClient(); // Tags const string tagsPath = $"{GiteaServer}/api/v1/repos/{GiteaRepository}/tags"; await using var tagsJsonStream = await httpClient.GetStreamAsync(tagsPath); var tagsJson = await JsonNode.ParseAsync(tagsJsonStream); if (tagsJson != null) { foreach (var node in tagsJson.AsArray()) { if (node == null) continue; var name = node["name"]?.GetValue() ?? "N/A"; var createdAt = node["createdAt"]?.GetValue() ?? DateTimeOffset.MinValue; if(!RegexFilter().IsMatch(name)) continue; versions.Add(new MoonlightVersion( name, name.EndsWith('b'), false, createdAt )); } } // Branches const string branchesPath = $"{GiteaServer}/api/v1/repos/{GiteaRepository}/branches"; await using var branchesJsonStream = await httpClient.GetStreamAsync(branchesPath); var branchesJson = await JsonNode.ParseAsync(branchesJsonStream); if (branchesJson != null) { foreach (var node in branchesJson.AsArray()) { if (node == null) continue; var name = node["name"]?.GetValue() ?? "N/A"; var commit = node["commit"]; if (commit == null) continue; var createdAt = commit["timestamp"]?.GetValue() ?? DateTimeOffset.MinValue; if(!RegexFilter().IsMatch(name)) continue; versions.Add(new MoonlightVersion( name, name.EndsWith('b'), true, createdAt )); } } return versions.ToArray(); } public async Task GetInstanceVersionAsync() { var knownVersions = await GetVersionsAsync(); string versionIdentifier; if (!string.IsNullOrWhiteSpace(Options.Value.CurrentOverride)) versionIdentifier = Options.Value.CurrentOverride; else { if (File.Exists(VersionPath)) versionIdentifier = await File.ReadAllTextAsync(VersionPath); else versionIdentifier = "unknown"; } var version = knownVersions.FirstOrDefault(x => x.Identifier == versionIdentifier); if (version != null) return version; return new MoonlightVersion( versionIdentifier, false, false, DateTimeOffset.UtcNow ); } public async Task GetLatestVersionAsync() { var versions = await GetVersionsAsync(); return versions .Where(x => x is { IsDevelopment: false, IsPreRelease: false }) .OrderByDescending(x => x.CreatedAt) .FirstOrDefault(); } }