From ecde91ff2517defe84140f9e2a63c41f745c8e11 Mon Sep 17 00:00:00 2001 From: Kukks Date: Tue, 25 Jan 2022 11:15:15 +0100 Subject: [PATCH] Plugins: Support plugin git remote with multiple versions of same plugins --- .../Controllers/UIServerController.Plugins.cs | 4 +- BTCPayServer/Plugins/PluginService.cs | 36 +++++++++++++----- .../Views/UIServer/ListPlugins.cshtml | 37 +++++++++++++------ 3 files changed, 53 insertions(+), 24 deletions(-) diff --git a/BTCPayServer/Controllers/UIServerController.Plugins.cs b/BTCPayServer/Controllers/UIServerController.Plugins.cs index 6788b630d..c33b9c647 100644 --- a/BTCPayServer/Controllers/UIServerController.Plugins.cs +++ b/BTCPayServer/Controllers/UIServerController.Plugins.cs @@ -83,11 +83,11 @@ namespace BTCPayServer.Controllers [HttpPost("server/plugins/install")] public async Task InstallPlugin( - [FromServices] PluginService pluginService, string plugin, bool update = false) + [FromServices] PluginService pluginService, string plugin, bool update = false, string path ="") { try { - await pluginService.DownloadRemotePlugin(plugin); + await pluginService.DownloadRemotePlugin(plugin, path); if (update) { pluginService.UpdatePlugin(plugin); diff --git a/BTCPayServer/Plugins/PluginService.cs b/BTCPayServer/Plugins/PluginService.cs index a18fdd2ca..4a9589c62 100644 --- a/BTCPayServer/Plugins/PluginService.cs +++ b/BTCPayServer/Plugins/PluginService.cs @@ -33,23 +33,36 @@ namespace BTCPayServer.Plugins public IEnumerable LoadedPlugins { get; } - public async Task> GetRemotePlugins() + public async Task GetRemotePlugins(string path = "") { var resp = await _githubClient - .GetStringAsync(new Uri($"https://api.github.com/repos/{_btcPayServerOptions.PluginRemote}/contents")); + .GetStringAsync(new Uri($"https://api.github.com/repos/{_btcPayServerOptions.PluginRemote}/contents/{path}")); var files = JsonConvert.DeserializeObject(resp); - return await Task.WhenAll(files.Where(file => file.Name.EndsWith($"{PluginManager.BTCPayPluginSuffix}.json", StringComparison.InvariantCulture)).Select(async file => - { - return await _githubClient.GetStringAsync(file.DownloadUrl).ContinueWith( - task => JsonConvert.DeserializeObject(task.Result), TaskScheduler.Current); - })); - } + var dirs = files.Where(file => file.Type == "dir"); + var result = dirs.Select(file => GetRemotePlugins(file.Path)); - public async Task DownloadRemotePlugin(string plugin) + var fileTask = Task.WhenAll(files + .Where(file => file.Type == "file" && file.Name.EndsWith($"{PluginManager.BTCPayPluginSuffix}.json", + StringComparison.InvariantCulture)).Select(async file => + { + return await _githubClient.GetStringAsync(file.DownloadUrl).ContinueWith( + task => + { + var r = JsonConvert.DeserializeObject(task.Result); + r.Path = path; + return r; + }, TaskScheduler.Current); + })); + return (await Task.WhenAll( result.Concat(new[] { fileTask })).ContinueWith(task => task.Result.SelectMany(plugins => plugins))).ToArray(); + } + + + + public async Task DownloadRemotePlugin(string plugin, string path) { var dest = _dataDirectories.Value.PluginDir; var resp = await _githubClient - .GetStringAsync(new Uri($"https://api.github.com/repos/{_btcPayServerOptions.PluginRemote}/contents")); + .GetStringAsync(new Uri($"https://api.github.com/repos/{_btcPayServerOptions.PluginRemote}/contents/{path}")); var files = JsonConvert.DeserializeObject(resp); var ext = files.SingleOrDefault(file => file.Name == $"{plugin}{PluginManager.BTCPayPluginSuffix}"); if (ext is null) @@ -108,6 +121,7 @@ namespace BTCPayServer.Plugins public bool SystemPlugin { get; set; } = false; public IBTCPayServerPlugin.PluginDependency[] Dependencies { get; set; } = Array.Empty(); + public string Path { get; set; } public void Execute(IApplicationBuilder applicationBuilder, IServiceProvider applicationBuilderApplicationServices) @@ -124,6 +138,8 @@ namespace BTCPayServer.Plugins [JsonProperty("name")] public string Name { get; set; } [JsonProperty("sha")] public string Sha { get; set; } + [JsonProperty("type")] public string Type { get; set; } + [JsonProperty("path")] public string Path { get; set; } [JsonProperty("download_url")] public string DownloadUrl { get; set; } } diff --git a/BTCPayServer/Views/UIServer/ListPlugins.cshtml b/BTCPayServer/Views/UIServer/ListPlugins.cshtml index d5c496123..4948b2321 100644 --- a/BTCPayServer/Views/UIServer/ListPlugins.cshtml +++ b/BTCPayServer/Views/UIServer/ListPlugins.cshtml @@ -1,16 +1,26 @@ @using BTCPayServer.Configuration @using BTCPayServer.Abstractions.Contracts +@using BTCPayServer.Plugins @model BTCPayServer.Controllers.UIServerController.ListPluginsViewModel @inject BTCPayServerOptions BTCPayServerOptions @{ Layout = "_Layout"; ViewData.SetActivePage(ServerNavPages.Plugins); var installed = Model.Installed.ToDictionary(plugin => plugin.Identifier.ToLowerInvariant(), plugin => plugin.Version); - var availableAndNotInstalled = Model.Available + + var availableAndNotInstalledx = Model.Available .Where(plugin => !installed.ContainsKey(plugin.Identifier.ToLowerInvariant())) - .OrderBy(plugin => plugin.Identifier) + .GroupBy(plugin => plugin.Identifier) .ToList(); + var availableAndNotInstalled = new List(); + foreach (var availableAndNotInstalledItem in availableAndNotInstalledx) + { + var ordered = availableAndNotInstalledItem.OrderByDescending(plugin => plugin.Version).ToArray(); + availableAndNotInstalled.Add(ordered.FirstOrDefault(availablePlugin => DependenciesMet(availablePlugin.Dependencies)) ?? ordered.FirstOrDefault()); + } + + bool DependentOn(string plugin) { foreach (var installedPlugin in Model.Installed) @@ -125,8 +135,10 @@
@foreach (var plugin in Model.Installed) { - var matchedAvailable = Model.Available.SingleOrDefault(availablePlugin => availablePlugin.Identifier == plugin.Identifier); - var updateAvailable = !plugin.SystemPlugin && matchedAvailable != null && plugin.Version < matchedAvailable.Version; + var matchedAvailable = Model.Available.Where(availablePlugin => availablePlugin.Identifier == plugin.Identifier && availablePlugin.Version > plugin.Version).OrderByDescending(availablePlugin => availablePlugin.Version).ToArray(); + + var x = matchedAvailable.FirstOrDefault(availablePlugin => DependenciesMet(availablePlugin.Dependencies)) ?? matchedAvailable.FirstOrDefault(); + var updateAvailable = !plugin.SystemPlugin && matchedAvailable.Any(); var tabId = plugin.Identifier.ToLowerInvariant().Replace(".", "_");
@@ -142,7 +154,7 @@ else if (updateAvailable) {
- @matchedAvailable.Version available + @x.Version available
} @@ -179,12 +191,12 @@ @if (updateAvailable) {
-

@matchedAvailable.Description

- @if (matchedAvailable.Dependencies.Any()) +

@x.Description

+ @if (x.Dependencies.Any()) {
Dependencies
    - @foreach (var dependency in matchedAvailable.Dependencies) + @foreach (var dependency in x.Dependencies) {
  • @dependency @@ -203,7 +215,7 @@ @{ var pendingAction = Model.Commands.Any(tuple => tuple.plugin.Equals(plugin.Identifier, StringComparison.InvariantCultureIgnoreCase)); } - @if (!plugin.SystemPlugin && (pendingAction || (updateAvailable && DependenciesMet(matchedAvailable.Dependencies)) || !DependentOn(plugin.Identifier))) + @if (!plugin.SystemPlugin && (pendingAction || (updateAvailable && DependenciesMet(x.Dependencies)) || !DependentOn(plugin.Identifier))) {