diff --git a/Plugins/BTCPayServer.Plugins.Breez/BreezController.cs b/Plugins/BTCPayServer.Plugins.Breez/BreezController.cs index 0be705c..8acfcf5 100644 --- a/Plugins/BTCPayServer.Plugins.Breez/BreezController.cs +++ b/Plugins/BTCPayServer.Plugins.Breez/BreezController.cs @@ -2,12 +2,17 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using Breez.Sdk; using BTCPayServer.Abstractions.Constants; using BTCPayServer.Client; +using BTCPayServer.Models; +using BTCPayServer.Services.Wallets; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using NBXplorer.DerivationStrategy; namespace BTCPayServer.Plugins.Breez; + [Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie)] [Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Cookie)] [Route("plugins/{storeId}/Breez")] @@ -15,26 +20,201 @@ public class BreezController : Controller { private readonly BTCPayNetworkProvider _btcPayNetworkProvider; private readonly BreezService _breezService; + private readonly BTCPayWalletProvider _btcWalletProvider; - public BreezController(BTCPayNetworkProvider btcPayNetworkProvider, BreezService breezService) + public BreezController(BTCPayNetworkProvider btcPayNetworkProvider, + BreezService breezService, + BTCPayWalletProvider btcWalletProvider) { _btcPayNetworkProvider = btcPayNetworkProvider; _breezService = breezService; + _btcWalletProvider = btcWalletProvider; } -[HttpGet("")] + + + [HttpGet("")] public async Task Index(string storeId) { - return View(await _breezService.Get(storeId)); - + var client = _breezService.GetClient(storeId); + return RedirectToAction(client is null ? nameof(Configure) : nameof(Info), new {storeId}); } + + [HttpGet("swapin")] + public async Task SwapIn(string storeId) + { + var client = _breezService.GetClient(storeId); + if (client is null) + { + return RedirectToAction(nameof(Configure), new {storeId}); + } + + return View((object) storeId); + } + + [HttpGet("info")] + public async Task Info(string storeId) + { + var client = _breezService.GetClient(storeId); + if (client is null) + { + return RedirectToAction(nameof(Configure), new {storeId}); + } + + return View((object) storeId); + } + + [HttpGet("sweep")] + public async Task Sweep(string storeId) + { + var client = _breezService.GetClient(storeId); + if (client is null) + { + return RedirectToAction(nameof(Configure), new {storeId}); + } + + return View((object) storeId); + } + + [HttpPost("sweep")] + public async Task Sweep(string storeId, string address, uint satPerByte) + { + var client = _breezService.GetClient(storeId); + if (client is null) + { + return RedirectToAction(nameof(Configure), new {storeId}); + } + + if (address.Equals("store", StringComparison.InvariantCultureIgnoreCase)) + { + var store = ControllerContext.HttpContext.GetStoreData() + .GetDerivationSchemeSettings(_btcPayNetworkProvider, "BTC"); + var res = await _btcWalletProvider.GetWallet(storeId) + .ReserveAddressAsync(storeId, store.AccountDerivation, "Breez"); + address = res.Address.ToString(); + } + + try + { + var response = client.Sdk.Sweep(new SweepRequest(address, satPerByte)); + + TempData[WellKnownTempData.SuccessMessage] = $"sweep successful: {response.txid}"; + } + catch (Exception e) + { + TempData[WellKnownTempData.ErrorMessage] = $"error with sweep: {e.Message}"; + } + + + return View((object) storeId); + } + + [HttpGet("swapin/create")] + public async Task SwapInCreate(string storeId) + { + var client = _breezService.GetClient(storeId); + if (client is null) + { + return RedirectToAction(nameof(Configure), new {storeId}); + } + + client.Sdk.ReceiveOnchain(new ReceiveOnchainRequest()); + TempData[WellKnownTempData.SuccessMessage] = "Swapin created successfully"; + return RedirectToAction(nameof(SwapIn), new {storeId}); + } + + + [HttpGet("swapout")] + public async Task SwapOut(string storeId) + { + var client = _breezService.GetClient(storeId); + if (client is null) + { + return RedirectToAction(nameof(Configure), new {storeId}); + } + + return View((object) storeId); + } + + [HttpPost("swapout")] + public async Task SwapOut(string storeId, string address, ulong amount, uint satPerByte, + string feesHash) + { + var client = _breezService.GetClient(storeId); + if (client is null) + { + return RedirectToAction(nameof(Configure), new {storeId}); + } + + if (address.Equals("store", StringComparison.InvariantCultureIgnoreCase)) + { + var store = ControllerContext.HttpContext.GetStoreData() + .GetDerivationSchemeSettings(_btcPayNetworkProvider, "BTC"); + var res = await _btcWalletProvider.GetWallet(storeId) + .ReserveAddressAsync(storeId, store.AccountDerivation, "Breez"); + address = res.Address.ToString(); + } + + try + { + var result = client.Sdk.SendOnchain(new SendOnchainRequest(amount, address, feesHash, satPerByte)); + TempData[WellKnownTempData.SuccessMessage] = $"swap out created: {result.reverseSwapInfo.id}"; + } + catch (Exception e) + { + TempData[WellKnownTempData.ErrorMessage] = $"Couldnt create swap out: {e.Message}"; + } + + return RedirectToAction("SwapOut", new {storeId}); + } + + [HttpGet("swapin/{address}/refund")] + public async Task SwapInRefund(string storeId, string address) + { + var client = _breezService.GetClient(storeId); + if (client is null) + { + return RedirectToAction(nameof(Configure), new {storeId}); + } + + return View((object) storeId); + } + + [HttpPost("swapin/{address}/refund")] + public async Task SwapInRefund(string storeId, string address, string refundAddress, uint satPerByte) + { + var client = _breezService.GetClient(storeId); + if (client is null) + { + return RedirectToAction(nameof(Configure), new {storeId}); + } + + try + { + var resp = client.Sdk.Refund(new RefundRequest(address, refundAddress, satPerByte)); + TempData[WellKnownTempData.SuccessMessage] = $"Refund tx: {resp.refundTxId}"; + } + catch (Exception e) + { + TempData[WellKnownTempData.ErrorMessage] = $"Couldnt refund: {e.Message}"; + } + + return RedirectToAction(nameof(SwapIn), new {storeId}); + } + + [HttpGet("configure")] + public async Task Configure(string storeId) + { + return View(await _breezService.Get(storeId)); + } + [HttpPost("")] - public async Task Index(string storeId, string command, BreezSettings settings) + public async Task Configure(string storeId, string command, BreezSettings settings) { if (command == "clear") { await _breezService.Set(storeId, null); TempData[WellKnownTempData.SuccessMessage] = "Settings cleared successfully"; - return RedirectToAction(nameof(Index), new {storeId}); + return RedirectToAction(nameof(Configure), new {storeId}); } if (command == "save") @@ -48,11 +228,33 @@ public class BreezController : Controller TempData[WellKnownTempData.ErrorMessage] = $"Couldnt use provided settings: {e.Message}"; return View(settings); } - + TempData[WellKnownTempData.SuccessMessage] = "Settings saved successfully"; - return RedirectToAction(nameof(Index), new {storeId}); + return RedirectToAction(nameof(Configure), new {storeId}); } return NotFound(); } + + [Route("payments")] + public async Task Payments(string storeId, PaymentsViewModel viewModel) + { + var client = _breezService.GetClient(storeId); + if (client is null) + { + return RedirectToAction(nameof(Configure), new {storeId}); + } + viewModel ??= new PaymentsViewModel(); + + viewModel.Payments = client.Sdk.ListPayments(new ListPaymentsRequest(PaymentTypeFilter.ALL, null, null, null, + (uint?) viewModel.Skip, (uint?) viewModel.Count)); + + return View(viewModel); + } +} + +public class PaymentsViewModel : BasePagingViewModel +{ + public List Payments { get; set; } = new(); + public override int CurrentPageCount => Payments.Count; } \ No newline at end of file diff --git a/Plugins/BTCPayServer.Plugins.Breez/BreezLightningConnectionStringHandler.cs b/Plugins/BTCPayServer.Plugins.Breez/BreezLightningConnectionStringHandler.cs index bc44381..39709e0 100644 --- a/Plugins/BTCPayServer.Plugins.Breez/BreezLightningConnectionStringHandler.cs +++ b/Plugins/BTCPayServer.Plugins.Breez/BreezLightningConnectionStringHandler.cs @@ -20,13 +20,13 @@ public class BreezLightningConnectionStringHandler : ILightningConnectionStringH return null; } - if (!kv.TryGetValue("store", out var storeId)) + if (!kv.TryGetValue("key", out var key)) { - error = $"The key 'store' is mandatory for breez connection strings"; + error = $"The key 'key' is mandatory for breez connection strings"; return null; } error = null; - return _breezService.GetClient(storeId); + return _breezService.GetClientByPaymentKey(key); } } diff --git a/Plugins/BTCPayServer.Plugins.Breez/BreezPlugin.cs b/Plugins/BTCPayServer.Plugins.Breez/BreezPlugin.cs index f36f5a5..8c90bcb 100644 --- a/Plugins/BTCPayServer.Plugins.Breez/BreezPlugin.cs +++ b/Plugins/BTCPayServer.Plugins.Breez/BreezPlugin.cs @@ -27,6 +27,10 @@ namespace BTCPayServer.Plugins.Breez applicationBuilder.AddSingleton(new UIExtension("Breez/LNPaymentMethodSetupTabhead", "ln-payment-method-setup-tabhead")); applicationBuilder.AddSingleton(new UIExtension("Breez/LNPaymentMethodSetupTab", "ln-payment-method-setup-tab")); + applicationBuilder.AddSingleton(new UIExtension("Breez/BreezNodeInfo", + "dashboard")); + applicationBuilder.AddSingleton(new UIExtension("Breez/BreezPaymentsTable", + "dashboard")); base.Execute(applicationBuilder); } } diff --git a/Plugins/BTCPayServer.Plugins.Breez/BreezService.cs b/Plugins/BTCPayServer.Plugins.Breez/BreezService.cs index 2745847..bd0877e 100644 --- a/Plugins/BTCPayServer.Plugins.Breez/BreezService.cs +++ b/Plugins/BTCPayServer.Plugins.Breez/BreezService.cs @@ -6,6 +6,9 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; using BTCPayServer.Configuration; +using BTCPayServer.Data; +using BTCPayServer.Payments; +using BTCPayServer.Payments.Lightning; using BTCPayServer.Services.Stores; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; @@ -106,8 +109,18 @@ public class BreezService:IHostedService await _storeRepository.UpdateSetting(storeId, "Breez", settings!); if (settings is null) { - _settings.Remove(storeId); - + _settings.Remove(storeId, out var oldSettings ); + var data = await _storeRepository.FindStore(storeId); + var existing = data?.GetSupportedPaymentMethods(_btcPayNetworkProvider) + .OfType().FirstOrDefault(method => + method.CryptoCode == "BTC" && method.PaymentId.PaymentType == LightningPaymentType.Instance); + var isBreez = existing?.GetExternalLightningUrl() == $"type=breez;key={oldSettings.PaymentKey}"; + if (isBreez) + { + data.SetSupportedPaymentMethod(new PaymentMethodId("BTC", LightningPaymentType.Instance), null ); + await _storeRepository.UpdateStore(data); + } + } else if(result is not null ) { @@ -122,9 +135,18 @@ public class BreezService:IHostedService _clients.Values.ToList().ForEach(c => c.Dispose()); } - public BreezLightningClient? GetClient(string storeId) + public BreezLightningClient? GetClient(string? storeId) { + if(storeId is null) + return null; _clients.TryGetValue(storeId, out var client); return client; + } + public BreezLightningClient? GetClientByPaymentKey(string? paymentKey) + { + if(paymentKey is null) + return null; + var match = _settings.FirstOrDefault(pair => pair.Value.PaymentKey == paymentKey).Key; + return GetClient(match); } } \ No newline at end of file diff --git a/Plugins/BTCPayServer.Plugins.Breez/BreezSettings.cs b/Plugins/BTCPayServer.Plugins.Breez/BreezSettings.cs index 297ee8f..807d9ab 100644 --- a/Plugins/BTCPayServer.Plugins.Breez/BreezSettings.cs +++ b/Plugins/BTCPayServer.Plugins.Breez/BreezSettings.cs @@ -1,4 +1,6 @@ #nullable enable +using System; + namespace BTCPayServer.Plugins.Breez; public class BreezSettings @@ -6,4 +8,6 @@ public class BreezSettings public string InviteCode { get; set; } public string Mnemonic { get; set; } public string ApiKey { get; set; } + + public string PaymentKey { get; set; } = Guid.NewGuid().ToString(); } \ No newline at end of file diff --git a/Plugins/BTCPayServer.Plugins.Breez/Views/Breez/Index.cshtml b/Plugins/BTCPayServer.Plugins.Breez/Views/Breez/Configure.cshtml similarity index 54% rename from Plugins/BTCPayServer.Plugins.Breez/Views/Breez/Index.cshtml rename to Plugins/BTCPayServer.Plugins.Breez/Views/Breez/Configure.cshtml index 45d6102..51a61b5 100644 --- a/Plugins/BTCPayServer.Plugins.Breez/Views/Breez/Index.cshtml +++ b/Plugins/BTCPayServer.Plugins.Breez/Views/Breez/Configure.cshtml @@ -4,18 +4,25 @@ @model BTCPayServer.Plugins.Breez.BreezSettings? @inject BreezService BreezService @{ - ViewData.SetActivePage("Breez", "Breez", "Breez"); + ViewData.SetActivePage("Breez", "Configure", "Configure"); var storeId = Context.GetCurrentStoreId(); } +
+
+
+
+

