From cfa7efd11bf739e0f91c370bfe8f15787d9c965b Mon Sep 17 00:00:00 2001 From: Kukks Date: Wed, 1 Nov 2023 15:25:29 +0100 Subject: [PATCH] wip --- BTCPayServerPlugins.sln | 9 + .../BTCPayServer.Plugins.Breez.csproj | 42 +++ .../BreezController.cs | 59 ++++ .../BreezLightningClient.cs | 281 ++++++++++++++++++ .../BreezLightningConnectionStringHandler.cs | 35 +++ Plugins/BTCPayServer.Plugins.Breez/Program.cs | 149 ++++++++++ .../Views/Breez/Index.cshtml | 53 ++++ .../Views/Shared/Breez/BreezNav.cshtml | 27 ++ .../Views/_ViewImports.cshtml | 2 + 9 files changed, 657 insertions(+) create mode 100644 Plugins/BTCPayServer.Plugins.Breez/BTCPayServer.Plugins.Breez.csproj create mode 100644 Plugins/BTCPayServer.Plugins.Breez/BreezController.cs create mode 100644 Plugins/BTCPayServer.Plugins.Breez/BreezLightningClient.cs create mode 100644 Plugins/BTCPayServer.Plugins.Breez/BreezLightningConnectionStringHandler.cs create mode 100644 Plugins/BTCPayServer.Plugins.Breez/Program.cs create mode 100644 Plugins/BTCPayServer.Plugins.Breez/Views/Breez/Index.cshtml create mode 100644 Plugins/BTCPayServer.Plugins.Breez/Views/Shared/Breez/BreezNav.cshtml create mode 100644 Plugins/BTCPayServer.Plugins.Breez/Views/_ViewImports.cshtml diff --git a/BTCPayServerPlugins.sln b/BTCPayServerPlugins.sln index ccb4d6f..2639a6d 100644 --- a/BTCPayServerPlugins.sln +++ b/BTCPayServerPlugins.sln @@ -49,6 +49,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BTCPayServer.Plugins.Prism" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BTCPayServer.Plugins.FileSeller", "Plugins\BTCPayServer.Plugins.FileSeller\BTCPayServer.Plugins.FileSeller.csproj", "{F2D81B6A-E1EA-4900-BF9A-924D2CA951DD}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BTCPayServer.Plugins.Breez", "Plugins\BTCPayServer.Plugins.Breez\BTCPayServer.Plugins.Breez.csproj", "{5934F898-00B1-4781-BD18-04DF8685BC76}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BTCPayServer.Plugins.DynamicReports", "Plugins\BTCPayServer.Plugins.DynamicReports\BTCPayServer.Plugins.DynamicReports.csproj", "{BCB4E68D-089F-481E-A3AE-FC9CED6AA34D}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BTCPayServer.Plugins.Bringin", "Plugins\BTCPayServer.Plugins.Bringin\BTCPayServer.Plugins.Bringin.csproj", "{D4AFEC95-64D4-4FC4-9AE4-B82F4C6D6E29}" @@ -253,6 +254,14 @@ Global {D4AFEC95-64D4-4FC4-9AE4-B82F4C6D6E29}.Altcoins-Debug|Any CPU.Build.0 = Debug|Any CPU {D4AFEC95-64D4-4FC4-9AE4-B82F4C6D6E29}.Altcoins-Release|Any CPU.ActiveCfg = Debug|Any CPU {D4AFEC95-64D4-4FC4-9AE4-B82F4C6D6E29}.Altcoins-Release|Any CPU.Build.0 = Debug|Any CPU + {5934F898-00B1-4781-BD18-04DF8685BC76}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5934F898-00B1-4781-BD18-04DF8685BC76}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5934F898-00B1-4781-BD18-04DF8685BC76}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5934F898-00B1-4781-BD18-04DF8685BC76}.Release|Any CPU.Build.0 = Release|Any CPU + {5934F898-00B1-4781-BD18-04DF8685BC76}.Altcoins-Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5934F898-00B1-4781-BD18-04DF8685BC76}.Altcoins-Debug|Any CPU.Build.0 = Debug|Any CPU + {5934F898-00B1-4781-BD18-04DF8685BC76}.Altcoins-Release|Any CPU.ActiveCfg = Debug|Any CPU + {5934F898-00B1-4781-BD18-04DF8685BC76}.Altcoins-Release|Any CPU.Build.0 = Debug|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution {B19C9F52-DC47-466D-8B5C-2D202B7B003F} = {9E04ECE9-E304-4FF2-9CBC-83256E6C6962} diff --git a/Plugins/BTCPayServer.Plugins.Breez/BTCPayServer.Plugins.Breez.csproj b/Plugins/BTCPayServer.Plugins.Breez/BTCPayServer.Plugins.Breez.csproj new file mode 100644 index 0000000..fc639cf --- /dev/null +++ b/Plugins/BTCPayServer.Plugins.Breez/BTCPayServer.Plugins.Breez.csproj @@ -0,0 +1,42 @@ + + + + net6.0 + 10 + + + + + Breez / Greenlight + Lightwight lightning baby! + 1.0.0 + + + + true + false + true + + + + + + StaticWebAssetsEnabled=false + false + runtime;native;build;buildTransitive;contentFiles + + + + + + + + + + + + + + + + diff --git a/Plugins/BTCPayServer.Plugins.Breez/BreezController.cs b/Plugins/BTCPayServer.Plugins.Breez/BreezController.cs new file mode 100644 index 0000000..ddf2947 --- /dev/null +++ b/Plugins/BTCPayServer.Plugins.Breez/BreezController.cs @@ -0,0 +1,59 @@ +using System; +using System.Collections.Generic; +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; + +namespace BTCPayServer.Plugins.Breez; +[Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie)] +[Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Cookie)] +[Route("plugins/{storeId}/Breez")] +public class BreezController : Controller +{ + private readonly BTCPayNetworkProvider _btcPayNetworkProvider; + private readonly BreezService _breezService; + + public BreezController(BTCPayNetworkProvider btcPayNetworkProvider, BreezService breezService) + { + _btcPayNetworkProvider = btcPayNetworkProvider; + _breezService = breezService; + } +[HttpGet("")] + public async Task Index(string storeId) + { + return View(await _breezService.Get(storeId)); + + } + [HttpPost("")] + public async Task Index(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}); + } + + if (command == "save") + { + try + { + await _breezService.Set(storeId, settings); + } + catch (Exception e) + { + 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 NotFound(); + } +} \ No newline at end of file diff --git a/Plugins/BTCPayServer.Plugins.Breez/BreezLightningClient.cs b/Plugins/BTCPayServer.Plugins.Breez/BreezLightningClient.cs new file mode 100644 index 0000000..5573311 --- /dev/null +++ b/Plugins/BTCPayServer.Plugins.Breez/BreezLightningClient.cs @@ -0,0 +1,281 @@ +using System; +using System.Collections.Concurrent; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Breez.Sdk; +using NBitcoin; +using Network = Breez.Sdk.Network; + +namespace BTCPayServer.Lightning.Breez; + + +public class BreezLightningClient: ILightningClient, IDisposable, EventListener +{ + private readonly NBitcoin.Network _network; + + public BreezLightningClient(string inviteCode, string apiKey, string workingDir, NBitcoin.Network network, + string mnemonic) + { + _network = network; + var nodeConfig = new NodeConfig.Greenlight( + new GreenlightNodeConfig(null, inviteCode) + ); + var config = BreezSdkMethods.DefaultConfig( + EnvironmentType.PRODUCTION, + apiKey, + nodeConfig + ) with { + workingDir= workingDir, + network = network == NBitcoin.Network.Main ? Network.BITCOIN : network == NBitcoin.Network.TestNet ? Network.TESTNET: network == NBitcoin.Network.RegTest? Network.REGTEST: Network.SIGNET + }; + var seed = BreezSdkMethods.MnemonicToSeed(mnemonic); + Sdk = BreezSdkMethods.Connect(config, seed, this); + + } + + public BlockingBreezServices Sdk { get; } + + public event EventHandler EventReceived; + public void OnEvent(BreezEvent e) + { + EventReceived?.Invoke(this, e); + } + + public Task GetInvoice(string invoiceId, CancellationToken cancellation = default) + { + return GetInvoice(uint256.Parse(invoiceId), cancellation); + } + + private LightningPayment ToLightningPayment(Payment payment) + { + if (payment?.details is not PaymentDetails.Ln lnPaymentDetails) + { + return null; + } + + return new LightningPayment() + { + Amount = LightMoney.MilliSatoshis(payment.amountMsat), + Id = lnPaymentDetails.data.paymentHash, + Preimage = lnPaymentDetails.data.paymentPreimage, + PaymentHash = lnPaymentDetails.data.paymentHash, + BOLT11 = lnPaymentDetails.data.bolt11, + Status = payment.status switch + { + PaymentStatus.FAILED => LightningPaymentStatus.Failed, + PaymentStatus.COMPLETE => LightningPaymentStatus.Complete, + PaymentStatus.PENDING => LightningPaymentStatus.Pending, + _ => throw new ArgumentOutOfRangeException() + }, + CreatedAt = DateTimeOffset.FromUnixTimeMilliseconds(payment.paymentTime), + Fee = LightMoney.MilliSatoshis(payment.feeMsat), + AmountSent = LightMoney.MilliSatoshis(payment.amountMsat) + }; + + + } + private LightningInvoice FromPayment(Payment p) + { + if (p?.details is not PaymentDetails.Ln lnPaymentDetails) + { + return null; + } + var bolt11 = BOLT11PaymentRequest.Parse(lnPaymentDetails.data.bolt11, _network); + + return new LightningInvoice() + { + Amount = LightMoney.MilliSatoshis(p.amountMsat), + Id = lnPaymentDetails.data.paymentHash, + Preimage = lnPaymentDetails.data.paymentPreimage, + PaymentHash = lnPaymentDetails.data.paymentHash, + BOLT11 = lnPaymentDetails.data.bolt11, + Status = p.status switch + { + PaymentStatus.FAILED => LightningInvoiceStatus.Expired, + PaymentStatus.COMPLETE => LightningInvoiceStatus.Paid, + _ => LightningInvoiceStatus.Unpaid + }, + PaidAt = DateTimeOffset.FromUnixTimeMilliseconds(p.paymentTime), + ExpiresAt = bolt11.ExpiryDate + }; + } + + public async Task GetInvoice(uint256 paymentHash, CancellationToken cancellation = default) + { + var p = Sdk.PaymentByHash(paymentHash.ToString()!); + return FromPayment(p); + } + + public async Task ListInvoices(CancellationToken cancellation = default) + { + + return await ListInvoices(null, cancellation); + } + + public async Task ListInvoices(ListInvoicesParams request, CancellationToken cancellation = default) + { + return Sdk.ListPayments(new ListPaymentsRequest(PaymentTypeFilter.RECEIVED, null, null, request?.PendingOnly is not true, (uint?) request?.OffsetIndex, null)) + .Select(FromPayment).ToArray(); + } + + public async Task GetPayment(string paymentHash, CancellationToken cancellation = default) + { + return ToLightningPayment(Sdk.PaymentByHash(paymentHash)); + } + + public async Task ListPayments(CancellationToken cancellation = default) + { + return await ListPayments(null, cancellation); + } + + public async Task ListPayments(ListPaymentsParams request, CancellationToken cancellation = default) + { + return Sdk.ListPayments(new ListPaymentsRequest(PaymentTypeFilter.RECEIVED, null, null, null, (uint?) request?.OffsetIndex, null)) + .Select(ToLightningPayment).ToArray(); + } + + + public async Task CreateInvoice(LightMoney amount, string description, TimeSpan expiry, CancellationToken cancellation = default) + { + + var expiryS =expiry == TimeSpan.Zero? (uint?) null: Math.Max(0, (uint)expiry.TotalSeconds); + var p = Sdk.ReceivePayment(new ReceivePaymentRequest((ulong)amount.MilliSatoshi, description, null, null, false,expiryS )); + return await GetInvoice(p.lnInvoice.paymentHash, cancellation); + } + + public async Task CreateInvoice(CreateInvoiceParams createInvoiceRequest, CancellationToken cancellation = default) + { + var expiryS =createInvoiceRequest.Expiry == TimeSpan.Zero? (uint?) null: Math.Max(0, (uint)createInvoiceRequest.Expiry.TotalSeconds); + var p = Sdk.ReceivePayment(new ReceivePaymentRequest((ulong)createInvoiceRequest.Amount.MilliSatoshi, (createInvoiceRequest.Description??createInvoiceRequest.DescriptionHash.ToString())!, null, null, createInvoiceRequest.DescriptionHashOnly,expiryS )); + return await GetInvoice(p.lnInvoice.paymentHash, cancellation); + } + + public async Task Listen(CancellationToken cancellation = default) + { + return new BreezInvoiceListener(this, cancellation); + } + + public async Task GetInfo(CancellationToken cancellation = default) + { + + var ni = Sdk.NodeInfo(); + return new LightningNodeInformation() + { + PeersCount = ni.connectedPeers.Count, + Alias = $"greenlight {ni.id}", + NodeInfoList = {new NodeInfo(new PubKey(ni.id), "blockstrean.com", 69)},//we have to fake this as btcpay currently requires this to enable the payment method + BlockHeight = (int)ni.blockHeight + }; + } + + public async Task GetBalance(CancellationToken cancellation = default) + { + var ni = Sdk.NodeInfo(); + return new LightningNodeBalance() + { + OnchainBalance = + new OnchainBalance() + { + Confirmed = Money.Coins(LightMoney.MilliSatoshis(ni.onchainBalanceMsat) + .ToUnit(LightMoneyUnit.BTC)) + }, + OffchainBalance = new OffchainBalance() + { + Local = LightMoney.MilliSatoshis(ni.channelsBalanceMsat), + Remote = LightMoney.MilliSatoshis(ni.inboundLiquidityMsats), + } + }; + } + + public async Task Pay(PayInvoiceParams payParams, CancellationToken cancellation = default) + { + return await Pay(null, payParams, cancellation); + } + + public async Task Pay(string bolt11, PayInvoiceParams payParams, CancellationToken cancellation = default) + { + throw new NotImplementedException(); + } + + public async Task Pay(string bolt11, CancellationToken cancellation = default) + { + return await Pay(bolt11, null, cancellation); + } + + public async Task OpenChannel(OpenChannelRequest openChannelRequest, CancellationToken cancellation = default) + { + throw new NotImplementedException(); + } + + public async Task GetDepositAddress(CancellationToken cancellation = default) + { + throw new NotImplementedException(); + } + + public async Task ConnectTo(NodeInfo nodeInfo, CancellationToken cancellation = default) + { + throw new NotImplementedException(); + } + + public async Task CancelInvoice(string invoiceId, CancellationToken cancellation = default) + { + + throw new NotImplementedException(); + } + + public async Task ListChannels(CancellationToken cancellation = default) + { + throw new NotImplementedException(); + } + + public void Dispose() + { + Sdk.Dispose(); + Sdk.Dispose(); + } + + public class BreezInvoiceListener: ILightningInvoiceListener + { + private readonly BreezLightningClient _breezLightningClient; + private readonly CancellationToken _cancellationToken; + + public BreezInvoiceListener(BreezLightningClient breezLightningClient, CancellationToken cancellationToken) + { + _breezLightningClient = breezLightningClient; + _cancellationToken = cancellationToken; + + breezLightningClient.EventReceived += BreezLightningClientOnEventReceived; + } + + private readonly ConcurrentQueue> _invoices = new(); + + private void BreezLightningClientOnEventReceived(object sender, BreezEvent e) + { + if (e is BreezEvent.InvoicePaid pre) + { + _invoices.Enqueue(_breezLightningClient.GetInvoice(pre.details.paymentHash, _cancellationToken)); + } + } + + public void Dispose() + { + _breezLightningClient.EventReceived -= BreezLightningClientOnEventReceived; + } + + public async Task WaitInvoice(CancellationToken cancellation) + { + while(cancellation.IsCancellationRequested is not true) + { + if (_invoices.TryDequeue(out var task)) + { + return await task.WithCancellation(cancellation); + } + await Task.Delay(100, cancellation); + } + cancellation.ThrowIfCancellationRequested(); + return null; + } + } +} + diff --git a/Plugins/BTCPayServer.Plugins.Breez/BreezLightningConnectionStringHandler.cs b/Plugins/BTCPayServer.Plugins.Breez/BreezLightningConnectionStringHandler.cs new file mode 100644 index 0000000..22ebcf7 --- /dev/null +++ b/Plugins/BTCPayServer.Plugins.Breez/BreezLightningConnectionStringHandler.cs @@ -0,0 +1,35 @@ +using System; +using System.Linq; +using System.Net.Http; +using BTCPayServer.Plugins.FixedFloat; +using NBitcoin; + +namespace BTCPayServer.Lightning.Breez; + +public class BreezLightningConnectionStringHandler : ILightningConnectionStringHandler +{ + private readonly BreezService _breezService; + + public BreezLightningConnectionStringHandler(BreezService breezService) + { + _breezService = breezService; + } + public ILightningClient Create(string connectionString, Network network, out string error) + { + var kv = LightningConnectionStringHelper.ExtractValues(connectionString, out var type); + if (type != "breez") + { + error = null; + return null; + } + + if (!kv.TryGetValue("store", out var storeId)) + { + error = $"The key 'store' is mandatory for breez connection strings"; + return null; + } + + error = null; + return _breezService.GetClient(storeId); + } +} diff --git a/Plugins/BTCPayServer.Plugins.Breez/Program.cs b/Plugins/BTCPayServer.Plugins.Breez/Program.cs new file mode 100644 index 0000000..cd9b12c --- /dev/null +++ b/Plugins/BTCPayServer.Plugins.Breez/Program.cs @@ -0,0 +1,149 @@ +#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; + private readonly BTCPayNetworkProvider _btcPayNetworkProvider; + private readonly ILogger _logger; + private Dictionary _settings; + private Dictionary _clients = new(); + + public BreezService(StoreRepository storeRepository, + IOptions dataDirectories, + BTCPayNetworkProvider btcPayNetworkProvider, + ILogger 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("Breez")).Where(pair => pair.Value is not null).ToDictionary(pair => pair.Key, pair => pair.Value!); + tcs.TrySetResult(); + } + + public async Task Get(string storeId) + { + await tcs.Task; + _settings.TryGetValue(storeId, out var settings); + return settings; + } + + public async Task 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(); + applicationBuilder.AddSingleton(provider => provider.GetRequiredService()); + applicationBuilder.AddSingleton(); + applicationBuilder.AddSingleton(provider => provider.GetRequiredService()); + applicationBuilder.AddSingleton(new UIExtension("Breez/BreezNav", + "store-integrations-nav")); + base.Execute(applicationBuilder); + } + } +} \ No newline at end of file diff --git a/Plugins/BTCPayServer.Plugins.Breez/Views/Breez/Index.cshtml b/Plugins/BTCPayServer.Plugins.Breez/Views/Breez/Index.cshtml new file mode 100644 index 0000000..43200fe --- /dev/null +++ b/Plugins/BTCPayServer.Plugins.Breez/Views/Breez/Index.cshtml @@ -0,0 +1,53 @@ +@using BTCPayServer +@using BTCPayServer.Abstractions.Extensions +@using BTCPayServer.Plugins.FixedFloat +@model BreezSettings? +@inject BreezService BreezService +@{ + ViewData.SetActivePage("Breez", "Breez", "Breez"); + var storeId = Context.GetCurrentStoreId(); +} + + + +

@ViewData["Title"]

+ + +
+
+
+
+ + + + +
+
+ + + + +
+
+ + + + +
+ + @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 diff --git a/Plugins/BTCPayServer.Plugins.Breez/Views/Shared/Breez/BreezNav.cshtml b/Plugins/BTCPayServer.Plugins.Breez/Views/Shared/Breez/BreezNav.cshtml new file mode 100644 index 0000000..eb3c78c --- /dev/null +++ b/Plugins/BTCPayServer.Plugins.Breez/Views/Shared/Breez/BreezNav.cshtml @@ -0,0 +1,27 @@ +@using BTCPayServer.Abstractions.Contracts +@using BTCPayServer.Abstractions.Extensions +@using BTCPayServer.Client +@using Microsoft.AspNetCore.Mvc.TagHelpers +@inject IScopeProvider ScopeProvider +@{ + var storeId = ScopeProvider.GetCurrentStoreId(); +} +@if (!string.IsNullOrEmpty(storeId)) +{ + +} diff --git a/Plugins/BTCPayServer.Plugins.Breez/Views/_ViewImports.cshtml b/Plugins/BTCPayServer.Plugins.Breez/Views/_ViewImports.cshtml new file mode 100644 index 0000000..52e6837 --- /dev/null +++ b/Plugins/BTCPayServer.Plugins.Breez/Views/_ViewImports.cshtml @@ -0,0 +1,2 @@ +@addTagHelper *, BTCPayServer.Abstractions +@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers