btcpayserver/BTCPayServer/Components/MainNav/MainNav.cs
d11n 8d429f064b
Show Lightning node availability in navigation (#5951)
* Show Lightning node availability in navigation

Instead of simply communicating the setup state of the store's LN node, this now also checks its availability.

Closes  #5940.

* Cleanups

* Add Selenium test for public node page and status in nav

* Cache the available lightning node result

---------

Co-authored-by: nicolas.dorier <nicolas.dorier@gmail.com>
2024-04-26 08:30:34 +02:00

133 lines
4.7 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using BTCPayServer.Controllers;
using BTCPayServer.Data;
using BTCPayServer.Lightning;
using BTCPayServer.Models.StoreViewModels;
using BTCPayServer.Payments;
using BTCPayServer.Payments.Lightning;
using BTCPayServer.Services;
using BTCPayServer.Services.Apps;
using BTCPayServer.Services.Invoices;
using BTCPayServer.Services.Stores;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.Extensions.Caching.Memory;
using NBitcoin;
using NBitcoin.Secp256k1;
namespace BTCPayServer.Components.MainNav
{
public class MainNav : ViewComponent
{
private readonly AppService _appService;
private readonly StoreRepository _storeRepo;
private readonly UIStoresController _storesController;
private readonly BTCPayNetworkProvider _networkProvider;
private readonly UserManager<ApplicationUser> _userManager;
private readonly PaymentMethodHandlerDictionary _paymentMethodHandlerDictionary;
private readonly SettingsRepository _settingsRepository;
private readonly IMemoryCache _cache;
public PoliciesSettings PoliciesSettings { get; }
public MainNav(
AppService appService,
StoreRepository storeRepo,
UIStoresController storesController,
BTCPayNetworkProvider networkProvider,
UserManager<ApplicationUser> userManager,
PaymentMethodHandlerDictionary paymentMethodHandlerDictionary,
SettingsRepository settingsRepository,
IMemoryCache cache,
PoliciesSettings policiesSettings)
{
_storeRepo = storeRepo;
_appService = appService;
_userManager = userManager;
_networkProvider = networkProvider;
_storesController = storesController;
_paymentMethodHandlerDictionary = paymentMethodHandlerDictionary;
_settingsRepository = settingsRepository;
_cache = cache;
PoliciesSettings = policiesSettings;
}
public async Task<IViewComponentResult> InvokeAsync()
{
var store = ViewContext.HttpContext.GetStoreData();
var serverSettings = await _settingsRepository.GetSettingAsync<ServerSettings>() ?? new ServerSettings();
var vm = new MainNavViewModel
{
Store = store,
ContactUrl = serverSettings.ContactUrl
};
#if ALTCOINS
vm.AltcoinsBuild = true;
#endif
if (store != null)
{
var storeBlob = store.GetStoreBlob();
// Wallets
_storesController.AddPaymentMethods(store, storeBlob,
out var derivationSchemes, out var lightningNodes);
foreach (var lnNode in lightningNodes)
{
var pmi = PaymentTypes.LN.GetPaymentMethodId(lnNode.CryptoCode);
if (_paymentMethodHandlerDictionary.TryGet(pmi) is not LightningLikePaymentHandler handler)
continue;
if (lnNode.CacheKey is not null)
{
using var cts = new CancellationTokenSource(5000);
try
{
lnNode.Available = await _cache.GetOrCreateAsync(lnNode.CacheKey, async entry =>
{
entry.SetAbsoluteExpiration(TimeSpan.FromMinutes(5));
try
{
var paymentMethodDetails = store.GetPaymentMethodConfig<LightningPaymentMethodConfig>(pmi, _paymentMethodHandlerDictionary);
await handler.GetNodeInfo(paymentMethodDetails, null, throws: true);
// if we came here without exception, this means the node is available
return true;
}
catch (Exception)
{
return false;
}
}).WithCancellation(cts.Token);
}
catch when (cts.IsCancellationRequested) { }
}
}
vm.DerivationSchemes = derivationSchemes;
vm.LightningNodes = lightningNodes;
// Apps
var apps = await _appService.GetAllApps(UserId, false, store.Id, true);
vm.Apps = apps
.Where(a => !a.Archived)
.Select(a => new StoreApp
{
Id = a.Id,
AppName = a.AppName,
AppType = a.AppType
}).ToList();
vm.ArchivedAppsCount = apps.Count(a => a.Archived);
}
return View(vm);
}
private string UserId => _userManager.GetUserId(HttpContext.User);
}
}