+ @ViewData["Title"] +

+
+ + @if (await BreezService.Get(storeId) is not null) + { + + } +
+
- - -

@ViewData["Title"]

- - -
-
-
@@ -34,20 +41,8 @@
- - @if (await BreezService.Get(storeId) is not null) - { - - var client = BreezService.GetClient(storeId); - if (client is not null) - { -
-                        @Json.Serialize(client.Sdk.NodeInfo())
-                        
-                    
- } - } - + +
-
\ No newline at end of file + \ No newline at end of file diff --git a/Plugins/BTCPayServer.Plugins.Breez/Views/Breez/Info.cshtml b/Plugins/BTCPayServer.Plugins.Breez/Views/Breez/Info.cshtml new file mode 100644 index 0000000..676024e --- /dev/null +++ b/Plugins/BTCPayServer.Plugins.Breez/Views/Breez/Info.cshtml @@ -0,0 +1,15 @@ +@using BTCPayServer.Abstractions.Extensions +@using BTCPayServer.Models.StoreViewModels +@using BTCPayServer.Security +@model object +@{ + var storeId = Model switch + { + string s => s, + StoreDashboardViewModel dashboardModel => dashboardModel.StoreId, + _ => Context.GetImplicitStoreId() + }; + ViewData.SetActivePage("Breez", "Create Swapin Refund", "Info"); + +} + \ No newline at end of file diff --git a/Plugins/BTCPayServer.Plugins.Breez/Views/Breez/Payments.cshtml b/Plugins/BTCPayServer.Plugins.Breez/Views/Breez/Payments.cshtml new file mode 100644 index 0000000..8d0081a --- /dev/null +++ b/Plugins/BTCPayServer.Plugins.Breez/Views/Breez/Payments.cshtml @@ -0,0 +1,27 @@ +@using BTCPayServer.Components +@using BTCPayServer +@using BTCPayServer.Abstractions.Extensions +@model BTCPayServer.Plugins.Breez.PaymentsViewModel +@{ + var storeId = Context.GetCurrentStoreId(); + + ViewData.SetActivePage("Breez", "Payments", "Payments"); +} + +
+
+
+

