mirror of
https://github.com/aljazceru/btcpayserver.git
synced 2025-12-18 14:34:23 +01:00
refactor: add parallel checks and DRY mapper
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Abstractions.Contracts;
|
||||
using BTCPayServer.Configuration;
|
||||
@@ -9,6 +11,7 @@ using BTCPayServer.Services;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
@@ -20,6 +23,7 @@ namespace BTCPayServer.Plugins
|
||||
private readonly IOptions<DataDirectories> _dataDirectories;
|
||||
private readonly PoliciesSettings _policiesSettings;
|
||||
private readonly PluginBuilderClient _pluginBuilderClient;
|
||||
private readonly ILogger<PluginService> _logger;
|
||||
private static readonly HashSet<string> _builtInPluginIdentifiers = new(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
"BTCPayServer",
|
||||
@@ -37,7 +41,9 @@ namespace BTCPayServer.Plugins
|
||||
PluginBuilderClient pluginBuilderClient,
|
||||
IOptions<DataDirectories> dataDirectories,
|
||||
PoliciesSettings policiesSettings,
|
||||
BTCPayServerEnvironment env)
|
||||
BTCPayServerEnvironment env,
|
||||
ILogger<PluginService> logger
|
||||
)
|
||||
{
|
||||
LoadedPlugins = btcPayServerPlugins;
|
||||
Installed = btcPayServerPlugins.ToDictionary(p => p.Identifier, p => p.Version, StringComparer.OrdinalIgnoreCase);
|
||||
@@ -45,6 +51,7 @@ namespace BTCPayServer.Plugins
|
||||
_dataDirectories = dataDirectories;
|
||||
_policiesSettings = policiesSettings;
|
||||
Env = env;
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
public Dictionary<string, Version> Installed { get; set; }
|
||||
@@ -67,20 +74,10 @@ namespace BTCPayServer.Plugins
|
||||
var versions = await _pluginBuilderClient.GetPublishedVersions(
|
||||
btcpayVersion, _policiesSettings.PluginPreReleases, searchPluginName);
|
||||
|
||||
var plugins = versions.Select(v =>
|
||||
{
|
||||
var p = v.ManifestInfo.ToObject<AvailablePlugin>();
|
||||
p.Documentation = v.Documentation;
|
||||
var github = v.BuildInfo.GetGithubRepository();
|
||||
if (github != null)
|
||||
{
|
||||
p.Source = github.GetSourceUrl(v.BuildInfo.gitCommit, v.BuildInfo.pluginDir);
|
||||
p.Author = github.Owner;
|
||||
p.AuthorLink = $"https://github.com/{github.Owner}";
|
||||
}
|
||||
p.SystemPlugin = false;
|
||||
return p;
|
||||
}).ToList();
|
||||
var plugins = versions
|
||||
.Select(MapToAvailablePlugin)
|
||||
.Where(p => p is not null)
|
||||
.ToList()!;
|
||||
|
||||
var unlistedUpdates = await GetUpdatesForUnlistedInstalledAsync(plugins, btcpayVersion);
|
||||
plugins.AddRange(unlistedUpdates);
|
||||
@@ -89,72 +86,101 @@ namespace BTCPayServer.Plugins
|
||||
}
|
||||
|
||||
private async Task<List<AvailablePlugin>> GetUpdatesForUnlistedInstalledAsync(
|
||||
List<AvailablePlugin> publishedPlugins,
|
||||
string btcpayVersion)
|
||||
List<AvailablePlugin> listedPlugins,
|
||||
string btcpayVersion,
|
||||
CancellationToken ct = default)
|
||||
{
|
||||
var updatables = new List<AvailablePlugin>();
|
||||
var presentIdentifiers = new HashSet<string>(
|
||||
publishedPlugins.Select(p => p.Identifier),
|
||||
var listedIdentifiers = new HashSet<string>(
|
||||
listedPlugins.Select(p => p.Identifier),
|
||||
StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
foreach (var (installedIdentifier, installedVersion) in Installed)
|
||||
var installedToCheck = Installed
|
||||
.Where(installedPlugin => !_builtInPluginIdentifiers.Contains(installedPlugin.Key) &&
|
||||
!listedIdentifiers.Contains(installedPlugin.Key))
|
||||
.ToList();
|
||||
|
||||
var results = new ConcurrentBag<AvailablePlugin>();
|
||||
|
||||
var parallelOpts = new ParallelOptions
|
||||
{
|
||||
if (_builtInPluginIdentifiers.Contains(installedIdentifier))
|
||||
continue;
|
||||
MaxDegreeOfParallelism = Math.Min(6, Environment.ProcessorCount),
|
||||
CancellationToken = ct
|
||||
};
|
||||
|
||||
if (presentIdentifiers.Contains(installedIdentifier))
|
||||
continue;
|
||||
await Parallel.ForEachAsync(installedToCheck, parallelOpts, async (installedPlugin, ct2) =>
|
||||
{
|
||||
var (installedIdentifier, installedVersion) = installedPlugin;
|
||||
|
||||
var publishedVersions = await _pluginBuilderClient.GetPluginVersionsForDownload(
|
||||
installedIdentifier,
|
||||
btcpayVersion,
|
||||
_policiesSettings.PluginPreReleases,
|
||||
includeAllVersions: true);
|
||||
|
||||
if (publishedVersions == null || !publishedVersions.Any())
|
||||
continue;
|
||||
|
||||
var candidates = publishedVersions
|
||||
.Select(versionInfo => new
|
||||
{
|
||||
VersionInfo = versionInfo,
|
||||
Available = versionInfo.ManifestInfo?.ToObject<AvailablePlugin>()
|
||||
})
|
||||
.Where(candidate => candidate.Available != null &&
|
||||
candidate.Available.Identifier.Equals(installedIdentifier, StringComparison.OrdinalIgnoreCase))
|
||||
.ToList();
|
||||
|
||||
if (candidates.Count == 0)
|
||||
continue;
|
||||
|
||||
var latestCandidate = candidates
|
||||
.OrderByDescending(candidate => candidate.Available!.Version)
|
||||
.First();
|
||||
|
||||
var latestAvailable = latestCandidate.Available!;
|
||||
|
||||
|
||||
if (latestAvailable.Version <= installedVersion)
|
||||
continue;
|
||||
|
||||
latestAvailable.Documentation = latestCandidate.VersionInfo.Documentation;
|
||||
|
||||
var githubRepository = latestCandidate.VersionInfo.BuildInfo.GetGithubRepository();
|
||||
if (githubRepository != null)
|
||||
try
|
||||
{
|
||||
latestAvailable.Source = githubRepository.GetSourceUrl(
|
||||
latestCandidate.VersionInfo.BuildInfo.gitCommit,
|
||||
latestCandidate.VersionInfo.BuildInfo.pluginDir);
|
||||
latestAvailable.Author = githubRepository.Owner;
|
||||
latestAvailable.AuthorLink = $"https://github.com/{githubRepository.Owner}";
|
||||
var publishedVersions = await _pluginBuilderClient.GetPluginVersionsForDownload(
|
||||
installedIdentifier,
|
||||
btcpayVersion,
|
||||
_policiesSettings.PluginPreReleases,
|
||||
includeAllVersions: true);
|
||||
|
||||
if (publishedVersions == null || !publishedVersions.Any())
|
||||
return;
|
||||
|
||||
var latestCandidate = publishedVersions
|
||||
.Select(publishedVersion => (VersionInfo: publishedVersion, Manifest: publishedVersion.ManifestInfo))
|
||||
.Where(versionTuple => versionTuple.Manifest != null)
|
||||
.Select(versionTuple =>
|
||||
{
|
||||
var identifier = versionTuple.Manifest!["Identifier"]?.ToString();
|
||||
var parsedVersion = Version.TryParse(versionTuple.Manifest!["Version"]?.ToString(), out var ver) ? ver : null;
|
||||
|
||||
return new { versionTuple.VersionInfo, Identifier = identifier, Version = parsedVersion };
|
||||
})
|
||||
.Where(candidate => candidate.Identifier != null &&
|
||||
candidate.Identifier.Equals(installedIdentifier, StringComparison.OrdinalIgnoreCase) &&
|
||||
candidate.Version is not null)
|
||||
.OrderByDescending(candidate => candidate.Version)
|
||||
.FirstOrDefault();
|
||||
|
||||
if (latestCandidate == null)
|
||||
return;
|
||||
|
||||
var latestAvailable = MapToAvailablePlugin(latestCandidate.VersionInfo);
|
||||
|
||||
if (latestAvailable is null)
|
||||
return;
|
||||
|
||||
if (latestAvailable.Version <= installedVersion)
|
||||
return;
|
||||
|
||||
results.Add(latestAvailable);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning(ex,
|
||||
"Error while checking for updates for installed plugin {InstalledPluginIdentifier}",
|
||||
installedIdentifier);
|
||||
}
|
||||
});
|
||||
|
||||
latestAvailable.SystemPlugin = false;
|
||||
return results.ToList();
|
||||
}
|
||||
|
||||
updatables.Add(latestAvailable);
|
||||
private AvailablePlugin MapToAvailablePlugin(PublishedVersion publishedVersion)
|
||||
{
|
||||
var availablePlugin = publishedVersion.ManifestInfo.ToObject<AvailablePlugin>();
|
||||
if (availablePlugin is null)
|
||||
{
|
||||
_logger.LogWarning("ManifestInfo missing for published version {Version}", publishedVersion.ToString());
|
||||
return null;
|
||||
}
|
||||
|
||||
return updatables;
|
||||
availablePlugin.Documentation = publishedVersion.Documentation;
|
||||
var github = publishedVersion.BuildInfo?.GetGithubRepository();
|
||||
if (github != null)
|
||||
{
|
||||
availablePlugin.Source = github.GetSourceUrl(publishedVersion.BuildInfo.gitCommit, publishedVersion.BuildInfo.pluginDir);
|
||||
availablePlugin.Author = github.Owner;
|
||||
availablePlugin.AuthorLink = $"https://github.com/{github.Owner}";
|
||||
}
|
||||
availablePlugin.SystemPlugin = false;
|
||||
return availablePlugin;
|
||||
}
|
||||
|
||||
public async Task<AvailablePlugin> DownloadRemotePlugin(string pluginIdentifier, string version, VersionCondition condition = null)
|
||||
|
||||
Reference in New Issue
Block a user