mirror of
https://github.com/aljazceru/btcpayserver.git
synced 2025-12-18 14:34:23 +01:00
Dashboard: Add Lightning balances and services (#3838)
* Update Lightning lib * Refactoring: Move Lightning methods and props to ExternalServices * Rename Lightning services * Add Lightning balance to dashboard * Split Lightning dashboard tiles * View updates
This commit is contained in:
@@ -28,7 +28,7 @@
|
||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BTCPayServer.Lightning.Common" Version="1.3.4" />
|
||||
<PackageReference Include="BTCPayServer.Lightning.Common" Version="1.3.6" />
|
||||
<PackageReference Include="NBitcoin" Version="7.0.1" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BIP78.Sender" Version="0.2.2" />
|
||||
<PackageReference Include="BTCPayServer.Hwi" Version="2.0.2" />
|
||||
<PackageReference Include="BTCPayServer.Lightning.All" Version="1.3.8" />
|
||||
<PackageReference Include="BTCPayServer.Lightning.All" Version="1.3.10" />
|
||||
<PackageReference Include="BuildBundlerMinifier" Version="3.2.449" />
|
||||
<PackageReference Include="BundlerMinifier.Core" Version="3.2.435" />
|
||||
<PackageReference Include="BundlerMinifier.TagHelpers" Version="3.2.435" />
|
||||
@@ -232,5 +232,9 @@
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<AdditionalFiles Include="Components\StoreLightningBalance\Default.cshtml" />
|
||||
</ItemGroup>
|
||||
|
||||
<ProjectExtensions><VisualStudio><UserProperties wwwroot_4swagger_4v1_4swagger_1template_1invoices_1json__JsonSchema="https://raw.githubusercontent.com/OAI/OpenAPI-Specification/master/schemas/v3.0/schema.json" wwwroot_4swagger_4v1_4swagger_1template_1json__JsonSchema="https://raw.githubusercontent.com/OAI/OpenAPI-Specification/master/schemas/v3.0/schema.json" wwwroot_4swagger_4v1_4swagger_1template_1misc_1json__JsonSchema="https://raw.githubusercontent.com/OAI/OpenAPI-Specification/master/schemas/v3.0/schema.json" wwwroot_4swagger_4v1_4swagger_1template_1pull-payments_1json__JsonSchema="https://raw.githubusercontent.com/OAI/OpenAPI-Specification/master/schemas/v3.0/schema.json" wwwroot_4swagger_4v1_4swagger_1template_1serverinfo_1json__JsonSchema="https://raw.githubusercontent.com/OAI/OpenAPI-Specification/master/schemas/v3.0/schema.json" wwwroot_4swagger_4v1_4swagger_1template_1stores_1json__JsonSchema="https://raw.githubusercontent.com/OAI/OpenAPI-Specification/master/schemas/v3.0/schema.json" wwwroot_4swagger_4v1_4swagger_1template_1users_1json__JsonSchema="https://raw.githubusercontent.com/OAI/OpenAPI-Specification/master/schemas/v3.0/schema.json" wwwroot_4swagger_4v1_4swagger_1template_1webhooks_1json__JsonSchema="https://raw.githubusercontent.com/OAI/OpenAPI-Specification/master/schemas/v3.0/schema.json" /></VisualStudio></ProjectExtensions>
|
||||
</Project>
|
||||
|
||||
92
BTCPayServer/Components/StoreLightningBalance/Default.cshtml
Normal file
92
BTCPayServer/Components/StoreLightningBalance/Default.cshtml
Normal file
@@ -0,0 +1,92 @@
|
||||
@using BTCPayServer.Lightning
|
||||
@model BTCPayServer.Components.StoreLightningBalance.StoreLightningBalanceViewModel
|
||||
|
||||
<div id="StoreLightningBalance-@Model.Store.Id" class="widget store-lightning-balance">
|
||||
<header class="mb-3">
|
||||
<h6>Lightning Balance</h6>
|
||||
<a
|
||||
asp-controller="UIPublicLightningNodeInfo"
|
||||
asp-action="ShowLightningNodeInfo"app-top-items
|
||||
asp-route-cryptoCode="@Model.CryptoCode"
|
||||
asp-route-storeId="@Model.Store.Id"
|
||||
target="_blank"
|
||||
id="PublicNodeInfo">
|
||||
Node Info
|
||||
</a>
|
||||
</header>
|
||||
|
||||
@if (Model.Balance == null)
|
||||
{
|
||||
<p>@Model.ProblemDescription</p>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="balances d-flex flex-wrap">
|
||||
<div class="balance me-3">
|
||||
<h3 class="d-inline-block me-1">@Model.TotalOffchain</h3>
|
||||
<span class="text-secondary fw-semibold text-nowrap">@Model.CryptoCode in channels</span>
|
||||
@if (Model.TotalOffchain != LightMoney.Zero && Model.Balance.OffchainBalance != null)
|
||||
{
|
||||
<div class="balance-details collapse" id="balanceDetailsOffchain">
|
||||
@if (Model.Balance.OffchainBalance.Opening != LightMoney.Zero)
|
||||
{
|
||||
<div class="mt-2">
|
||||
<span class="fw-semibold">@Model.Balance.OffchainBalance.Opening</span>
|
||||
<span class="text-secondary text-nowrap">@Model.CryptoCode opening channels</span>
|
||||
</div>
|
||||
}
|
||||
<div class="mt-2">
|
||||
<span class="fw-semibold">@Model.Balance.OffchainBalance.Local</span>
|
||||
<span class="text-secondary text-nowrap">@Model.CryptoCode local balance</span>
|
||||
</div>
|
||||
<div class="mt-2">
|
||||
<span class="fw-semibold">@Model.Balance.OffchainBalance.Remote</span>
|
||||
<span class="text-secondary text-nowrap">@Model.CryptoCode remote balance</span>
|
||||
</div>
|
||||
@if (Model.Balance.OffchainBalance.Closing != LightMoney.Zero)
|
||||
{
|
||||
<div class="mt-2">
|
||||
<span class="fw-semibold">@Model.Balance.OffchainBalance.Closing</span>
|
||||
<span class="text-secondary text-nowrap">@Model.CryptoCode closing channels</span>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<div class="balance">
|
||||
<h3 class="d-inline-block me-1">@Model.TotalOnchain</h3>
|
||||
<span class="text-secondary fw-semibold text-nowrap">@Model.CryptoCode on-chain</span>
|
||||
@if (Model.TotalOnchain != LightMoney.Zero && Model.Balance.OnchainBalance != null)
|
||||
{
|
||||
<div class="balance-details collapse" id="balanceDetailsOnchain">
|
||||
@if (Model.Balance.OnchainBalance.Confirmed != LightMoney.Zero)
|
||||
{
|
||||
<div class="mt-2">
|
||||
<span class="fw-semibold">@Model.Balance.OnchainBalance.Confirmed</span>
|
||||
<span class="text-secondary text-nowrap">@Model.CryptoCode confirmed</span>
|
||||
</div>
|
||||
}
|
||||
@if (Model.Balance.OnchainBalance.Unconfirmed != LightMoney.Zero)
|
||||
{
|
||||
<div class="mt-2">
|
||||
<span class="fw-semibold">@Model.Balance.OnchainBalance.Unconfirmed</span>
|
||||
<span class="text-secondary text-nowrap">@Model.CryptoCode unconfirmed</span>
|
||||
</div>
|
||||
}
|
||||
@if (Model.Balance.OnchainBalance.Reserved != LightMoney.Zero)
|
||||
{
|
||||
<div class="mt-2">
|
||||
<span class="fw-semibold">@Model.Balance.OnchainBalance.Reserved</span>
|
||||
<span class="text-secondary text-nowrap">@Model.CryptoCode reserved</span>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
@if (Model.Balance.OffchainBalance != null && Model.Balance.OnchainBalance != null && (Model.TotalOffchain != LightMoney.Zero || Model.TotalOnchain != LightMoney.Zero))
|
||||
{
|
||||
<a class="d-inline-block mt-3" role="button" data-bs-toggle="collapse" data-bs-target=".balance-details" aria-expanded="false" aria-controls="balanceDetailsOffchain balanceDetailsOnchain">Show details</a>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
@@ -0,0 +1,108 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Abstractions.Extensions;
|
||||
using BTCPayServer.Configuration;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.Lightning;
|
||||
using BTCPayServer.Models;
|
||||
using BTCPayServer.Models.StoreViewModels;
|
||||
using BTCPayServer.Payments;
|
||||
using BTCPayServer.Payments.Lightning;
|
||||
using BTCPayServer.Services;
|
||||
using BTCPayServer.Services.Stores;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace BTCPayServer.Components.StoreLightningBalance;
|
||||
|
||||
public class StoreLightningBalance : ViewComponent
|
||||
{
|
||||
private string _cryptoCode;
|
||||
private readonly StoreRepository _storeRepo;
|
||||
private readonly BTCPayNetworkBase _network;
|
||||
private readonly BTCPayServerOptions _btcpayServerOptions;
|
||||
private readonly BTCPayNetworkProvider _networkProvider;
|
||||
private readonly LightningClientFactoryService _lightningClientFactory;
|
||||
private readonly IOptions<LightningNetworkOptions> _lightningNetworkOptions;
|
||||
private readonly IOptions<ExternalServicesOptions> _externalServiceOptions;
|
||||
|
||||
public StoreLightningBalance(
|
||||
StoreRepository storeRepo,
|
||||
BTCPayNetworkProvider networkProvider,
|
||||
BTCPayServerOptions btcpayServerOptions,
|
||||
LightningClientFactoryService lightningClientFactory,
|
||||
IOptions<LightningNetworkOptions> lightningNetworkOptions,
|
||||
IOptions<ExternalServicesOptions> externalServiceOptions)
|
||||
{
|
||||
_storeRepo = storeRepo;
|
||||
_networkProvider = networkProvider;
|
||||
_btcpayServerOptions = btcpayServerOptions;
|
||||
_externalServiceOptions = externalServiceOptions;
|
||||
_lightningClientFactory = lightningClientFactory;
|
||||
_lightningNetworkOptions = lightningNetworkOptions;
|
||||
_network = _networkProvider.DefaultNetwork;
|
||||
_cryptoCode = _network.CryptoCode;
|
||||
}
|
||||
|
||||
public async Task<IViewComponentResult> InvokeAsync(StoreData store)
|
||||
{
|
||||
var walletId = new WalletId(store.Id, _cryptoCode);
|
||||
var lightningClient = GetLightningClient(store);
|
||||
var vm = new StoreLightningBalanceViewModel
|
||||
{
|
||||
Store = store,
|
||||
CryptoCode = _cryptoCode,
|
||||
WalletId = walletId
|
||||
};
|
||||
|
||||
if (lightningClient != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
var balance = await lightningClient.GetBalance();
|
||||
vm.Balance = balance;
|
||||
vm.TotalOnchain = balance.OnchainBalance != null
|
||||
? balance.OnchainBalance.Confirmed + balance.OnchainBalance.Reserved +
|
||||
balance.OnchainBalance.Unconfirmed
|
||||
: LightMoney.Zero;
|
||||
vm.TotalOffchain = balance.OffchainBalance != null
|
||||
? balance.OffchainBalance.Opening + balance.OffchainBalance.Local +
|
||||
balance.OffchainBalance.Closing
|
||||
: LightMoney.Zero;
|
||||
}
|
||||
catch (NotSupportedException)
|
||||
{
|
||||
// not all implementations support balance fetching
|
||||
vm.ProblemDescription = "Your node does not support balance fetching.";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
vm.ProblemDescription = "Cannot instantiate Lightning client.";
|
||||
}
|
||||
|
||||
return View(vm);
|
||||
}
|
||||
|
||||
private ILightningClient GetLightningClient(StoreData store)
|
||||
{
|
||||
var network = _networkProvider.GetNetwork<BTCPayNetwork>(_cryptoCode);
|
||||
var id = new PaymentMethodId(_cryptoCode, PaymentTypes.LightningLike);
|
||||
var existing = store.GetSupportedPaymentMethods(_networkProvider)
|
||||
.OfType<LightningSupportedPaymentMethod>()
|
||||
.FirstOrDefault(d => d.PaymentId == id);
|
||||
if (existing == null) return null;
|
||||
|
||||
if (existing.GetExternalLightningUrl() is {} connectionString)
|
||||
{
|
||||
return _lightningClientFactory.Create(connectionString, network);
|
||||
}
|
||||
if (existing.IsInternalNode && _lightningNetworkOptions.Value.InternalLightningByCryptoCode.TryGetValue(_cryptoCode, out var internalLightningNode))
|
||||
{
|
||||
return _lightningClientFactory.Create(internalLightningNode, network);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using System.Collections.Generic;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.Lightning;
|
||||
using BTCPayServer.Models;
|
||||
using BTCPayServer.Models.StoreViewModels;
|
||||
|
||||
namespace BTCPayServer.Components.StoreLightningBalance;
|
||||
|
||||
public class StoreLightningBalanceViewModel
|
||||
{
|
||||
public string CryptoCode { get; set; }
|
||||
public StoreData Store { get; set; }
|
||||
public WalletId WalletId { get; set; }
|
||||
public LightMoney TotalOnchain { get; set; }
|
||||
public LightMoney TotalOffchain { get; set; }
|
||||
public LightningNodeBalance Balance { get; set; }
|
||||
public string ProblemDescription { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
@using Microsoft.AspNetCore.Mvc.TagHelpers
|
||||
@model BTCPayServer.Components.StoreLightningServices.StoreLightningServicesViewModel
|
||||
|
||||
@if (Model.Services != null && Model.Services.Any())
|
||||
{
|
||||
<div id="StoreLightningServices-@Model.Store.Id" class="widget store-lightning-services">
|
||||
<header class="mb-4">
|
||||
<h6>Lightning Services</h6>
|
||||
<a class asp-controller="UIStores" asp-action="Lightning" asp-route-storeId="@Model.Store.Id" asp-route-cryptoCode="@Model.CryptoCode">Details</a>
|
||||
</header>
|
||||
<div id="Services" class="services-list">
|
||||
@foreach (var service in Model.Services)
|
||||
{
|
||||
@if (!string.IsNullOrEmpty(service.Error))
|
||||
{
|
||||
<div class="service" id="@($"Service-{service.ServiceName}")">
|
||||
<img src="@($"~/img/{service.Type.ToLower()}.png")" asp-append-version="true" alt="@service.DisplayName" />
|
||||
<small class="d-block mt-3 lh-sm fw-semibold text-danger">@service.Error</small>
|
||||
</div>
|
||||
}
|
||||
else if (string.IsNullOrEmpty(service.Link))
|
||||
{
|
||||
<a asp-controller="UIServer" asp-action="Service" asp-route-serviceName="@service.ServiceName" asp-route-cryptoCode="@service.CryptoCode" class="service" id="@($"Service-{service.ServiceName}")">
|
||||
<img src="@($"~/img/{service.Type.ToLower()}.png")" asp-append-version="true" alt="@service.DisplayName" />
|
||||
</a>
|
||||
}
|
||||
else
|
||||
{
|
||||
<a href="@service.Link" target="_blank" rel="noreferrer noopener" class="service" id="@($"Service-{service.ServiceName}")">
|
||||
<img src="@($"~/img/{service.Type.ToLower()}.png")" asp-append-version="true" alt="@service.DisplayName" />
|
||||
</a>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Abstractions.Extensions;
|
||||
using BTCPayServer.Configuration;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.Lightning;
|
||||
using BTCPayServer.Models;
|
||||
using BTCPayServer.Models.StoreViewModels;
|
||||
using BTCPayServer.Payments;
|
||||
using BTCPayServer.Payments.Lightning;
|
||||
using BTCPayServer.Services;
|
||||
using BTCPayServer.Services.Stores;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace BTCPayServer.Components.StoreLightningServices;
|
||||
|
||||
public class StoreLightningServices : ViewComponent
|
||||
{
|
||||
private readonly string _cryptoCode;
|
||||
private readonly BTCPayServerOptions _btcpayServerOptions;
|
||||
private readonly BTCPayNetworkProvider _networkProvider;
|
||||
private readonly IOptions<ExternalServicesOptions> _externalServiceOptions;
|
||||
|
||||
public StoreLightningServices(
|
||||
BTCPayNetworkProvider networkProvider,
|
||||
BTCPayServerOptions btcpayServerOptions,
|
||||
IOptions<ExternalServicesOptions> externalServiceOptions)
|
||||
{
|
||||
_networkProvider = networkProvider;
|
||||
_btcpayServerOptions = btcpayServerOptions;
|
||||
_externalServiceOptions = externalServiceOptions;
|
||||
_cryptoCode = _networkProvider.DefaultNetwork.CryptoCode;
|
||||
}
|
||||
|
||||
public IViewComponentResult Invoke(StoreData store)
|
||||
{
|
||||
var vm = new StoreLightningServicesViewModel
|
||||
{
|
||||
Store = store,
|
||||
CryptoCode = _cryptoCode,
|
||||
};
|
||||
|
||||
if (vm.LightningNodeType == LightningNodeType.Internal)
|
||||
{
|
||||
var services = _externalServiceOptions.Value.ExternalServices.ToList()
|
||||
.Where(service => ExternalServices.LightningServiceTypes.Contains(service.Type))
|
||||
.Select(async service =>
|
||||
{
|
||||
var model = new AdditionalServiceViewModel
|
||||
{
|
||||
DisplayName = service.DisplayName,
|
||||
ServiceName = service.ServiceName,
|
||||
CryptoCode = service.CryptoCode,
|
||||
Type = service.Type.ToString()
|
||||
};
|
||||
try
|
||||
{
|
||||
model.Link = await service.GetLink(Request.GetAbsoluteUriNoPathBase(), _btcpayServerOptions.NetworkType);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
model.Error = exception.Message;
|
||||
}
|
||||
return model;
|
||||
})
|
||||
.Select(t => t.Result)
|
||||
.ToList();
|
||||
|
||||
// other services
|
||||
foreach ((string key, Uri value) in _externalServiceOptions.Value.OtherExternalServices)
|
||||
{
|
||||
if (ExternalServices.LightningServiceNames.Contains(key))
|
||||
{
|
||||
services.Add(new AdditionalServiceViewModel
|
||||
{
|
||||
DisplayName = key,
|
||||
ServiceName = key,
|
||||
Type = key.Replace(" ", ""),
|
||||
Link = Request.GetAbsoluteUriNoPathBase(value).AbsoluteUri
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
vm.Services = services;
|
||||
}
|
||||
|
||||
return View(vm);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using System.Collections.Generic;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.Lightning;
|
||||
using BTCPayServer.Models;
|
||||
using BTCPayServer.Models.StoreViewModels;
|
||||
|
||||
namespace BTCPayServer.Components.StoreLightningServices;
|
||||
|
||||
public class StoreLightningServicesViewModel
|
||||
{
|
||||
public string CryptoCode { get; set; }
|
||||
public StoreData Store { get; set; }
|
||||
public LightningNodeType LightningNodeType { get; set; }
|
||||
public List<AdditionalServiceViewModel> Services { get; set; }
|
||||
}
|
||||
@@ -2,7 +2,9 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using NBitcoin;
|
||||
|
||||
namespace BTCPayServer.Configuration
|
||||
{
|
||||
@@ -15,13 +17,13 @@ namespace BTCPayServer.Configuration
|
||||
"lnd server: 'server=https://lnd.example.com;macaroonfilepath=/root/.lnd/admin.macaroon;certthumbprint=2abdf302...'" + Environment.NewLine +
|
||||
"lnd server: 'server=https://lnd.example.com;macaroondirectorypath=/root/.lnd;certthumbprint=2abdf302...'" + Environment.NewLine +
|
||||
"Error: {1}",
|
||||
"LND (gRPC server)");
|
||||
"LND (gRPC)");
|
||||
Load(configuration, cryptoCode, "lndrest", ExternalServiceTypes.LNDRest, "Invalid setting {0}, " + Environment.NewLine +
|
||||
"lnd server: 'server=https://lnd.example.com;macaroon=abf239...;certthumbprint=2abdf302...'" + Environment.NewLine +
|
||||
"lnd server: 'server=https://lnd.example.com;macaroonfilepath=/root/.lnd/admin.macaroon;certthumbprint=2abdf302...'" + Environment.NewLine +
|
||||
"lnd server: 'server=https://lnd.example.com;macaroondirectorypath=/root/.lnd;certthumbprint=2abdf302...'" + Environment.NewLine +
|
||||
"Error: {1}",
|
||||
"LND (REST server)");
|
||||
"LND (REST)");
|
||||
Load(configuration, cryptoCode, "lndseedbackup", ExternalServiceTypes.LNDSeedBackup, "Invalid setting {0}, " + Environment.NewLine +
|
||||
"lnd seed backup: /etc/merchant_lnd/data/chain/bitcoin/regtest/walletunlock.json'" + Environment.NewLine +
|
||||
"Error: {1}",
|
||||
@@ -29,11 +31,11 @@ namespace BTCPayServer.Configuration
|
||||
Load(configuration, cryptoCode, "spark", ExternalServiceTypes.Spark, "Invalid setting {0}, " + Environment.NewLine +
|
||||
$"Valid example: 'server=https://btcpay.example.com/spark/btc/;cookiefile=/etc/clightning_bitcoin_spark/.cookie'" + Environment.NewLine +
|
||||
"Error: {1}",
|
||||
"C-Lightning (Spark server)");
|
||||
"Core Lightning (Spark)");
|
||||
Load(configuration, cryptoCode, "rtl", ExternalServiceTypes.RTL, "Invalid setting {0}, " + Environment.NewLine +
|
||||
$"Valid example: 'server=https://btcpay.example.com/rtl/btc/;cookiefile=/etc/clightning_bitcoin_rtl/.cookie'" + Environment.NewLine +
|
||||
"Error: {1}",
|
||||
"Ride the Lightning server");
|
||||
"Ride The Lightning");
|
||||
Load(configuration, cryptoCode, "thunderhub", ExternalServiceTypes.ThunderHub, "Invalid setting {0}, " + Environment.NewLine +
|
||||
$"Valid example: 'server=https://btcpay.example.com/thub/;cookiefile=/etc/clightning_bitcoin_rtl/.cookie'" + Environment.NewLine +
|
||||
"Error: {1}",
|
||||
@@ -41,12 +43,12 @@ namespace BTCPayServer.Configuration
|
||||
Load(configuration, cryptoCode, "clightningrest", ExternalServiceTypes.CLightningRest, "Invalid setting {0}, " + Environment.NewLine +
|
||||
$"Valid example: 'server=https://btcpay.example.com/clightning-rest/btc/;cookiefile=/etc/clightning_bitcoin_rtl/.cookie'" + Environment.NewLine +
|
||||
"Error: {1}",
|
||||
"C-Lightning REST");
|
||||
"Core Lightning (REST)");
|
||||
Load(configuration, cryptoCode, "charge", ExternalServiceTypes.Charge, "Invalid setting {0}, " + Environment.NewLine +
|
||||
$"lightning charge server: 'type=charge;server=https://charge.example.com;api-token=2abdf302...'" + Environment.NewLine +
|
||||
$"lightning charge server: 'type=charge;server=https://charge.example.com;cookiefilepath=/root/.charge/.cookie'" + Environment.NewLine +
|
||||
"Error: {1}",
|
||||
"C-Lightning (Charge server)");
|
||||
"Core Lightning (Charge)");
|
||||
|
||||
}
|
||||
|
||||
@@ -86,6 +88,18 @@ namespace BTCPayServer.Configuration
|
||||
&&
|
||||
o.ServiceName.Equals(serviceName, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
public static readonly ExternalServiceTypes[] LightningServiceTypes =
|
||||
{
|
||||
ExternalServiceTypes.Spark,
|
||||
ExternalServiceTypes.RTL,
|
||||
ExternalServiceTypes.ThunderHub
|
||||
};
|
||||
|
||||
public static readonly string[] LightningServiceNames =
|
||||
{
|
||||
"Lightning Terminal"
|
||||
};
|
||||
}
|
||||
|
||||
public class ExternalService
|
||||
@@ -95,6 +109,13 @@ namespace BTCPayServer.Configuration
|
||||
public ExternalConnectionString ConnectionString { get; set; }
|
||||
public string CryptoCode { get; set; }
|
||||
public string ServiceName { get; set; }
|
||||
|
||||
public async Task<string> GetLink(Uri absoluteUriNoPathBase, ChainName networkType)
|
||||
{
|
||||
var connectionString = await ConnectionString.Expand(absoluteUriNoPathBase, Type, networkType);
|
||||
var tokenParam = Type == ExternalServiceTypes.ThunderHub ? "token" : "access-key";
|
||||
return $"{connectionString.Server}?{tokenParam}={connectionString.AccessKey}";
|
||||
}
|
||||
}
|
||||
|
||||
public enum ExternalServiceTypes
|
||||
|
||||
@@ -20,17 +20,6 @@ namespace BTCPayServer.Controllers
|
||||
{
|
||||
public partial class UIStoresController
|
||||
{
|
||||
private readonly ExternalServiceTypes[] _externalServiceTypes =
|
||||
{
|
||||
ExternalServiceTypes.Spark,
|
||||
ExternalServiceTypes.RTL,
|
||||
ExternalServiceTypes.ThunderHub
|
||||
};
|
||||
private readonly string[] _externalServiceNames =
|
||||
{
|
||||
"Lightning Terminal"
|
||||
};
|
||||
|
||||
[HttpGet("{storeId}/lightning/{cryptoCode}")]
|
||||
public IActionResult Lightning(string storeId, string cryptoCode)
|
||||
{
|
||||
@@ -48,7 +37,7 @@ namespace BTCPayServer.Controllers
|
||||
if (vm.LightningNodeType == LightningNodeType.Internal)
|
||||
{
|
||||
var services = _externalServiceOptions.Value.ExternalServices.ToList()
|
||||
.Where(service => _externalServiceTypes.Contains(service.Type))
|
||||
.Where(service => ExternalServices.LightningServiceTypes.Contains(service.Type))
|
||||
.Select(async service =>
|
||||
{
|
||||
var model = new AdditionalServiceViewModel
|
||||
@@ -60,7 +49,7 @@ namespace BTCPayServer.Controllers
|
||||
};
|
||||
try
|
||||
{
|
||||
model.Link = await GetServiceLink(service);
|
||||
model.Link = await service.GetLink(Request.GetAbsoluteUriNoPathBase(), _BtcpayServerOptions.NetworkType);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
@@ -74,7 +63,7 @@ namespace BTCPayServer.Controllers
|
||||
// other services
|
||||
foreach ((string key, Uri value) in _externalServiceOptions.Value.OtherExternalServices)
|
||||
{
|
||||
if (_externalServiceNames.Contains(key))
|
||||
if (ExternalServices.LightningServiceNames.Contains(key))
|
||||
{
|
||||
services.Add(new AdditionalServiceViewModel
|
||||
{
|
||||
@@ -398,12 +387,5 @@ namespace BTCPayServer.Controllers
|
||||
.FirstOrDefault(d => d.PaymentId == id);
|
||||
return existing;
|
||||
}
|
||||
|
||||
private async Task<string> GetServiceLink(ExternalService service)
|
||||
{
|
||||
var connectionString = await service.ConnectionString.Expand(Request.GetAbsoluteUriNoPathBase(), service.Type, _BtcpayServerOptions.NetworkType);
|
||||
var tokenParam = service.Type == ExternalServiceTypes.ThunderHub ? "token" : "access-key";
|
||||
return $"{connectionString.Server}?{tokenParam}={connectionString.AccessKey}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,16 +22,12 @@ namespace BTCPayServer.Payments.Lightning
|
||||
public LightningConnectionString? GetExternalLightningUrl()
|
||||
{
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
if (!string.IsNullOrEmpty(LightningConnectionString))
|
||||
{
|
||||
if (string.IsNullOrEmpty(LightningConnectionString)) return null;
|
||||
if (!BTCPayServer.Lightning.LightningConnectionString.TryParse(LightningConnectionString, false, out var connectionString, out var error))
|
||||
{
|
||||
throw new FormatException(error);
|
||||
}
|
||||
return connectionString;
|
||||
}
|
||||
else
|
||||
return null;
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
}
|
||||
|
||||
|
||||
@@ -85,6 +85,11 @@
|
||||
</div>
|
||||
}
|
||||
<vc:store-numbers store="@store"/>
|
||||
@if (Model.LightningEnabled)
|
||||
{
|
||||
<vc:store-lightning-balance store="@store"/>
|
||||
<vc:store-lightning-services store="@store"/>
|
||||
}
|
||||
@if (Model.WalletEnabled)
|
||||
{
|
||||
<vc:store-recent-transactions store="@store"/>
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
|
||||
@if (Model.Services != null && Model.Services.Any())
|
||||
{
|
||||
<div permission="@Policies.CanModifyServerSettings" class="mt-4">
|
||||
<div permission="@Policies.CanModifyServerSettings" class="mt-5">
|
||||
<h3 class="mb-3">Services</h3>
|
||||
<div id="Services" class="services-list">
|
||||
@foreach (var service in Model.Services)
|
||||
|
||||
@@ -233,12 +233,13 @@ svg.icon-note {
|
||||
/* Services */
|
||||
.services-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: var(--btcpay-space-l);
|
||||
}
|
||||
|
||||
.services-list .service {
|
||||
--service-width: 100px;
|
||||
flex: 0 0 var(--service-width);
|
||||
margin: 0 var(--btcpay-space-l) var(--btcpay-space-l) 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@@ -334,6 +335,18 @@ svg.icon-note {
|
||||
color: var(--btcpay-body-link-accent);
|
||||
}
|
||||
|
||||
.widget.store-lightning-balance .balances {
|
||||
gap: 1.5rem 2.25rem;
|
||||
}
|
||||
|
||||
.widget.store-lightning-services .services-list {
|
||||
gap: var(--btcpay-space-m);
|
||||
}
|
||||
|
||||
.widget.store-lightning-services .services-list .service {
|
||||
--service-width: 3rem;
|
||||
}
|
||||
|
||||
.widget.store-numbers {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
@@ -390,6 +403,10 @@ svg.icon-note {
|
||||
}
|
||||
|
||||
@media (max-width: 575px) {
|
||||
.widget.store-lightning-services .services-list .service {
|
||||
--service-width: 3rem;
|
||||
}
|
||||
|
||||
.widget .store-number {
|
||||
flex: 0 1 100%;
|
||||
}
|
||||
@@ -401,15 +418,41 @@ svg.icon-note {
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 576px) and (max-width: 1199px) {
|
||||
.widget.store-lightning-services .services-list {
|
||||
gap: 1.5rem;
|
||||
}
|
||||
.widget.store-lightning-services .services-list .service {
|
||||
--service-width: 4rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 1199px) {
|
||||
/* Reorder so that Lightning is below the wallet balance */
|
||||
.widget.store-wallet-balance {
|
||||
order: -3;
|
||||
}
|
||||
|
||||
.widget.store-lightning-balance {
|
||||
order: -2;
|
||||
}
|
||||
|
||||
.widget.store-lightning-services {
|
||||
order: -1;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1200px) {
|
||||
.widget.app-sales,
|
||||
.widget.setup-guide,
|
||||
.widget.store-wallet-balance {
|
||||
.widget.store-wallet-balance,
|
||||
.widget.store-lightning-balance {
|
||||
--widget-chart-width: 80vw;
|
||||
grid-column-start: 1;
|
||||
grid-column-end: 9;
|
||||
}
|
||||
|
||||
.widget.store-lightning-services,
|
||||
.widget.app-top-items,
|
||||
.widget.store-numbers {
|
||||
grid-column-start: 9;
|
||||
|
||||
Reference in New Issue
Block a user