+ @ViewData["Title"] +

+
+ + + +
+
+ + + +
+
\ No newline at end of file diff --git a/Plugins/BTCPayServer.Plugins.Breez/Views/Breez/SwapIn.cshtml b/Plugins/BTCPayServer.Plugins.Breez/Views/Breez/SwapIn.cshtml new file mode 100644 index 0000000..b3331eb --- /dev/null +++ b/Plugins/BTCPayServer.Plugins.Breez/Views/Breez/SwapIn.cshtml @@ -0,0 +1,193 @@ +@using Breez.Sdk +@using BTCPayServer +@using BTCPayServer.Abstractions.Extensions +@using BTCPayServer.Abstractions.Contracts +@using BTCPayServer.Components.QRCode +@using BTCPayServer.Components.TruncateCenter +@using BTCPayServer.Models.StoreViewModels +@using BTCPayServer.Payments +@using BTCPayServer.Plugins.Breez +@using BTCPayServer.Security +@using NBitcoin +@inject BreezService BreezService +@inject BTCPayNetworkProvider BtcPayNetworkProvider +@{ + ViewData.SetActivePage("Breez", "Swap In", "SwapIn"); + string storeId = Model switch + { + string s => s, + StoreDashboardViewModel dashboardModel => dashboardModel.StoreId, + _ => Context.GetImplicitStoreId() + }; + var sdk = BreezService.GetClient(storeId)?.Sdk; + if (sdk is null) + return; + + var inProgressSwap = sdk.InProgressSwap(); + var refundables = sdk.ListRefundables(); + var deriv = Context.GetStoreData().GetDerivationSchemeSettings(BtcPayNetworkProvider, "BTC"); + var ni = sdk.NodeInfo(); + var f = sdk.RecommendedFees(); + + +} + + + + + + + + + +
+
+
+

