mirror of
https://github.com/aljazceru/btcpayserver.git
synced 2025-12-17 05:54:26 +01:00
Merge pull request #6896 from btcpayserver/fix/unlisted-plugins-updates
fix: ensure unlisted installed plugins appear as updatable
This commit is contained in:
@@ -26,12 +26,12 @@ namespace BTCPayServer.Controllers
|
||||
{
|
||||
availablePlugins = await pluginService.GetRemotePlugins(search);
|
||||
}
|
||||
catch (Exception)
|
||||
catch (Exception ex)
|
||||
{
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Severity = StatusMessageModel.StatusSeverity.Error,
|
||||
Message = StringLocalizer["Remote plugins lookup failed. Try again later."].Value
|
||||
Message = StringLocalizer["Remote plugins lookup failed. Try again later. Error: {0}", ex.Message].Value
|
||||
});
|
||||
availablePlugins = Array.Empty<PluginService.AvailablePlugin>();
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
@@ -46,32 +47,35 @@ namespace BTCPayServer.Plugins
|
||||
public JObject ManifestInfo { get; set; }
|
||||
public string Documentation { get; set; }
|
||||
}
|
||||
|
||||
public record InstalledPluginRequest(string Identifier, string Version);
|
||||
|
||||
public class PluginBuilderClient
|
||||
{
|
||||
HttpClient httpClient;
|
||||
public HttpClient HttpClient => httpClient;
|
||||
private readonly HttpClient _httpClient;
|
||||
public HttpClient HttpClient => _httpClient;
|
||||
public PluginBuilderClient(HttpClient httpClient)
|
||||
{
|
||||
this.httpClient = httpClient;
|
||||
_httpClient = httpClient;
|
||||
}
|
||||
static JsonSerializerSettings serializerSettings = new() { ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver() };
|
||||
public async Task<PublishedVersion[]> GetPublishedVersions(string btcpayVersion, bool includePreRelease, string searchPluginName = null, bool? includeAllVersions = null)
|
||||
{
|
||||
var queryString = $"?includePreRelease={includePreRelease}";
|
||||
if (btcpayVersion is not null)
|
||||
queryString += $"&btcpayVersion={btcpayVersion}";
|
||||
queryString += $"&btcpayVersion={Uri.EscapeDataString(btcpayVersion)}";
|
||||
if (searchPluginName is not null)
|
||||
queryString += $"&searchPluginName={searchPluginName}";
|
||||
queryString += $"&searchPluginName={Uri.EscapeDataString(searchPluginName)}";
|
||||
if (includeAllVersions is not null)
|
||||
queryString += $"&includeAllVersions={includeAllVersions}";
|
||||
var result = await httpClient.GetStringAsync($"api/v1/plugins{queryString}");
|
||||
var result = await _httpClient.GetStringAsync($"api/v1/plugins{queryString}");
|
||||
return JsonConvert.DeserializeObject<PublishedVersion[]>(result, serializerSettings) ?? throw new InvalidOperationException();
|
||||
}
|
||||
public async Task<PublishedVersion> GetPlugin(string pluginSlug, string version)
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = await httpClient.GetStringAsync($"api/v1/plugins/{pluginSlug}/versions/{version}");
|
||||
var result = await _httpClient.GetStringAsync($"api/v1/plugins/{pluginSlug}/versions/{version}");
|
||||
return JsonConvert.DeserializeObject<PublishedVersion>(result, serializerSettings);
|
||||
}
|
||||
catch (HttpRequestException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound)
|
||||
@@ -85,9 +89,33 @@ namespace BTCPayServer.Plugins
|
||||
var queryString = $"?btcpayVersion={btcpayVersion}&includePreRelease={includePreRelease}&includeAllVersions={includeAllVersions}";
|
||||
var url = $"api/v1/plugins/{identifier}{queryString}";
|
||||
|
||||
var result = await httpClient.GetStringAsync(url);
|
||||
var result = await _httpClient.GetStringAsync(url);
|
||||
return JsonConvert.DeserializeObject<PublishedVersion[]>(result, serializerSettings)
|
||||
?? throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
public async Task<PublishedVersion[]> GetInstalledPluginsUpdates(
|
||||
string btcpayVersion,
|
||||
bool includePreRelease,
|
||||
IEnumerable<InstalledPluginRequest> plugins)
|
||||
{
|
||||
var queryString = $"?includePreRelease={includePreRelease}";
|
||||
if (!string.IsNullOrWhiteSpace(btcpayVersion))
|
||||
queryString += $"&btcpayVersion={Uri.EscapeDataString(btcpayVersion)}";
|
||||
|
||||
var json = JsonConvert.SerializeObject(plugins, serializerSettings);
|
||||
using var content = new StringContent(json, Encoding.UTF8, "application/json");
|
||||
|
||||
using var resp = await _httpClient.PostAsync($"api/v1/plugins/updates{queryString}", content);
|
||||
resp.EnsureSuccessStatusCode();
|
||||
|
||||
var body = await resp.Content.ReadAsStringAsync();
|
||||
var result = JsonConvert.DeserializeObject<PublishedVersion[]>(body, serializerSettings);
|
||||
|
||||
if (result is null)
|
||||
throw new JsonException("Plugin updates response deserialized to null.");
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,12 +20,14 @@ namespace BTCPayServer.Plugins
|
||||
private readonly IOptions<DataDirectories> _dataDirectories;
|
||||
private readonly PoliciesSettings _policiesSettings;
|
||||
private readonly PluginBuilderClient _pluginBuilderClient;
|
||||
|
||||
public PluginService(
|
||||
IEnumerable<IBTCPayServerPlugin> btcPayServerPlugins,
|
||||
PluginBuilderClient pluginBuilderClient,
|
||||
IOptions<DataDirectories> dataDirectories,
|
||||
PoliciesSettings policiesSettings,
|
||||
BTCPayServerEnvironment env)
|
||||
BTCPayServerEnvironment env
|
||||
)
|
||||
{
|
||||
LoadedPlugins = btcPayServerPlugins;
|
||||
Installed = btcPayServerPlugins.ToDictionary(p => p.Identifier, p => p.Version, StringComparer.OrdinalIgnoreCase);
|
||||
@@ -49,32 +51,77 @@ namespace BTCPayServer.Plugins
|
||||
return pluginManifest.Version;
|
||||
}
|
||||
|
||||
private string GetShortBtcpayVersion() => Env.Version.TrimStart('v').Split('+')[0];
|
||||
|
||||
public async Task<AvailablePlugin[]> GetRemotePlugins(string searchPluginName)
|
||||
{
|
||||
string btcpayVersion = Env.Version.TrimStart('v').Split('+')[0];
|
||||
string btcpayVersion = GetShortBtcpayVersion();
|
||||
var versions = await _pluginBuilderClient.GetPublishedVersions(
|
||||
btcpayVersion, _policiesSettings.PluginPreReleases, searchPluginName);
|
||||
return versions.Select(v =>
|
||||
|
||||
var plugins = versions
|
||||
.Select(MapToAvailablePlugin)
|
||||
.Where(p => p is not null)
|
||||
.Select(p => p!)
|
||||
.ToList();
|
||||
|
||||
var listedIds = new HashSet<string>(
|
||||
plugins.Select(p => p.Identifier),
|
||||
StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
var loadedToCheck = LoadedPlugins
|
||||
.Where(p => !p.SystemPlugin && !listedIds.Contains(p.Identifier))
|
||||
.Select(p => new InstalledPluginRequest(p.Identifier, p.Version.ToString()))
|
||||
.ToList();
|
||||
|
||||
if (loadedToCheck.Count <= 0) return plugins.ToArray();
|
||||
|
||||
var updates = await _pluginBuilderClient.GetInstalledPluginsUpdates(
|
||||
btcpayVersion,
|
||||
_policiesSettings.PluginPreReleases,
|
||||
loadedToCheck);
|
||||
|
||||
if (updates is { Length: > 0 })
|
||||
{
|
||||
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;
|
||||
}).ToArray();
|
||||
plugins.AddRange(
|
||||
updates.Select(MapToAvailablePlugin)
|
||||
.Where(p => p is not null)
|
||||
.Select(p => p!)
|
||||
);
|
||||
}
|
||||
|
||||
return plugins.ToArray();
|
||||
}
|
||||
|
||||
#nullable enable
|
||||
private AvailablePlugin? MapToAvailablePlugin(PublishedVersion publishedVersion)
|
||||
{
|
||||
if (publishedVersion.ManifestInfo is null)
|
||||
return null;
|
||||
|
||||
var availablePlugin = publishedVersion.ManifestInfo.ToObject<AvailablePlugin>();
|
||||
if (availablePlugin is null)
|
||||
throw new InvalidDataException($"Manifest deserialized to null BuildId: {publishedVersion.BuildId} PluginSlug: {publishedVersion.ProjectSlug}");
|
||||
|
||||
availablePlugin.Documentation = publishedVersion.Documentation;
|
||||
var buildInfo = publishedVersion.BuildInfo;
|
||||
var github = buildInfo?.GetGithubRepository();
|
||||
if (buildInfo is not null && github is not null)
|
||||
{
|
||||
availablePlugin.Source = github.GetSourceUrl(buildInfo.gitCommit, buildInfo.pluginDir);
|
||||
availablePlugin.Author = github.Owner;
|
||||
availablePlugin.AuthorLink = $"https://github.com/{github.Owner}";
|
||||
}
|
||||
availablePlugin.SystemPlugin = false;
|
||||
return availablePlugin;
|
||||
}
|
||||
#nullable restore
|
||||
|
||||
public async Task<AvailablePlugin> DownloadRemotePlugin(string pluginIdentifier, string version, VersionCondition condition = null)
|
||||
{
|
||||
if (version is null)
|
||||
{
|
||||
string btcpayVersion = Env.Version.TrimStart('v').Split('+')[0];
|
||||
string btcpayVersion = GetShortBtcpayVersion();
|
||||
var versions = await _pluginBuilderClient.GetPluginVersionsForDownload(pluginIdentifier,
|
||||
btcpayVersion, _policiesSettings.PluginPreReleases, includeAllVersions: true);
|
||||
var potentialVersions = versions
|
||||
|
||||
Reference in New Issue
Block a user