mirror of
https://github.com/btcpayserver/btcpayserver.git
synced 2025-02-20 13:34:37 +01:00
Improve public LN node info (#2876)
* Add store name to LN info unavailable pae * Display multiple node info items if available Allows to view clearnet and Tor connection info side by side. * Re-add preferOnion for certain cases * HTML compatible node ids * Display more node connection failure details * Fix syntax error * Update BTCPayServer/Payments/Lightning/LightningLikePaymentHandler.cs Co-authored-by: Nicolas Dorier <nicolas.dorier@gmail.com> * View updates * Revert previous variable change * Keep logic out of the view Co-authored-by: Nicolas Dorier <nicolas.dorier@gmail.com>
This commit is contained in:
parent
5d8bc73063
commit
7f49824783
4 changed files with 73 additions and 25 deletions
|
@ -3,6 +3,7 @@ using System.Linq;
|
|||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.Filters;
|
||||
using BTCPayServer.Lightning;
|
||||
using BTCPayServer.Payments;
|
||||
using BTCPayServer.Payments.Lightning;
|
||||
using BTCPayServer.Services.Stores;
|
||||
|
@ -40,14 +41,12 @@ namespace BTCPayServer.Controllers
|
|||
{
|
||||
var paymentMethodDetails = GetExistingLightningSupportedPaymentMethod(cryptoCode, store);
|
||||
var network = _BtcPayNetworkProvider.GetNetwork<BTCPayNetwork>(cryptoCode);
|
||||
var nodeInfo =
|
||||
await _LightningLikePaymentHandler.GetNodeInfo(Request.IsOnion(), paymentMethodDetails,
|
||||
network);
|
||||
var nodeInfo = await _LightningLikePaymentHandler.GetNodeInfo(paymentMethodDetails, network);
|
||||
|
||||
return View(new ShowLightningNodeInfoViewModel
|
||||
{
|
||||
Available = true,
|
||||
NodeInfo = nodeInfo.ToString(),
|
||||
NodeInfo = nodeInfo.Select(n => new ShowLightningNodeInfoViewModel.NodeData(n)).ToArray(),
|
||||
CryptoCode = cryptoCode,
|
||||
CryptoImage = GetImage(paymentMethodDetails.PaymentId, network),
|
||||
StoreName = store.StoreName
|
||||
|
@ -55,7 +54,12 @@ namespace BTCPayServer.Controllers
|
|||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return View(new ShowLightningNodeInfoViewModel { Available = false, CryptoCode = cryptoCode });
|
||||
return View(new ShowLightningNodeInfoViewModel
|
||||
{
|
||||
Available = false,
|
||||
CryptoCode = cryptoCode,
|
||||
StoreName = store.StoreName
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -78,9 +82,26 @@ namespace BTCPayServer.Controllers
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
public class ShowLightningNodeInfoViewModel
|
||||
{
|
||||
public string NodeInfo { get; set; }
|
||||
public class NodeData
|
||||
{
|
||||
string _connection;
|
||||
public NodeData(NodeInfo nodeInfo)
|
||||
{
|
||||
_connection = nodeInfo.ToString();
|
||||
Id = $"{nodeInfo.Host}-{nodeInfo.Port}".Replace(".", "-", StringComparison.OrdinalIgnoreCase);
|
||||
IsTor = nodeInfo.IsTor;
|
||||
}
|
||||
public string Id { get; }
|
||||
public bool IsTor { get; }
|
||||
public override string ToString()
|
||||
{
|
||||
return _connection;
|
||||
}
|
||||
}
|
||||
public NodeData[] NodeInfo { get; set; }
|
||||
public bool Available { get; set; }
|
||||
public string CryptoCode { get; set; }
|
||||
public string CryptoImage { get; set; }
|
||||
|
|
|
@ -109,13 +109,13 @@ namespace BTCPayServer.Controllers
|
|||
var handler = _ServiceProvider.GetRequiredService<LightningLikePaymentHandler>();
|
||||
try
|
||||
{
|
||||
var info = await handler.GetNodeInfo(Request.IsOnion(), paymentMethod, network);
|
||||
var info = await handler.GetNodeInfo(paymentMethod, network, Request.IsOnion());
|
||||
if (!vm.SkipPortTest)
|
||||
{
|
||||
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(20));
|
||||
await handler.TestConnection(info, cts.Token);
|
||||
await handler.TestConnection(info.First(), cts.Token);
|
||||
}
|
||||
TempData[WellKnownTempData.SuccessMessage] = $"Connection to the Lightning node successful. Your node address: {info}";
|
||||
TempData[WellKnownTempData.SuccessMessage] = $"Connection to the Lightning node successful. Your node address: {info.First()}";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
|
|
@ -68,7 +68,7 @@ namespace BTCPayServer.Payments.Lightning
|
|||
}
|
||||
//direct casting to (BTCPayNetwork) is fixed in other pull requests with better generic interfacing for handlers
|
||||
var storeBlob = store.GetStoreBlob();
|
||||
var test = GetNodeInfo(paymentMethod.PreferOnion, supportedPaymentMethod, network);
|
||||
var test = GetNodeInfo(supportedPaymentMethod, network, paymentMethod.PreferOnion);
|
||||
|
||||
var invoice = paymentMethod.ParentEntity;
|
||||
decimal due = Extensions.RoundUp(invoice.Price / paymentMethod.Rate, network.Divisibility);
|
||||
|
@ -108,17 +108,18 @@ namespace BTCPayServer.Payments.Lightning
|
|||
throw new PaymentMethodUnavailableException($"Impossible to create lightning invoice ({ex.Message})", ex);
|
||||
}
|
||||
}
|
||||
|
||||
var nodeInfo = await test;
|
||||
return new LightningLikePaymentMethodDetails
|
||||
{
|
||||
Activated = true,
|
||||
BOLT11 = lightningInvoice.BOLT11,
|
||||
InvoiceId = lightningInvoice.Id,
|
||||
NodeInfo = nodeInfo.ToString()
|
||||
NodeInfo = nodeInfo.First().ToString()
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<NodeInfo> GetNodeInfo(bool preferOnion, LightningSupportedPaymentMethod supportedPaymentMethod, BTCPayNetwork network)
|
||||
public async Task<NodeInfo[]> GetNodeInfo(LightningSupportedPaymentMethod supportedPaymentMethod, BTCPayNetwork network, bool? preferOnion = null)
|
||||
{
|
||||
if (!_Dashboard.IsFullySynched(network.CryptoCode, out var summary))
|
||||
throw new PaymentMethodUnavailableException("Full node not available");
|
||||
|
@ -137,10 +138,15 @@ namespace BTCPayServer.Payments.Lightning
|
|||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new PaymentMethodUnavailableException($"Error while connecting to the API ({ex.Message})");
|
||||
throw new PaymentMethodUnavailableException($"Error while connecting to the API: {ex.Message}" +
|
||||
(!string.IsNullOrEmpty(ex.InnerException?.Message) ? $" ({ex.InnerException.Message})" : ""));
|
||||
}
|
||||
var nodeInfo = info.NodeInfoList.FirstOrDefault(i => i.IsTor == preferOnion) ?? info.NodeInfoList.FirstOrDefault();
|
||||
if (nodeInfo == null)
|
||||
|
||||
var nodeInfo = preferOnion != null && info.NodeInfoList.Any(i => i.IsTor == preferOnion)
|
||||
? info.NodeInfoList.Where(i => i.IsTor == preferOnion.Value).ToArray()
|
||||
: info.NodeInfoList.Select(i => i).ToArray();
|
||||
|
||||
if (!nodeInfo.Any())
|
||||
{
|
||||
throw new PaymentMethodUnavailableException("No lightning node public address has been configured");
|
||||
}
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
@addTagHelper *, BundlerMinifier.TagHelpers
|
||||
@addTagHelper *, BundlerMinifier.TagHelpers
|
||||
@inject ISettingsRepository _settingsRepository
|
||||
|
||||
@using BTCPayServer.Abstractions.Contracts
|
||||
@using BTCPayServer.Lightning
|
||||
@model BTCPayServer.Controllers.ShowLightningNodeInfoViewModel
|
||||
@{
|
||||
Layout = null;
|
||||
|
@ -28,7 +29,7 @@
|
|||
<div class="card border-0">
|
||||
<div class="card-body p-4">
|
||||
<h1 class="card-title text-center">@Model.StoreName</h1>
|
||||
<h2 class="card-subtitle text-center text-secondary mb-2">
|
||||
<h2 class="card-subtitle text-center text-secondary my-3">
|
||||
<span>@Model.CryptoCode</span>
|
||||
Lightning Node
|
||||
</h2>
|
||||
|
@ -42,13 +43,33 @@
|
|||
</h3>
|
||||
@if (Model.Available)
|
||||
{
|
||||
<div class="qr-container my-3">
|
||||
<img alt="@Model.CryptoCode" class="qr-icon" src="@Model.CryptoImage" />
|
||||
<vc:qr-code data="@Model.NodeInfo"> </vc:qr-code>
|
||||
</div>
|
||||
<div class="input-group d-flex" data-clipboard="@Model.NodeInfo">
|
||||
<input type="text" class="form-control" style="cursor: copy" readonly="readonly" value="@Model.NodeInfo" id="peer-info"/>
|
||||
<button type="button" class="btn btn-outline-secondary" data-clipboard-confirm>Copy node info</button>
|
||||
@if (Model.NodeInfo.Length > 1)
|
||||
{
|
||||
<ul class="nav nav-pills justify-content-center mt-4" id="nodeInfo-tab" role="tablist">
|
||||
@for (int i = 0; i < Model.NodeInfo.Length; i++)
|
||||
{
|
||||
var nodeInfo = Model.NodeInfo[i];
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link w-100px @(i == 0 ? "active" : "")" id="nodeInfo-tab-@nodeInfo.Id" data-bs-toggle="pill" data-bs-target="#nodeInfo-@nodeInfo.Id" type="button" role="tab" aria-controls="nodeInfo-@nodeInfo.Id" aria-selected="true">@(Model.NodeInfo[i].IsTor ? "Tor" : "Clearnet")</button>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
}
|
||||
<div class="tab-content" id="nodeInfo-tabContent">
|
||||
@for (int i = 0; i < Model.NodeInfo.Length; i++)
|
||||
{
|
||||
var nodeInfo = Model.NodeInfo[i];
|
||||
<div class="tab-pane fade @(i == 0 ? "show active" : "")" id="nodeInfo-@nodeInfo.Id" role="tabpanel" aria-labelledby="nodeInfo-tab-@nodeInfo.Id">
|
||||
<div class="qr-container my-3">
|
||||
<img alt="@Model.CryptoCode" class="qr-icon" src="@Model.CryptoImage"/>
|
||||
<vc:qr-code data="@nodeInfo.ToString()"/>
|
||||
</div>
|
||||
<div class="input-group" data-clipboard="@nodeInfo.ToString()">
|
||||
<input type="text" class="form-control" style="cursor:copy" readonly="readonly" value="@nodeInfo.ToString()" id="nodeInfo-addr-@nodeInfo.Id"/>
|
||||
<button type="button" class="btn btn-outline-secondary" data-clipboard-confirm>Copy</button>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
@ -56,6 +77,6 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="~/js/copy-to-clipboard.js" asp-append-version="true"></script>
|
||||
<partial name="LayoutFoot" />
|
||||
</body>
|
||||
</html>
|
||||
|
|
Loading…
Add table
Reference in a new issue