+ @ViewData["Title"] +

+
+ @if (inProgressSwap is null) + { + Create + } +
+
+ + @if (inProgressSwap is not null) + { +
+
+ +
+
+
+ + +
+ Please send an amount between @Money.Satoshis(inProgressSwap.minAllowedDeposit).ToDecimal(MoneyUnit.BTC) BTC and @Money.Satoshis(inProgressSwap.maxAllowedDeposit).ToDecimal(MoneyUnit.BTC) + @if (deriv is not null) + { + Send using BTCPay Wallet + } + @{ + var onChainSats = ni.onchainBalanceMsat / 1000; + if (inProgressSwap.minAllowedDeposit <= (long) onChainSats) + { +
+
+ + +
+
+ } + + } + +
+ + @if (inProgressSwap.unconfirmedSats + inProgressSwap.confirmedSats + inProgressSwap.paidSats > 0) + { +
+ @inProgressSwap.unconfirmedSats sats unconfirmed + @inProgressSwap.confirmedSats sats confirmed + @inProgressSwap.paidSats sats paid +
+ } + @if (inProgressSwap.unconfirmedTxIds.Any()) + { +
+ + Unconfirmed transactions + @foreach (var txId in inProgressSwap.unconfirmedTxIds) + { + + } +
+ } + @if (inProgressSwap.confirmedTxIds.Any()) + { +
+ + Confirmed transactions + @foreach (var txId in inProgressSwap.confirmedTxIds) + { + + } +
+ } +
+ } + @if (refundables?.Any() is true) + { + + + + + + + + + + + + @foreach (var refund in refundables) + { + + + + + + + + } + +
StatusCreatedDeposit AddressPaymentActions
@refund.status@DateTimeOffset.FromUnixTimeSeconds(refund.createdAt)@refund.bitcoinAddress + @if (refund.unconfirmedSats + refund.confirmedSats + refund.paidSats > 0) + { +
+ @refund.unconfirmedSats sats unconfirmed + @refund.confirmedSats sats confirmed + @refund.paidSats sats paid +
+ } + @if (refund.unconfirmedTxIds.Any()) + { +
+ + Unconfirmed transactions + @foreach (var txId in refund.unconfirmedTxIds) + { + + } +
+ } + @if (refund.confirmedTxIds.Any()) + { +
+ + Confirmed transactions + @foreach (var txId in refund.confirmedTxIds) + { + + } +
+ } + @if (refund.refundTxIds.Any()) + { +
+ + Refund transactions + @foreach (var txId in refund.refundTxIds) + { + + } +
+ } + +
+ Start Refund +
+ } +
+
\ No newline at end of file diff --git a/Plugins/BTCPayServer.Plugins.Breez/Views/Breez/SwapInRefund.cshtml b/Plugins/BTCPayServer.Plugins.Breez/Views/Breez/SwapInRefund.cshtml new file mode 100644 index 0000000..ac3e438 --- /dev/null +++ b/Plugins/BTCPayServer.Plugins.Breez/Views/Breez/SwapInRefund.cshtml @@ -0,0 +1,52 @@ +@using BTCPayServer.Abstractions.Extensions +@using BTCPayServer.Plugins.Breez +@using BTCPayServer.Security +@using Microsoft.AspNetCore.Routing +@model string +@inject BreezService BreezService +@{ + var storeId = Context.GetImplicitStoreId(); + var address = Context.GetRouteValue("address").ToString(); + ViewData.SetActivePage("Breez", "Create Swapin Refund", "SwapIn"); + + var sdk = BreezService.GetClient(storeId)?.Sdk; + var f = sdk.RecommendedFees(); +} + + + + + + + + + + +
+
+
+
+

