using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using BTCPayServer.Abstractions.Contracts; using BTCPayServer.Abstractions.Extensions; using BTCPayServer.Abstractions.Models; using BTCPayServer.Configuration; using BTCPayServer.Plugins; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using static BTCPayServer.Plugins.PluginService; namespace BTCPayServer.Controllers { public partial class UIServerController { [HttpGet("server/plugins")] public async Task ListPlugins( [FromServices] PluginService pluginService, [FromServices] BTCPayServerOptions btcPayServerOptions, string search = null) { IEnumerable availablePlugins; try { availablePlugins = await pluginService.GetRemotePlugins(search); } catch (Exception) { TempData.SetStatusMessageModel(new StatusMessageModel { Severity = StatusMessageModel.StatusSeverity.Error, Message = StringLocalizer["Remote plugins lookup failed. Try again later."].Value }); availablePlugins = Array.Empty(); } var availablePluginsByIdentifier = new Dictionary(); foreach (var p in availablePlugins) availablePluginsByIdentifier.TryAdd(p.Identifier, p); var res = new ListPluginsViewModel() { Installed = pluginService.LoadedPlugins, Available = availablePlugins, Commands = pluginService.GetPendingCommands(), Disabled = pluginService.GetDisabledPlugins(), CanShowRestart = true, DownloadedPluginsByIdentifier = availablePluginsByIdentifier }; return View(res); } public class ListPluginsViewModel { public IEnumerable Installed { get; set; } public IEnumerable Available { get; set; } public (string command, string plugin)[] Commands { get; set; } public bool CanShowRestart { get; set; } public Dictionary Disabled { get; set; } public Dictionary DownloadedPluginsByIdentifier { get; set; } = new Dictionary(); } [HttpPost("server/plugins/uninstall-all")] public IActionResult UnInstallAllDisabledPlugin( [FromServices] PluginService pluginService, string plugin) { var disabled = pluginService.GetDisabledPlugins(); foreach (var d in disabled) pluginService.UninstallPlugin(d.Key); return RedirectToAction(nameof(ListPlugins)); } [HttpPost("server/plugins/uninstall")] public IActionResult UnInstallPlugin( [FromServices] PluginService pluginService, string plugin) { pluginService.UninstallPlugin(plugin); TempData.SetStatusMessageModel(new StatusMessageModel { Message = StringLocalizer["Plugin scheduled to be uninstalled."].Value, Severity = StatusMessageModel.StatusSeverity.Success }); return RedirectToAction("ListPlugins"); } [HttpPost("server/plugins/cancel")] public IActionResult CancelPluginCommands( [FromServices] PluginService pluginService, string plugin) { pluginService.CancelCommands(plugin); TempData.SetStatusMessageModel(new StatusMessageModel { Message = StringLocalizer["Plugin action cancelled."].Value, Severity = StatusMessageModel.StatusSeverity.Success }); return RedirectToAction("ListPlugins"); } [HttpPost("server/plugins/install")] public async Task InstallPlugin( [FromServices] PluginService pluginService, string plugin, bool update = false, string version = null) { try { await pluginService.DownloadRemotePlugin(plugin, version); if (update) { pluginService.UpdatePlugin(plugin); } else { pluginService.InstallPlugin(plugin); } TempData.SetStatusMessageModel(new StatusMessageModel { Message = StringLocalizer["Plugin scheduled to be installed."].Value, Severity = StatusMessageModel.StatusSeverity.Success }); } catch (Exception) { TempData.SetStatusMessageModel(new StatusMessageModel { Message = StringLocalizer["The plugin could not be downloaded. Try again later."].Value, Severity = StatusMessageModel.StatusSeverity.Error }); } return RedirectToAction("ListPlugins"); } [HttpPost("server/plugins/upload")] public async Task UploadPlugin([FromServices] PluginService pluginService, List files) { foreach (var formFile in files.Where(file => file.Length > 0).Where(file => file.FileName.IsValidFileName())) { await pluginService.UploadPlugin(formFile); pluginService.InstallPlugin(formFile.FileName.TrimEnd(PluginManager.BTCPayPluginSuffix, StringComparison.InvariantCultureIgnoreCase)); } TempData.SetStatusMessageModel(new StatusMessageModel { Message = StringLocalizer["Files uploaded, restart server to load plugins"].Value, Severity = StatusMessageModel.StatusSeverity.Success }); return RedirectToAction("ListPlugins"); } } }