This commit is contained in:
Kukks
2023-11-02 08:19:31 +01:00
parent cfa7efd11b
commit 74c6931e8c
10 changed files with 266 additions and 159 deletions

View File

@@ -4,7 +4,6 @@ using System.Linq;
using System.Threading.Tasks;
using BTCPayServer.Abstractions.Constants;
using BTCPayServer.Client;
using BTCPayServer.Plugins.FixedFloat;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

View File

@@ -4,10 +4,11 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Breez.Sdk;
using BTCPayServer.Lightning;
using NBitcoin;
using Network = Breez.Sdk.Network;
namespace BTCPayServer.Lightning.Breez;
namespace BTCPayServer.Plugins.Breez;
public class BreezLightningClient: ILightningClient, IDisposable, EventListener
@@ -22,7 +23,7 @@ public class BreezLightningClient: ILightningClient, IDisposable, EventListener
new GreenlightNodeConfig(null, inviteCode)
);
var config = BreezSdkMethods.DefaultConfig(
EnvironmentType.PRODUCTION,
network ==NBitcoin.Network.Main ? EnvironmentType.PRODUCTION: EnvironmentType.STAGING,
apiKey,
nodeConfig
) with {

View File

@@ -1,10 +1,7 @@
using System;
using System.Linq;
using System.Net.Http;
using BTCPayServer.Plugins.FixedFloat;
using BTCPayServer.Lightning;
using NBitcoin;
namespace BTCPayServer.Lightning.Breez;
namespace BTCPayServer.Plugins.Breez;
public class BreezLightningConnectionStringHandler : ILightningConnectionStringHandler
{

View File

@@ -0,0 +1,33 @@
#nullable enable
using BTCPayServer.Abstractions.Contracts;
using BTCPayServer.Abstractions.Models;
using BTCPayServer.Abstractions.Services;
using BTCPayServer.Lightning;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace BTCPayServer.Plugins.Breez
{
public class BreezPlugin : BaseBTCPayServerPlugin
{
public override IBTCPayServerPlugin.PluginDependency[] Dependencies { get; } =
{
new() {Identifier = nameof(BTCPayServer), Condition = ">=1.12.0"}
};
public override void Execute(IServiceCollection applicationBuilder)
{
applicationBuilder.AddSingleton<BreezService>();
applicationBuilder.AddSingleton<IHostedService>(provider => provider.GetRequiredService<BreezService>());
applicationBuilder.AddSingleton<BreezLightningConnectionStringHandler>();
applicationBuilder.AddSingleton<ILightningConnectionStringHandler>(provider => provider.GetRequiredService<BreezLightningConnectionStringHandler>());
applicationBuilder.AddSingleton<IUIExtension>(new UIExtension("Breez/BreezNav",
"store-integrations-nav"));
applicationBuilder.AddSingleton<IUIExtension>(new UIExtension("Breez/LNPaymentMethodSetupTabhead", "ln-payment-method-setup-tabhead"));
applicationBuilder.AddSingleton<IUIExtension>(new UIExtension("Breez/LNPaymentMethodSetupTab", "ln-payment-method-setup-tab"));
base.Execute(applicationBuilder);
}
}
}

View File

@@ -0,0 +1,130 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using BTCPayServer.Configuration;
using BTCPayServer.Services.Stores;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using NBitcoin;
namespace BTCPayServer.Plugins.Breez;
public class BreezService:IHostedService
{
private readonly StoreRepository _storeRepository;
private readonly IOptions<DataDirectories> _dataDirectories;
private readonly BTCPayNetworkProvider _btcPayNetworkProvider;
private readonly ILogger _logger;
private Dictionary<string, BreezSettings> _settings;
private Dictionary<string, BreezLightningClient> _clients = new();
public BreezService(StoreRepository storeRepository,
IOptions<DataDirectories> dataDirectories,
BTCPayNetworkProvider btcPayNetworkProvider,
ILogger<BreezService> logger)
{
_storeRepository = storeRepository;
_dataDirectories = dataDirectories;
_btcPayNetworkProvider = btcPayNetworkProvider;
_logger = logger;
}
private string GetWorkDir()
{
var dir = _dataDirectories.Value.DataDir;
return Path.Combine(dir, "Plugins", "Breez");
}
TaskCompletionSource tcs = new();
public async Task StartAsync(CancellationToken cancellationToken)
{
_settings = (await _storeRepository.GetSettingsAsync<BreezSettings>("Breez")).Where(pair => pair.Value is not null).ToDictionary(pair => pair.Key, pair => pair.Value!);
foreach (var keyValuePair in _settings)
{
try
{
await Handle(keyValuePair.Key, keyValuePair.Value);
}
catch (Exception e)
{
}
}
tcs.TrySetResult();
}
public async Task<BreezSettings?> Get(string storeId)
{
await tcs.Task;
_settings.TryGetValue(storeId, out var settings);
return settings;
}
public async Task<BreezLightningClient?> Handle(string? storeId, BreezSettings? settings)
{
if (settings is null)
{
if (storeId is not null && _clients.Remove(storeId, out var client))
{
client.Dispose();
}
}
else
{
try
{
var network = Network.Main; // _btcPayNetworkProvider.BTC.NBitcoinNetwork;
Directory.CreateDirectory(GetWorkDir());
var client = new BreezLightningClient(settings.InviteCode, settings.ApiKey, GetWorkDir(),
network, settings.Mnemonic);
if (storeId is not null)
{
_clients.AddOrReplace(storeId, client);
}
return client;
}
catch (Exception e)
{
_logger.LogError(e, "Could not create Breez client");
throw;
}
}
return null;
}
public async Task Set(string storeId, BreezSettings? settings)
{
var result = await Handle(storeId, settings);
await _storeRepository.UpdateSetting(storeId, "Breez", settings!);
if (settings is null)
{
_settings.Remove(storeId);
}
else if(result is not null )
{
_settings.AddOrReplace(storeId, settings);
}
}
public async Task StopAsync(CancellationToken cancellationToken)
{
_clients.Values.ToList().ForEach(c => c.Dispose());
}
public BreezLightningClient? GetClient(string storeId)
{
_clients.TryGetValue(storeId, out var client);
return client;
}
}

View File

@@ -0,0 +1,9 @@
#nullable enable
namespace BTCPayServer.Plugins.Breez;
public class BreezSettings
{
public string InviteCode { get; set; }
public string Mnemonic { get; set; }
public string ApiKey { get; set; }
}

View File

@@ -1,149 +0,0 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using BTCPayServer.Abstractions.Contracts;
using BTCPayServer.Abstractions.Models;
using BTCPayServer.Abstractions.Services;
using BTCPayServer.Configuration;
using BTCPayServer.Lightning;
using BTCPayServer.Lightning.Breez;
using BTCPayServer.Services.Stores;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using NBitcoin;
namespace BTCPayServer.Plugins.FixedFloat
{
public class BreezSettings
{
public string InviteCode { get; set; }
public string Mnemonic { get; set; }
public string ApiKey { get; set; }
}
public class BreezService:IHostedService
{
private readonly StoreRepository _storeRepository;
private readonly IOptions<DataDirectories> _dataDirectories;
private readonly BTCPayNetworkProvider _btcPayNetworkProvider;
private readonly ILogger _logger;
private Dictionary<string, BreezSettings> _settings;
private Dictionary<string, BreezLightningClient> _clients = new();
public BreezService(StoreRepository storeRepository,
IOptions<DataDirectories> dataDirectories,
BTCPayNetworkProvider btcPayNetworkProvider,
ILogger<BreezService> logger)
{
_storeRepository = storeRepository;
_dataDirectories = dataDirectories;
_btcPayNetworkProvider = btcPayNetworkProvider;
_logger = logger;
}
private string GetWorkDir()
{
var dir = _dataDirectories.Value.DataDir;
return Path.Combine(dir, "Plugins", "Breez");
}
TaskCompletionSource tcs = new();
public async Task StartAsync(CancellationToken cancellationToken)
{
_settings = (await _storeRepository.GetSettingsAsync<BreezSettings>("Breez")).Where(pair => pair.Value is not null).ToDictionary(pair => pair.Key, pair => pair.Value!);
tcs.TrySetResult();
}
public async Task<BreezSettings?> Get(string storeId)
{
await tcs.Task;
_settings.TryGetValue(storeId, out var settings);
return settings;
}
public async Task<BreezLightningClient?> Handle(string? storeId, BreezSettings? settings)
{
if (settings is null)
{
if (storeId is not null && _clients.Remove(storeId, out var client))
{
client.Dispose();
}
}
else
{
try
{
var client = new BreezLightningClient(settings.InviteCode, settings.ApiKey, GetWorkDir(),
_btcPayNetworkProvider.BTC.NBitcoinNetwork, settings.Mnemonic);
if (storeId is not null)
{
_clients.AddOrReplace(storeId, client);
}
return client;
}
catch (Exception e)
{
_logger.LogError(e, "Could not create Breez client");
throw;
}
}
return null;
}
public async Task Set(string storeId, BreezSettings? settings)
{
var result = await Handle(storeId, settings);
await _storeRepository.UpdateSetting(storeId, "Breez", settings!);
if (settings is null)
{
_settings.Remove(storeId);
}
else if(result is not null )
{
_settings.AddOrReplace(storeId, settings);
}
}
public async Task StopAsync(CancellationToken cancellationToken)
{
_clients.Values.ToList().ForEach(c => c.Dispose());
}
public BreezLightningClient? GetClient(string storeId)
{
_clients.TryGetValue(storeId, out var client);
return client;
}
}
public class BreezPlugin : BaseBTCPayServerPlugin
{
public override IBTCPayServerPlugin.PluginDependency[] Dependencies { get; } =
{
new() {Identifier = nameof(BTCPayServer), Condition = ">=1.12.0"}
};
public override void Execute(IServiceCollection applicationBuilder)
{
applicationBuilder.AddSingleton<BreezService>();
applicationBuilder.AddSingleton<IHostedService>(provider => provider.GetRequiredService<BreezService>());
applicationBuilder.AddSingleton<BreezLightningConnectionStringHandler>();
applicationBuilder.AddSingleton<ILightningConnectionStringHandler>(provider => provider.GetRequiredService<BreezLightningConnectionStringHandler>());
applicationBuilder.AddSingleton<IUIExtension>(new UIExtension("Breez/BreezNav",
"store-integrations-nav"));
base.Execute(applicationBuilder);
}
}
}

View File

@@ -1,7 +1,7 @@
@using BTCPayServer
@using BTCPayServer.Abstractions.Extensions
@using BTCPayServer.Plugins.FixedFloat
@model BreezSettings?
@using BTCPayServer.Plugins.Breez
@model BTCPayServer.Plugins.Breez.BreezSettings?
@inject BreezService BreezService
@{
ViewData.SetActivePage("Breez", "Breez", "Breez");

View File

@@ -0,0 +1,74 @@

@inject BreezService BreezService;
@using BTCPayServer.Plugins.Breez
@model BTCPayServer.Models.StoreViewModels.LightningNodeViewModel
@{
var storeId = Model.StoreId;
if(Model.CryptoCode != "BTC")
{
return;
}
var client = BreezService.GetClient(storeId);
}
<div id="BreezSetup" class="pt-3 tab-pane fade" role="tabpanel" aria-labelledby="LightningNodeType-Breez">
@if (client is not null)
{
}
else
{
<a asp-action="Index" asp-controller="Breez" asp-route-storeId="@storeId">Breez needs to be configured beforehand.</a>
}
</div>
<script>
const typePrefix = 'type=breez;store=@Model.StoreId';
const triggerEl = document.getElementById('LightningNodeType-Breez')
const connStringEl = document.getElementById('ConnectionString')
const isBreez = connString.startsWith(typePrefix);
const setWallet = accessKey => connStringEl.value = accessKey.length
? `${typePrefix};`
: ''
if (isBreez) {
if (apiToken) {
walletEl.value = apiToken
} else if (walletId) {
const optionEl = document.querySelector(`option[data-wallet-id="${walletId}"]`)
if (optionEl) {
const accessKey = optionEl.getAttribute('value')
walletEl.value = accessKey
setWallet(accessKey)
}
}
// deactivate currently active tab and activate Breez tab
const activeEl = document.querySelector('input[name="LightningNodeType"]:checked')
if (activeEl) {
activeEl.removeAttribute('checked')
activeEl.setAttribute('aria-selected', 'false')
document.querySelector('#LightningNodeTypeTabs .tab-pane.active').classList.remove('active', 'show')
triggerEl.setAttribute('checked', 'checked')
triggerEl.setAttribute('aria-selected', 'true')
document.getElementById('BreezSetup').classList.add('active', 'show')
}
}
document.addEventListener('DOMContentLoaded', () => {
if (isBreez) {
const tabTrigger = new bootstrap.Tab(triggerEl)
triggerEl.checked = true
tabTrigger.show()
}
delegate('change', '#BreezWallet', e => {
const { value } = e.target
setWallet(value)
const tabTrigger = new bootstrap.Tab(triggerEl)
triggerEl.checked = true
tabTrigger.show()
})
})
</script>

View File

@@ -0,0 +1,13 @@
@inject BreezService BreezService;
@using BTCPayServer.Plugins.Breez
@model BTCPayServer.Models.StoreViewModels.LightningNodeViewModel
@if (Model.CryptoCode != "BTC")
{
return;
}
@{
var client = BreezService.GetClient(Model.StoreId);
}
<input value="Custom" type="radio" id="LightningNodeType-Breez" data-bs-toggle="pill" data-bs-target="#BreezSetup" role="tab" aria-controls="BreezSetup" aria-selected="false" name="LightningNodeType" disabled="@(client is null)">
<label for="LightningNodeType-Breez">Use Breez wallet</label>