+ @ViewData["Title"] + + + +

+
+ + +
+
+
+
+ + +
+
+ + +
+
+
+ +
\ No newline at end of file diff --git a/Plugins/BTCPayServer.Plugins.Breez/Views/Breez/SwapOut.cshtml b/Plugins/BTCPayServer.Plugins.Breez/Views/Breez/SwapOut.cshtml new file mode 100644 index 0000000..f12cffa --- /dev/null +++ b/Plugins/BTCPayServer.Plugins.Breez/Views/Breez/SwapOut.cshtml @@ -0,0 +1,103 @@ +@using Breez.Sdk +@using BTCPayServer +@using BTCPayServer.Abstractions.Extensions +@using BTCPayServer.Models.StoreViewModels +@using BTCPayServer.Plugins.Breez +@using BTCPayServer.Security +@inject BreezService BreezService +@inject BTCPayNetworkProvider BtcPayNetworkProvider + + +@{ + Layout = "_Layout"; + ViewData.SetActivePage("Breez", "Swap Out", "SwapOut"); + string storeId = null; + if (Model is string s) + { + storeId = s; + } + else if (Model is StoreDashboardViewModel dashboardModel) + { + storeId = dashboardModel.StoreId; + } + else + { + storeId = Context.GetImplicitStoreId(); + } + var sdk = BreezService.GetClient(storeId)?.Sdk; + if (sdk is null) + return; + + var inProgressSwaps = sdk.InProgressReverseSwaps(); + var deriv = Context.GetStoreData().GetDerivationSchemeSettings(BtcPayNetworkProvider, "BTC"); + var f = sdk.RecommendedFees(); + var swapOutRec = sdk.FetchReverseSwapFees(new ReverseSwapFeesRequest()); +} + + + + + + + + + + @if (deriv is not null) + { + + } + + +
+
+
+
+

