mirror of
https://github.com/btcpayserver/btcpayserver.git
synced 2025-01-18 13:26:47 +01:00
Plugins flexibility PR (#2129)
* Plugins flexibility PR * Makes the BTCPayServerOptions.LoadArgs async to support Plugin hooks and actions * relax the private set modifiers in the BTCPayNetwork * Separate IPluginHookService from PluginService to reduce dependencies on DI * fix some small bugs around image path * Fix bug with new dbreeze migration where data dir was incorrect * Update BTCPayServer/Plugins/PluginHookService.cs Co-authored-by: rockstardev <5191402+rockstardev@users.noreply.github.com> * Update BTCPayServer/Plugins/PluginHookService.cs Co-authored-by: rockstardev <5191402+rockstardev@users.noreply.github.com> Co-authored-by: rockstardev <5191402+rockstardev@users.noreply.github.com>
This commit is contained in:
parent
e2e37a0db4
commit
b8da6847b9
@ -1,6 +0,0 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Build and pack plugins" type="CompoundRunConfigurationType">
|
||||
<toRun name="Pack Test Plugin" type="DotNetProject" />
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
@ -21,7 +21,6 @@ namespace BTCPayServer.Abstractions.Models
|
||||
|
||||
public abstract string Description { get; }
|
||||
public bool SystemPlugin { get; set; }
|
||||
public bool SystemExtension { get; set; }
|
||||
public virtual IBTCPayServerPlugin.PluginDependency[] Dependencies { get; } = Array.Empty<IBTCPayServerPlugin.PluginDependency>();
|
||||
|
||||
public virtual void Execute(IApplicationBuilder applicationBuilder,
|
||||
|
@ -5,7 +5,7 @@ namespace BTCPayServer.Abstractions.Services
|
||||
{
|
||||
public abstract class PluginAction<T>:IPluginHookAction
|
||||
{
|
||||
public string Hook { get; }
|
||||
public abstract string Hook { get; }
|
||||
public Task Execute(object args)
|
||||
{
|
||||
return Execute(args is T args1 ? args1 : default);
|
||||
|
@ -5,7 +5,8 @@ namespace BTCPayServer.Abstractions.Services
|
||||
{
|
||||
public abstract class PluginHookFilter<T>:IPluginHookFilter
|
||||
{
|
||||
public string Hook { get; }
|
||||
public abstract string Hook { get; }
|
||||
|
||||
public Task<object> Execute(object args)
|
||||
{
|
||||
return Execute(args is T args1 ? args1 : default).ContinueWith(task => task.Result as object);
|
||||
|
@ -53,7 +53,7 @@ namespace BTCPayServer
|
||||
public bool SupportRBF { get; internal set; }
|
||||
public string LightningImagePath { get; set; }
|
||||
public BTCPayDefaultSettings DefaultSettings { get; set; }
|
||||
public KeyPath CoinType { get; internal set; }
|
||||
public KeyPath CoinType { get; set; }
|
||||
|
||||
public Dictionary<uint, DerivationType> ElectrumMapping = new Dictionary<uint, DerivationType>();
|
||||
|
||||
@ -132,7 +132,7 @@ namespace BTCPayServer
|
||||
{
|
||||
private string _blockExplorerLink;
|
||||
public bool ShowSyncSummary { get; set; } = true;
|
||||
public string CryptoCode { get; internal set; }
|
||||
public string CryptoCode { get; set; }
|
||||
|
||||
public string BlockExplorerLink
|
||||
{
|
||||
@ -161,7 +161,7 @@ namespace BTCPayServer
|
||||
}
|
||||
|
||||
public string CryptoImagePath { get; set; }
|
||||
public string[] DefaultRateRules { get; internal set; } = Array.Empty<string>();
|
||||
public string[] DefaultRateRules { get; set; } = Array.Empty<string>();
|
||||
public override string ToString()
|
||||
{
|
||||
return CryptoCode;
|
||||
|
@ -8,8 +8,7 @@ namespace BTCPayServer
|
||||
{
|
||||
public partial class BTCPayNetworkProvider
|
||||
{
|
||||
readonly Dictionary<string, BTCPayNetworkBase> _Networks = new Dictionary<string, BTCPayNetworkBase>();
|
||||
|
||||
protected readonly Dictionary<string, BTCPayNetworkBase> _Networks = new Dictionary<string, BTCPayNetworkBase>();
|
||||
|
||||
private readonly NBXplorerNetworkProvider _NBXplorerNetworkProvider;
|
||||
public NBXplorerNetworkProvider NBXplorerNetworkProvider
|
||||
|
@ -43,6 +43,7 @@ namespace BTCPayServer.PluginPacker
|
||||
}
|
||||
ZipFile.CreateFromDirectory(directory, outputFile + ".btcpay", CompressionLevel.Optimal, false);
|
||||
File.WriteAllText(outputFile + ".btcpay.json", json);
|
||||
Console.WriteLine($"Created {outputFile}.btcpay at {directory}");
|
||||
}
|
||||
|
||||
private static Type[] GetAllExtensionTypesFromAssembly(Assembly assembly)
|
||||
|
@ -1186,7 +1186,7 @@ namespace BTCPayServer.Controllers
|
||||
var res = paymentMethodId.PaymentType == PaymentTypes.BTCLike
|
||||
? Url.Content(network.CryptoImagePath)
|
||||
: Url.Content(network.LightningImagePath);
|
||||
return "/" + res;
|
||||
return Request.GetRelativePathOrAbsolute(res);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,7 @@ using BTCPayServer.Configuration;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.Services;
|
||||
using BTCPayServer.Services.Invoices;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace BTCPayServer.HostedServices
|
||||
{
|
||||
@ -18,9 +19,9 @@ namespace BTCPayServer.HostedServices
|
||||
private readonly InvoiceRepository _invoiceRepository;
|
||||
private readonly SettingsRepository _settingsRepository;
|
||||
private readonly ApplicationDbContextFactory _dbContextFactory;
|
||||
private readonly DataDirectories _datadirs;
|
||||
private readonly IOptions<DataDirectories> _datadirs;
|
||||
|
||||
public DbMigrationsHostedService(InvoiceRepository invoiceRepository, SettingsRepository settingsRepository, ApplicationDbContextFactory dbContextFactory, DataDirectories datadirs)
|
||||
public DbMigrationsHostedService(InvoiceRepository invoiceRepository, SettingsRepository settingsRepository, ApplicationDbContextFactory dbContextFactory, IOptions<DataDirectories> datadirs)
|
||||
{
|
||||
_invoiceRepository = invoiceRepository;
|
||||
_settingsRepository = settingsRepository;
|
||||
@ -48,7 +49,7 @@ namespace BTCPayServer.HostedServices
|
||||
private async Task MigratedInvoiceTextSearchToDb(int startFromPage)
|
||||
{
|
||||
// deleting legacy DBriize database if present
|
||||
var dbpath = Path.Combine(_datadirs.DataDir, "InvoiceDB");
|
||||
var dbpath = Path.Combine(_datadirs.Value.DataDir, "InvoiceDB");
|
||||
if (Directory.Exists(dbpath))
|
||||
{
|
||||
Directory.Delete(dbpath, true);
|
||||
|
@ -70,7 +70,6 @@ namespace BTCPayServer.Hosting
|
||||
}
|
||||
public static IServiceCollection AddBTCPayServer(this IServiceCollection services, IConfiguration configuration)
|
||||
{
|
||||
services.AddSingleton<DataDirectories>();
|
||||
services.AddSingleton<MvcNewtonsoftJsonOptions>(o => o.GetRequiredService<IOptions<MvcNewtonsoftJsonOptions>>().Value);
|
||||
services.AddDbContext<ApplicationDbContext>((provider, o) =>
|
||||
{
|
||||
@ -244,7 +243,7 @@ namespace BTCPayServer.Hosting
|
||||
|
||||
services.TryAddSingleton<AppService>();
|
||||
services.AddSingleton<PluginService>();
|
||||
services.AddSingleton<IPluginHookService>(provider => provider.GetService<PluginService>());
|
||||
services.AddSingleton<IPluginHookService, PluginHookService>();
|
||||
services.TryAddTransient<Safe>();
|
||||
services.TryAddSingleton<Ganss.XSS.HtmlSanitizer>(o =>
|
||||
{
|
||||
|
62
BTCPayServer/Plugins/PluginHookService.cs
Normal file
62
BTCPayServer/Plugins/PluginHookService.cs
Normal file
@ -0,0 +1,62 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Abstractions.Contracts;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace BTCPayServer.Plugins
|
||||
{
|
||||
public class PluginHookService : IPluginHookService
|
||||
{
|
||||
private readonly IEnumerable<IPluginHookAction> _actions;
|
||||
private readonly IEnumerable<IPluginHookFilter> _filters;
|
||||
private readonly ILogger<PluginHookService> _logger;
|
||||
|
||||
public PluginHookService(IEnumerable<IPluginHookAction> actions, IEnumerable<IPluginHookFilter> filters,
|
||||
ILogger<PluginHookService> logger)
|
||||
{
|
||||
_actions = actions;
|
||||
_filters = filters;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
// Trigger simple action hook for registered plugins
|
||||
public async Task ApplyAction(string hook, object args)
|
||||
{
|
||||
var filters = _actions
|
||||
.Where(filter => filter.Hook.Equals(hook, StringComparison.InvariantCultureIgnoreCase)).ToList();
|
||||
foreach (IPluginHookAction pluginHookFilter in filters)
|
||||
{
|
||||
try
|
||||
{
|
||||
await pluginHookFilter.Execute(args);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError(e, $"Action on hook {hook} failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Trigger hook on which registered plugins can optionally return modified args or new object back
|
||||
public async Task<object> ApplyFilter(string hook, object args)
|
||||
{
|
||||
var filters = _filters
|
||||
.Where(filter => filter.Hook.Equals(hook, StringComparison.InvariantCultureIgnoreCase)).ToList();
|
||||
foreach (IPluginHookFilter pluginHookFilter in filters)
|
||||
{
|
||||
try
|
||||
{
|
||||
args = await pluginHookFilter.Execute(args);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError(e, $"Filter on hook {hook} failed");
|
||||
}
|
||||
}
|
||||
|
||||
return args;
|
||||
}
|
||||
}
|
||||
}
|
@ -16,23 +16,19 @@ using Newtonsoft.Json;
|
||||
|
||||
namespace BTCPayServer.Plugins
|
||||
{
|
||||
public class PluginService: IPluginHookService
|
||||
public class PluginService
|
||||
{
|
||||
private readonly IOptions<DataDirectories> _datadirs;
|
||||
private readonly BTCPayServerOptions _options;
|
||||
private readonly IOptions<DataDirectories> _dataDirectories;
|
||||
private readonly BTCPayServerOptions _btcPayServerOptions;
|
||||
private readonly HttpClient _githubClient;
|
||||
private readonly IEnumerable<IPluginHookAction> _actions;
|
||||
private readonly IEnumerable<IPluginHookFilter> _filters;
|
||||
public PluginService(IEnumerable<IBTCPayServerPlugin> btcPayServerPlugins,
|
||||
IHttpClientFactory httpClientFactory, IOptions<DataDirectories> datadirs, BTCPayServerOptions options, IEnumerable<IPluginHookAction> actions, IEnumerable<IPluginHookFilter> filters)
|
||||
IHttpClientFactory httpClientFactory, BTCPayServerOptions btcPayServerOptions, IOptions<DataDirectories> dataDirectories)
|
||||
{
|
||||
LoadedPlugins = btcPayServerPlugins;
|
||||
_githubClient = httpClientFactory.CreateClient();
|
||||
_githubClient.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue("btcpayserver", "1"));
|
||||
_datadirs = datadirs;
|
||||
_options = options;
|
||||
_actions = actions;
|
||||
_filters = filters;
|
||||
_btcPayServerOptions = btcPayServerOptions;
|
||||
_dataDirectories = dataDirectories;
|
||||
}
|
||||
|
||||
public IEnumerable<IBTCPayServerPlugin> LoadedPlugins { get; }
|
||||
@ -40,7 +36,7 @@ namespace BTCPayServer.Plugins
|
||||
public async Task<IEnumerable<AvailablePlugin>> GetRemotePlugins()
|
||||
{
|
||||
var resp = await _githubClient
|
||||
.GetStringAsync(new Uri($"https://api.github.com/repos/{_options.PluginRemote}/contents"));
|
||||
.GetStringAsync(new Uri($"https://api.github.com/repos/{_btcPayServerOptions.PluginRemote}/contents"));
|
||||
var files = JsonConvert.DeserializeObject<GithubFile[]>(resp);
|
||||
return await Task.WhenAll(files.Where(file => file.Name.EndsWith($"{PluginManager.BTCPayPluginSuffix}.json", StringComparison.InvariantCulture)).Select(async file =>
|
||||
{
|
||||
@ -51,9 +47,9 @@ namespace BTCPayServer.Plugins
|
||||
|
||||
public async Task DownloadRemotePlugin(string plugin)
|
||||
{
|
||||
var dest = _datadirs.Value.PluginDir;
|
||||
var dest = _dataDirectories.Value.PluginDir;
|
||||
var resp = await _githubClient
|
||||
.GetStringAsync(new Uri($"https://api.github.com/repos/{_options.PluginRemote}/contents"));
|
||||
.GetStringAsync(new Uri($"https://api.github.com/repos/{_btcPayServerOptions.PluginRemote}/contents"));
|
||||
var files = JsonConvert.DeserializeObject<GithubFile[]>(resp);
|
||||
var ext = files.SingleOrDefault(file => file.Name == $"{plugin}{PluginManager.BTCPayPluginSuffix}");
|
||||
if (ext is null)
|
||||
@ -68,19 +64,19 @@ namespace BTCPayServer.Plugins
|
||||
|
||||
public void InstallPlugin(string plugin)
|
||||
{
|
||||
var dest = _datadirs.Value.PluginDir;
|
||||
var dest = _dataDirectories.Value.PluginDir;
|
||||
UninstallPlugin(plugin);
|
||||
PluginManager.QueueCommands(dest, ("install", plugin));
|
||||
}
|
||||
public void UpdatePlugin(string plugin)
|
||||
{
|
||||
var dest = _datadirs.Value.PluginDir;
|
||||
var dest = _dataDirectories.Value.PluginDir;
|
||||
PluginManager.QueueCommands(dest, ("update", plugin));
|
||||
}
|
||||
|
||||
public async Task UploadPlugin(IFormFile plugin)
|
||||
{
|
||||
var dest = _datadirs.Value.PluginDir;
|
||||
var dest = _dataDirectories.Value.PluginDir;
|
||||
var filedest = Path.Combine(dest, plugin.FileName);
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(filedest));
|
||||
if (Path.GetExtension(filedest) == PluginManager.BTCPayPluginSuffix)
|
||||
@ -92,7 +88,7 @@ namespace BTCPayServer.Plugins
|
||||
|
||||
public void UninstallPlugin(string plugin)
|
||||
{
|
||||
var dest = _datadirs.Value.PluginDir;
|
||||
var dest = _dataDirectories.Value.PluginDir;
|
||||
PluginManager.QueueCommands(dest, ("delete", plugin));
|
||||
}
|
||||
|
||||
@ -127,34 +123,12 @@ namespace BTCPayServer.Plugins
|
||||
|
||||
public (string command, string plugin)[] GetPendingCommands()
|
||||
{
|
||||
return PluginManager.GetPendingCommands(_datadirs.Value.PluginDir);
|
||||
return PluginManager.GetPendingCommands(_dataDirectories.Value.PluginDir);
|
||||
}
|
||||
|
||||
public void CancelCommands(string plugin)
|
||||
{
|
||||
PluginManager.CancelCommands(_datadirs.Value.PluginDir, plugin);
|
||||
}
|
||||
|
||||
public async Task ApplyAction(string hook, object args)
|
||||
{
|
||||
var filters = _actions
|
||||
.Where(filter => filter.Hook.Equals(hook, StringComparison.InvariantCultureIgnoreCase)).ToList();
|
||||
foreach (IPluginHookAction pluginHookFilter in filters)
|
||||
{
|
||||
await pluginHookFilter.Execute(args);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<object> ApplyFilter(string hook, object args)
|
||||
{
|
||||
var filters = _filters
|
||||
.Where(filter => filter.Hook.Equals(hook, StringComparison.InvariantCultureIgnoreCase)).ToList();
|
||||
foreach (IPluginHookFilter pluginHookFilter in filters)
|
||||
{
|
||||
args = await pluginHookFilter.Execute(args);
|
||||
}
|
||||
|
||||
return args;
|
||||
PluginManager.CancelCommands(_dataDirectories.Value.PluginDir, plugin);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -278,5 +278,6 @@ Global
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{545AFC8E-7BC2-43D9-84CA-F9468F4FF295} = {1FC7F660-ADF1-4D55-B61A-85C6AB083C33}
|
||||
{7DC94B25-1CFC-4170-AA41-7BA983E4C0B8} = {1FC7F660-ADF1-4D55-B61A-85C6AB083C33}
|
||||
{3E7D4925-0CA6-4A86-87BA-5BD00E5AA4A5} = {1FC7F660-ADF1-4D55-B61A-85C6AB083C33}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
Loading…
Reference in New Issue
Block a user