+ @ViewData["Title"] +

+
+ + +
+
+
+
+ + +
+
+ + +
+
+ + +
+ + + + @if (inProgressSwaps?.Any() is true) + { + + + + + + + + + @foreach (var swap in inProgressSwaps) + { + + + + + + } + +
IdStatus
@swap.id@swap.status
+ } + +
+
+
\ No newline at end of file diff --git a/Plugins/BTCPayServer.Plugins.Breez/Views/Breez/Sweep.cshtml b/Plugins/BTCPayServer.Plugins.Breez/Views/Breez/Sweep.cshtml new file mode 100644 index 0000000..60ee337 --- /dev/null +++ b/Plugins/BTCPayServer.Plugins.Breez/Views/Breez/Sweep.cshtml @@ -0,0 +1,67 @@ +@using BTCPayServer +@using BTCPayServer.Abstractions.Extensions +@using BTCPayServer.Plugins.Breez +@using BTCPayServer.Security +@model string +@inject BreezService BreezService +@inject BTCPayNetworkProvider BtcPayNetworkProvider +@{ + var storeId = Context.GetImplicitStoreId(); + Layout = "_Layout"; + ViewData.SetActivePage("Breez", "Sweep", "Sweep"); + + var sdk = BreezService.GetClient(storeId)?.Sdk; + var f = sdk.RecommendedFees(); + var info = sdk.NodeInfo(); + + + var deriv = Context.GetStoreData().GetDerivationSchemeSettings(BtcPayNetworkProvider, "BTC"); + +} + + @if (deriv is not null) + { + + } + + + + + + + + + +
+ +
+
+
+

+ @ViewData["Title"] +

+
+ + +
+
+
+ +
+ + +
+
+ + +
+ + + +
+
+
+ +@section PageFootContent { + +} \ No newline at end of file diff --git a/Plugins/BTCPayServer.Plugins.Breez/Views/Breez/_Layout.cshtml b/Plugins/BTCPayServer.Plugins.Breez/Views/Breez/_Layout.cshtml new file mode 100644 index 0000000..9035365 --- /dev/null +++ b/Plugins/BTCPayServer.Plugins.Breez/Views/Breez/_Layout.cshtml @@ -0,0 +1,5 @@ +@{ + Layout = "../Shared/_NavLayout.cshtml"; + ViewData["NavPartialName"] = "_Nav"; +} +@RenderBody() \ No newline at end of file diff --git a/Plugins/BTCPayServer.Plugins.Breez/Views/Breez/_Nav.cshtml b/Plugins/BTCPayServer.Plugins.Breez/Views/Breez/_Nav.cshtml new file mode 100644 index 0000000..dc0edb8 --- /dev/null +++ b/Plugins/BTCPayServer.Plugins.Breez/Views/Breez/_Nav.cshtml @@ -0,0 +1,33 @@ +@using BTCPayServer.Abstractions.Extensions +@using BTCPayServer.Models.StoreViewModels +@using BTCPayServer.Plugins.Breez +@using BTCPayServer.Security + +@inject BreezService BreezService +@{ + var storeId = Model switch + { + string s => s, + StoreDashboardViewModel dashboardModel => dashboardModel.StoreId, + _ => Context.GetImplicitStoreId() + }; + var sdk = BreezService.GetClient(storeId)?.Sdk; + +} + +
+ \ No newline at end of file diff --git a/Plugins/BTCPayServer.Plugins.Breez/Views/Shared/Breez/BreezNodeInfo.cshtml b/Plugins/BTCPayServer.Plugins.Breez/Views/Shared/Breez/BreezNodeInfo.cshtml new file mode 100644 index 0000000..0845cf3 --- /dev/null +++ b/Plugins/BTCPayServer.Plugins.Breez/Views/Shared/Breez/BreezNodeInfo.cshtml @@ -0,0 +1,99 @@ +@using BTCPayServer.Lightning +@using BTCPayServer.Models.StoreViewModels +@using BTCPayServer.Plugins.Breez +@using BTCPayServer.Security +@inject BreezService BreezService +@{ + string storeId = null; + if (Model is string s) + { + storeId = s; + } + else if (Model is StoreDashboardViewModel dashboardModel) + { + storeId = dashboardModel.StoreId; + } + else + { + storeId = Context.GetImplicitStoreId(); + } + var sdk = BreezService.GetClient(storeId)?.Sdk; + if (sdk is null) + return; + var nodeState = sdk.NodeInfo(); + var lspInfo = sdk.LspInfo(); +} +
+ @if (Model is StoreDashboardViewModel) + { +
+

Breez Node

+ + Manage + + +
+ } +
+ +
@nodeState.id
+
+ @if (lspInfo is not null) + { +
+ +
@lspInfo.name connected
+
+ } +
+
+
On-Chain Balance
+ @if (Model is StoreDashboardViewModel && nodeState.onchainBalanceMsat > 0) + { +
+ Sweep +
+ } + +
+
+

@LightMoney.MilliSatoshis(nodeState.onchainBalanceMsat)

+ BTC +
+
+
+
+
Lightning Balance
+ @if (Model is StoreDashboardViewModel) + { +
+ Swap in + @if (nodeState.channelsBalanceMsat > 0) + { + - + Swap out + } +
+ } + +
+
+

@LightMoney.MilliSatoshis(nodeState.channelsBalanceMsat)

+ BTC +
+
+ +
+
+
Lightning insight
+
+
+

@LightMoney.MilliSatoshis(nodeState.maxReceivableMsat)

+ BTC receivable +
+
+

@LightMoney.MilliSatoshis(nodeState.maxPayableMsat)

+ BTC spendable +
+
+
\ No newline at end of file diff --git a/Plugins/BTCPayServer.Plugins.Breez/Views/Shared/Breez/BreezPaymentsTable.cshtml b/Plugins/BTCPayServer.Plugins.Breez/Views/Shared/Breez/BreezPaymentsTable.cshtml new file mode 100644 index 0000000..403047d --- /dev/null +++ b/Plugins/BTCPayServer.Plugins.Breez/Views/Shared/Breez/BreezPaymentsTable.cshtml @@ -0,0 +1,90 @@ +@using Breez.Sdk +@using BTCPayServer.Abstractions.Extensions +@using BTCPayServer.Lightning +@using BTCPayServer.Models.StoreViewModels +@using BTCPayServer.Plugins.Breez +@using BTCPayServer.Security +@model object +@inject BreezService BreezService +@{ + var data = Model as List; + var storeId = Context.GetImplicitStoreId(); + if (data is null) + { + var sdk = BreezService.GetClient(storeId)?.Sdk; + if (sdk is null) + return; + + data = sdk.ListPayments(new ListPaymentsRequest(PaymentTypeFilter.ALL, null, null, null, 0, 10)); + } + + var isDashboard = Model is StoreDashboardViewModel; +} + +
+ @if (isDashboard) + { +
+

Breez Payments

+ @if (data.Any()) + { + View All + } +
+ } + @if (!data.Any()) + { +

+ There are no recent payments. +

+ } + else + { +
+ + + + + + + + + + + + + + @foreach (var payment in data) + { + + + + + + + + + + + } + +
IdTimestampTypeAmountFeeStatusDescription
+ + @payment.id + + + @DateTimeOffset.FromUnixTimeSeconds(payment.paymentTime).ToTimeAgo() + + >@payment.paymentType.ToString().ToLowerInvariant().Replace("_", " ") + + >@LightMoney.MilliSatoshis(payment.amountMsat).ToDecimal(LightMoneyUnit.BTC) BTC + + >@LightMoney.MilliSatoshis(payment.feeMsat).ToDecimal(LightMoneyUnit.BTC) BTC + + @payment.status.ToString().ToLowerInvariant() + + @payment.description +
+
+ } +
\ No newline at end of file diff --git a/Plugins/BTCPayServer.Plugins.Breez/Views/Shared/Breez/LNPaymentMethodSetupTab.cshtml b/Plugins/BTCPayServer.Plugins.Breez/Views/Shared/Breez/LNPaymentMethodSetupTab.cshtml index 4ea9267..2dae76d 100644 --- a/Plugins/BTCPayServer.Plugins.Breez/Views/Shared/Breez/LNPaymentMethodSetupTab.cshtml +++ b/Plugins/BTCPayServer.Plugins.Breez/Views/Shared/Breez/LNPaymentMethodSetupTab.cshtml @@ -18,7 +18,7 @@ } else { - Breez needs to be configured beforehand. + Breez needs to be configured beforehand. }
@@ -26,22 +26,11 @@ const typePrefix = 'type=breez;store=@Model.StoreId'; const triggerEl = document.getElementById('LightningNodeType-Breez') const connStringEl = document.getElementById('ConnectionString') + const connString = connStringEl.value; 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') @@ -52,6 +41,7 @@ triggerEl.setAttribute('checked', 'checked') triggerEl.setAttribute('aria-selected', 'true') document.getElementById('BreezSetup').classList.add('active', 'show') + } } @@ -62,12 +52,13 @@ tabTrigger.show() } - delegate('change', '#BreezWallet', e => { - const { value } = e.target - setWallet(value) - const tabTrigger = new bootstrap.Tab(triggerEl) - triggerEl.checked = true - tabTrigger.show() + delegate('change', 'input[name="LightningNodeType"]', e => { + + const activeEl = document.querySelector('input[name="LightningNodeType"]:checked') + if (activeEl.id === "LightningNodeType-Breez"){ + connStringEl.value =typePrefix; + } + }) })