Allow resolution of any settings via DI

This commit is contained in:
nicolas.dorier 2022-05-24 13:18:16 +09:00
parent 3285f24fe9
commit 67eeb4b69a
No known key found for this signature in database
GPG key ID: 6618763EF09186FE
41 changed files with 221 additions and 149 deletions

View file

@ -72,14 +72,14 @@ namespace BTCPayServer.Tests
// Setup Lightning
var controller = user.GetController<UIStoresController>();
var lightningVm = (LightningNodeViewModel)Assert.IsType<ViewResult>(await controller.SetupLightningNode(user.StoreId, cryptoCode)).Model;
var lightningVm = (LightningNodeViewModel)Assert.IsType<ViewResult>(controller.SetupLightningNode(user.StoreId, cryptoCode)).Model;
Assert.True(lightningVm.Enabled);
var response = await controller.SetLightningNodeEnabled(user.StoreId, cryptoCode, false);
Assert.IsType<RedirectToActionResult>(response);
// Get enabled state from settings
LightningSettingsViewModel lnSettingsModel;
response = controller.LightningSettings(user.StoreId, cryptoCode).GetAwaiter().GetResult();
response = controller.LightningSettings(user.StoreId, cryptoCode);
lnSettingsModel = (LightningSettingsViewModel)Assert.IsType<ViewResult>(response).Model;
Assert.NotNull(lnSettingsModel?.ConnectionString);
Assert.False(lnSettingsModel.Enabled);

View file

@ -137,8 +137,6 @@ namespace BTCPayServer.Tests
}
if (CheatMode)
config.AppendLine("cheatmode=1");
if (Experimental)
config.AppendLine("experimental=1");
config.AppendLine($"torrcfile={TestUtils.GetTestDataFullPath("Tor/torrc")}");
config.AppendLine($"socksendpoint={SocksEndpoint}");
@ -293,7 +291,6 @@ namespace BTCPayServer.Tests
public string SSHKeyFile { get; internal set; }
public string SSHConnection { get; set; }
public bool NoCSP { get; set; }
public bool Experimental { get; internal set; }
public T GetController<T>(string userId = null, string storeId = null, bool isAdmin = false) where T : Controller
{
@ -342,5 +339,13 @@ namespace BTCPayServer.Tests
var index = coinAverageMock.ExchangeRates.FindIndex(o => o.CurrencyPair == p);
coinAverageMock.ExchangeRates[index] = new PairRate(p, bidAsk);
}
public async Task EnableExperimental()
{
var r = GetService<SettingsRepository>();
var p = await r.GetSettingAsync<PoliciesSettings>() ?? new PoliciesSettings();
p.Experimental = true;
await r.UpdateSetting(p);
}
}
}

View file

@ -2519,8 +2519,8 @@ namespace BTCPayServer.Tests
public async Task CustodiansControllerTests()
{
using var tester = CreateServerTester();
tester.PayTester.Experimental = true;
await tester.StartAsync();
await tester.PayTester.EnableExperimental();
var unauthClient = new BTCPayServerClient(tester.PayTester.ServerUri);
await AssertHttpError(401, async () => await unauthClient.GetCustodians());
@ -2539,8 +2539,8 @@ namespace BTCPayServer.Tests
{
using var tester = CreateServerTester();
tester.PayTester.Experimental = true;
await tester.StartAsync();
await tester.PayTester.EnableExperimental();
var admin = tester.NewAccount();
await admin.GrantAccessAsync(true);
@ -2711,9 +2711,9 @@ namespace BTCPayServer.Tests
public async Task CustodianTests()
{
using var tester = CreateServerTester();
tester.PayTester.Experimental = true;
await tester.StartAsync();
await tester.PayTester.EnableExperimental();
var admin = tester.NewAccount();
await admin.GrantAccessAsync(true);

View file

@ -408,7 +408,7 @@ namespace BTCPayServer.Tests
var storeController = user.GetController<UIStoresController>();
var storeResponse = storeController.GeneralSettings();
Assert.IsType<ViewResult>(storeResponse);
Assert.IsType<ViewResult>(await storeController.SetupLightningNode(user.StoreId, "BTC"));
Assert.IsType<ViewResult>(storeController.SetupLightningNode(user.StoreId, "BTC"));
storeController.SetupLightningNode(user.StoreId, new LightningNodeViewModel
{
@ -430,7 +430,7 @@ namespace BTCPayServer.Tests
new LightningNodeViewModel { ConnectionString = tester.MerchantCharge.Client.Uri.AbsoluteUri },
"save", "BTC").GetAwaiter().GetResult());
storeResponse = storeController.LightningSettings(user.StoreId, "BTC").GetAwaiter().GetResult();
storeResponse = storeController.LightningSettings(user.StoreId, "BTC");
var storeVm =
Assert.IsType<LightningSettingsViewModel>(Assert
.IsType<ViewResult>(storeResponse).Model);
@ -1571,7 +1571,7 @@ namespace BTCPayServer.Tests
// enable unified QR code in settings
var vm = Assert.IsType<LightningSettingsViewModel>(Assert
.IsType<ViewResult>(await user.GetController<UIStoresController>().LightningSettings(user.StoreId, cryptoCode)).Model
.IsType<ViewResult>(user.GetController<UIStoresController>().LightningSettings(user.StoreId, cryptoCode)).Model
);
vm.OnChainWithLnInvoiceFallback = true;
Assert.IsType<RedirectToActionResult>(
@ -1629,7 +1629,7 @@ namespace BTCPayServer.Tests
// Activating LNUrl, we should still have only 1 payment criteria that can be set.
user.RegisterLightningNode(cryptoCode, LightningConnectionType.Charge);
var lnSettingsVm = await user.GetController<UIStoresController>().LightningSettings(user.StoreId, cryptoCode).AssertViewModelAsync<LightningSettingsViewModel>();
var lnSettingsVm = user.GetController<UIStoresController>().LightningSettings(user.StoreId, cryptoCode).AssertViewModel<LightningSettingsViewModel>();
lnSettingsVm.LNURLEnabled = true;
lnSettingsVm.LNURLStandardInvoiceEnabled = true;
Assert.IsType<RedirectToActionResult>(user.GetController<UIStoresController>().LightningSettings(lnSettingsVm).Result);

View file

@ -8,16 +8,15 @@
@using BTCPayServer.Abstractions.Extensions
@using BTCPayServer.Abstractions.Contracts
@using BTCPayServer.Client
@using BTCPayServer.Services
@inject BTCPayServer.Services.BTCPayServerEnvironment Env
@inject SignInManager<ApplicationUser> SignInManager
@inject ISettingsRepository SettingsRepository
@inject PoliciesSettings PoliciesSettings
@inject ThemeSettings Theme
@model BTCPayServer.Components.MainNav.MainNavViewModel
@addTagHelper *, BundlerMinifier.TagHelpers
@{
var theme = await SettingsRepository.GetTheme();
}
<nav id="mainNav" class="d-flex flex-column justify-content-between">
<div class="accordion px-3 px-lg-4">
@ -222,7 +221,7 @@
else if (Env.IsSecure)
{
<ul class="navbar-nav">
@if (!(await SettingsRepository.GetPolicies()).LockSubscription)
@if (!PoliciesSettings.LockSubscription)
{
<li class="nav-item">
<a asp-area="" asp-controller="UIAccount" asp-action="Register" class="nav-link js-scroll-trigger" id="Nav-Register">Register</a>
@ -256,7 +255,7 @@
<div class="text-secondary">Administrator</div>
}
</li>
@if (!theme.CustomTheme)
@if (!Theme.CustomTheme)
{
<li class="border-top py-1 px-3">
<vc:theme-switch css-class="nav-link"/>

View file

@ -147,7 +147,6 @@ namespace BTCPayServer.Configuration
PluginRemote = conf.GetOrDefault("plugin-remote", "btcpayserver/btcpayserver-plugins");
RecommendedPlugins = conf.GetOrDefault("recommended-plugins", "").ToLowerInvariant().Split('\r', '\n', '\t', ' ').Where(s => !string.IsNullOrEmpty(s)).Distinct().ToArray();
CheatMode = conf.GetOrDefault("cheatmode", false);
Experimental = conf.GetOrDefault("experimental", false);
if (CheatMode && this.NetworkType == ChainName.Mainnet)
throw new ConfigException($"cheatmode can't be used on mainnet");
}
@ -155,7 +154,6 @@ namespace BTCPayServer.Configuration
public string PluginRemote { get; set; }
public string[] RecommendedPlugins { get; set; }
public bool CheatMode { get; set; }
public bool Experimental { get; set; }
private SSHSettings ParseSSHConfiguration(IConfiguration conf)
{

View file

@ -50,7 +50,6 @@ namespace BTCPayServer.Configuration
app.Option("--recommended-plugins", "Plugins which would be marked as recommended to be installed. Separated by newline or space", CommandOptionType.MultipleValue);
app.Option("--xforwardedproto", "If specified, set X-Forwarded-Proto to the specified value, this may be useful if your reverse proxy handle https but is not configured to add X-Forwarded-Proto (example: --xforwardedproto https)", CommandOptionType.SingleValue);
app.Option("--cheatmode", "Add some helper UI to facilitate dev-time testing (Default false)", CommandOptionType.BoolValue);
app.Option("--experimental", "Enable experimental features (Default false)", CommandOptionType.BoolValue);
app.Option("--explorerpostgres", $"Connection string to the postgres database of NBXplorer. (optional, used for dashboard and reporting features)", CommandOptionType.SingleValue);
foreach (var network in provider.GetAll().OfType<BTCPayNetwork>())

View file

@ -28,10 +28,10 @@ namespace BTCPayServer.Controllers.Greenfield
public GreenfieldInternalLightningNodeApiController(
BTCPayNetworkProvider btcPayNetworkProvider, ISettingsRepository settingsRepository, LightningClientFactoryService lightningClientFactory,
BTCPayNetworkProvider btcPayNetworkProvider, PoliciesSettings policiesSettings, LightningClientFactoryService lightningClientFactory,
IOptions<LightningNetworkOptions> lightningNetworkOptions,
IAuthorizationService authorizationService) : base(
btcPayNetworkProvider, settingsRepository, authorizationService)
btcPayNetworkProvider, policiesSettings, authorizationService)
{
_btcPayNetworkProvider = btcPayNetworkProvider;
_lightningClientFactory = lightningClientFactory;

View file

@ -32,9 +32,9 @@ namespace BTCPayServer.Controllers.Greenfield
public GreenfieldStoreLightningNodeApiController(
IOptions<LightningNetworkOptions> lightningNetworkOptions,
LightningClientFactoryService lightningClientFactory, BTCPayNetworkProvider btcPayNetworkProvider,
ISettingsRepository settingsRepository,
PoliciesSettings policiesSettings,
IAuthorizationService authorizationService) : base(
btcPayNetworkProvider, settingsRepository, authorizationService)
btcPayNetworkProvider, policiesSettings, authorizationService)
{
_lightningNetworkOptions = lightningNetworkOptions;
_lightningClientFactory = lightningClientFactory;

View file

@ -8,6 +8,7 @@ using BTCPayServer.Client;
using BTCPayServer.Client.Models;
using BTCPayServer.Lightning;
using BTCPayServer.Security;
using BTCPayServer.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
@ -26,14 +27,14 @@ namespace BTCPayServer.Controllers.Greenfield
public abstract class GreenfieldLightningNodeApiController : Controller
{
private readonly BTCPayNetworkProvider _btcPayNetworkProvider;
private readonly ISettingsRepository _settingsRepository;
private readonly PoliciesSettings _policiesSettings;
private readonly IAuthorizationService _authorizationService;
protected GreenfieldLightningNodeApiController(BTCPayNetworkProvider btcPayNetworkProvider,
ISettingsRepository settingsRepository,
PoliciesSettings policiesSettings,
IAuthorizationService authorizationService)
{
_btcPayNetworkProvider = btcPayNetworkProvider;
_settingsRepository = settingsRepository;
_policiesSettings = policiesSettings;
_authorizationService = authorizationService;
}
@ -296,8 +297,7 @@ namespace BTCPayServer.Controllers.Greenfield
protected async Task<bool> CanUseInternalLightning(bool doingAdminThings)
{
return (!doingAdminThings && (await _settingsRepository.GetPolicies()).AllowLightningInternalNodeForAll) ||
return (!doingAdminThings && this._policiesSettings.AllowLightningInternalNodeForAll) ||
(await _authorizationService.AuthorizeAsync(User, null,
new PolicyRequirement(Policies.CanUseInternalLightningNode))).Succeeded;
}

View file

@ -15,6 +15,7 @@ using BTCPayServer.Lightning;
using BTCPayServer.Payments;
using BTCPayServer.Payments.Lightning;
using BTCPayServer.Security;
using BTCPayServer.Services;
using BTCPayServer.Services.Stores;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
@ -28,6 +29,9 @@ namespace BTCPayServer.Controllers.Greenfield
public class GreenfieldStoreLightningNetworkPaymentMethodsController : ControllerBase
{
private StoreData Store => HttpContext.GetStoreData();
public PoliciesSettings PoliciesSettings { get; }
private readonly StoreRepository _storeRepository;
private readonly BTCPayNetworkProvider _btcPayNetworkProvider;
private readonly IAuthorizationService _authorizationService;
@ -37,12 +41,14 @@ namespace BTCPayServer.Controllers.Greenfield
StoreRepository storeRepository,
BTCPayNetworkProvider btcPayNetworkProvider,
IAuthorizationService authorizationService,
ISettingsRepository settingsRepository)
ISettingsRepository settingsRepository,
PoliciesSettings policiesSettings)
{
_storeRepository = storeRepository;
_btcPayNetworkProvider = btcPayNetworkProvider;
_authorizationService = authorizationService;
_settingsRepository = settingsRepository;
PoliciesSettings = policiesSettings;
}
public static IEnumerable<LightningNetworkPaymentMethodData> GetLightningPaymentMethods(StoreData store,
@ -216,7 +222,7 @@ namespace BTCPayServer.Controllers.Greenfield
private async Task<bool> CanUseInternalLightning()
{
return (await _settingsRepository.GetPolicies()).AllowLightningInternalNodeForAll ||
return PoliciesSettings.AllowLightningInternalNodeForAll ||
(await _authorizationService.AuthorizeAsync(User, null,
new PolicyRequirement(Policies.CanUseInternalLightningNode))).Succeeded;
}

View file

@ -95,7 +95,7 @@ namespace BTCPayServer.Controllers.Greenfield
private async Task<(bool HotWallet, bool RPCImport)> CanUseHotWallet()
{
return await _authorizationService.CanUseHotWallet(await _settingsRepository.GetPolicies(), User);
return await _authorizationService.CanUseHotWallet(PoliciesSettings, User);
}
}
}

View file

@ -9,6 +9,7 @@ using BTCPayServer.Client.Models;
using BTCPayServer.Data;
using BTCPayServer.HostedServices;
using BTCPayServer.Payments;
using BTCPayServer.Services;
using BTCPayServer.Services.Stores;
using BTCPayServer.Services.Wallets;
using Microsoft.AspNetCore.Authorization;
@ -25,11 +26,13 @@ namespace BTCPayServer.Controllers.Greenfield
public partial class GreenfieldStoreOnChainPaymentMethodsController : ControllerBase
{
private StoreData Store => HttpContext.GetStoreData();
public PoliciesSettings PoliciesSettings { get; }
private readonly StoreRepository _storeRepository;
private readonly BTCPayNetworkProvider _btcPayNetworkProvider;
private readonly BTCPayWalletProvider _walletProvider;
private readonly IAuthorizationService _authorizationService;
private readonly ISettingsRepository _settingsRepository;
private readonly ExplorerClientProvider _explorerClientProvider;
public GreenfieldStoreOnChainPaymentMethodsController(
@ -37,14 +40,15 @@ namespace BTCPayServer.Controllers.Greenfield
BTCPayNetworkProvider btcPayNetworkProvider,
BTCPayWalletProvider walletProvider,
IAuthorizationService authorizationService,
ExplorerClientProvider explorerClientProvider, ISettingsRepository settingsRepository)
ExplorerClientProvider explorerClientProvider,
PoliciesSettings policiesSettings)
{
_storeRepository = storeRepository;
_btcPayNetworkProvider = btcPayNetworkProvider;
_walletProvider = walletProvider;
_authorizationService = authorizationService;
_explorerClientProvider = explorerClientProvider;
_settingsRepository = settingsRepository;
PoliciesSettings = policiesSettings;
}
public static IEnumerable<OnChainPaymentMethodData> GetOnChainPaymentMethods(StoreData store,

View file

@ -38,12 +38,14 @@ namespace BTCPayServer.Controllers.Greenfield
public class GreenfieldStoreOnChainWalletsController : Controller
{
private StoreData Store => HttpContext.GetStoreData();
public PoliciesSettings PoliciesSettings { get; }
private readonly IAuthorizationService _authorizationService;
private readonly BTCPayWalletProvider _btcPayWalletProvider;
private readonly BTCPayNetworkProvider _btcPayNetworkProvider;
private readonly WalletRepository _walletRepository;
private readonly ExplorerClientProvider _explorerClientProvider;
private readonly ISettingsRepository _settingsRepository;
private readonly NBXplorerDashboard _nbXplorerDashboard;
private readonly UIWalletsController _walletsController;
private readonly PayjoinClient _payjoinClient;
@ -59,8 +61,8 @@ namespace BTCPayServer.Controllers.Greenfield
BTCPayNetworkProvider btcPayNetworkProvider,
WalletRepository walletRepository,
ExplorerClientProvider explorerClientProvider,
ISettingsRepository settingsRepository,
NBXplorerDashboard nbXplorerDashboard,
PoliciesSettings policiesSettings,
UIWalletsController walletsController,
PayjoinClient payjoinClient,
DelayedTransactionBroadcaster delayedTransactionBroadcaster,
@ -75,7 +77,7 @@ namespace BTCPayServer.Controllers.Greenfield
_btcPayNetworkProvider = btcPayNetworkProvider;
_walletRepository = walletRepository;
_explorerClientProvider = explorerClientProvider;
_settingsRepository = settingsRepository;
PoliciesSettings = policiesSettings;
_nbXplorerDashboard = nbXplorerDashboard;
_walletsController = walletsController;
_payjoinClient = payjoinClient;
@ -615,7 +617,7 @@ namespace BTCPayServer.Controllers.Greenfield
private async Task<(bool HotWallet, bool RPCImport)> CanUseHotWallet()
{
return await _authorizationService.CanUseHotWallet(await _settingsRepository.GetPolicies(), User);
return await _authorizationService.CanUseHotWallet(PoliciesSettings, User);
}
private bool IsInvalidWalletRequest(string cryptoCode, [MaybeNullWhen(true)] out BTCPayNetwork network,

View file

@ -27,6 +27,7 @@ namespace BTCPayServer.Controllers.Greenfield
[EnableCors(CorsPolicies.All)]
public class GreenfieldUsersController : ControllerBase
{
public PoliciesSettings PoliciesSettings { get; }
public Logs Logs { get; }
private readonly UserManager<ApplicationUser> _userManager;
@ -42,6 +43,7 @@ namespace BTCPayServer.Controllers.Greenfield
public GreenfieldUsersController(UserManager<ApplicationUser> userManager,
RoleManager<IdentityRole> roleManager,
SettingsRepository settingsRepository,
PoliciesSettings policiesSettings,
EventAggregator eventAggregator,
IPasswordValidator<ApplicationUser> passwordValidator,
RateLimitService throttleService,
@ -54,6 +56,7 @@ namespace BTCPayServer.Controllers.Greenfield
_userManager = userManager;
_roleManager = roleManager;
_settingsRepository = settingsRepository;
PoliciesSettings = policiesSettings;
_eventAggregator = eventAggregator;
_passwordValidator = passwordValidator;
_throttleService = throttleService;
@ -147,7 +150,7 @@ namespace BTCPayServer.Controllers.Greenfield
if (request.IsAdministrator is true && !isAdmin)
return this.CreateAPIPermissionError(Policies.Unrestricted, $"Insufficient API Permissions. Please use an API key with permission: {Policies.Unrestricted} and be an admin.");
if (!isAdmin && (policies.LockSubscription || (await _settingsRepository.GetPolicies()).DisableNonAdminCreateUserApi))
if (!isAdmin && (policies.LockSubscription || PoliciesSettings.DisableNonAdminCreateUserApi))
{
// If we are not admin and subscriptions are locked, we need to check the Policies.CanCreateUser.Key permission
var canCreateUser = (await _authorizationService.AuthorizeAsync(User, null, new PolicyRequirement(Policies.CanCreateUser))).Succeeded;

View file

@ -33,9 +33,9 @@ namespace BTCPayServer.Controllers
private readonly UserManager<ApplicationUser> _userManager;
private readonly SignInManager<ApplicationUser> _signInManager;
readonly RoleManager<IdentityRole> _RoleManager;
readonly SettingsRepository _SettingsRepository;
readonly Configuration.BTCPayServerOptions _Options;
private readonly BTCPayServerEnvironment _btcPayServerEnvironment;
readonly SettingsRepository _SettingsRepository;
private readonly Fido2Service _fido2Service;
private readonly LnurlAuthService _lnurlAuthService;
private readonly LinkGenerator _linkGenerator;
@ -43,12 +43,14 @@ namespace BTCPayServer.Controllers
private readonly EventAggregator _eventAggregator;
readonly ILogger _logger;
public PoliciesSettings PoliciesSettings { get; }
public Logs Logs { get; }
public UIAccountController(
UserManager<ApplicationUser> userManager,
RoleManager<IdentityRole> roleManager,
SignInManager<ApplicationUser> signInManager,
PoliciesSettings policiesSettings,
SettingsRepository settingsRepository,
Configuration.BTCPayServerOptions options,
BTCPayServerEnvironment btcPayServerEnvironment,
@ -61,8 +63,9 @@ namespace BTCPayServer.Controllers
{
_userManager = userManager;
_signInManager = signInManager;
_RoleManager = roleManager;
PoliciesSettings = policiesSettings;
_SettingsRepository = settingsRepository;
_RoleManager = roleManager;
_Options = options;
_btcPayServerEnvironment = btcPayServerEnvironment;
_fido2Service = fido2Service;
@ -85,7 +88,7 @@ namespace BTCPayServer.Controllers
public async Task<IActionResult> Login(string returnUrl = null, string email = null)
{
if (User.Identity.IsAuthenticated && string.IsNullOrEmpty(returnUrl))
return await RedirectToLocal();
return RedirectToLocal();
// Clear the existing external cookie to ensure a clean login process
await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme);
@ -119,7 +122,7 @@ namespace BTCPayServer.Controllers
_logger.LogInformation("User with ID {UserId} logged in with a login code.", user.Id);
await _signInManager.SignInAsync(user, false, "LoginCode");
return await RedirectToLocal(returnUrl);
return RedirectToLocal(returnUrl);
}
return await Login(returnUrl, null);
}
@ -194,7 +197,7 @@ namespace BTCPayServer.Controllers
if (result.Succeeded)
{
_logger.LogInformation($"User '{user.Id}' logged in.");
return await RedirectToLocal(returnUrl);
return RedirectToLocal(returnUrl);
}
if (result.RequiresTwoFactor)
{
@ -293,7 +296,7 @@ namespace BTCPayServer.Controllers
_lnurlAuthService.FinalLoginStore.TryRemove(viewModel.UserId, out _);
await _signInManager.SignInAsync(user, viewModel.RememberMe, "FIDO2");
_logger.LogInformation("User logged in.");
return await RedirectToLocal(returnUrl);
return RedirectToLocal(returnUrl);
}
errorMessage = "Invalid login attempt.";
@ -344,7 +347,7 @@ namespace BTCPayServer.Controllers
{
await _signInManager.SignInAsync(user, viewModel.RememberMe, "FIDO2");
_logger.LogInformation("User logged in.");
return await RedirectToLocal(returnUrl);
return RedirectToLocal(returnUrl);
}
errorMessage = "Invalid login attempt.";
@ -423,7 +426,7 @@ namespace BTCPayServer.Controllers
if (result.Succeeded)
{
_logger.LogInformation("User with ID {UserId} logged in with 2fa.", user.Id);
return await RedirectToLocal(returnUrl);
return RedirectToLocal(returnUrl);
}
else if (result.IsLockedOut)
{
@ -492,7 +495,7 @@ namespace BTCPayServer.Controllers
if (result.Succeeded)
{
_logger.LogInformation("User with ID {UserId} logged in with a recovery code.", user.Id);
return await RedirectToLocal(returnUrl);
return RedirectToLocal(returnUrl);
}
if (result.IsLockedOut)
{
@ -518,14 +521,13 @@ namespace BTCPayServer.Controllers
[HttpGet("/register")]
[AllowAnonymous]
[RateLimitsFilter(ZoneLimits.Register, Scope = RateLimitsScope.RemoteAddress)]
public async Task<IActionResult> Register(string returnUrl = null, bool logon = true)
public IActionResult Register(string returnUrl = null, bool logon = true)
{
if (!CanLoginOrRegister())
{
SetInsecureFlags();
}
var policies = await _SettingsRepository.GetSettingAsync<PoliciesSettings>() ?? new PoliciesSettings();
if (policies.LockSubscription && !User.IsInRole(Roles.ServerAdmin))
if (PoliciesSettings.LockSubscription && !User.IsInRole(Roles.ServerAdmin))
return RedirectToAction(nameof(UIHomeController.Index), "UIHome");
ViewData["ReturnUrl"] = returnUrl;
return View();
@ -583,7 +585,7 @@ namespace BTCPayServer.Controllers
{
if (logon)
await _signInManager.SignInAsync(user, isPersistent: false);
return await RedirectToLocal(returnUrl);
return RedirectToLocal(returnUrl);
}
else
{
@ -750,7 +752,7 @@ namespace BTCPayServer.Controllers
}
}
private async Task<IActionResult> RedirectToLocal(string returnUrl = null)
private IActionResult RedirectToLocal(string returnUrl = null)
{
if (!string.IsNullOrEmpty(returnUrl) && Url.IsLocalUrl(returnUrl))
{
@ -759,10 +761,9 @@ namespace BTCPayServer.Controllers
else
{
// After login, if there is an app on "/", we should redirect to BTCPay explicit home route, and not to the app.
var policies = await _SettingsRepository.GetPolicies();
if (policies?.RootAppId is not null && policies?.RootAppType is not null)
if (PoliciesSettings.RootAppId is not null && PoliciesSettings.RootAppType is not null)
return RedirectToAction(nameof(UIHomeController.Home), "UIHome");
if (policies?.DomainToAppMapping is { } mapping)
if (PoliciesSettings.DomainToAppMapping is { } mapping)
{
var matchedDomainMapping = mapping.FirstOrDefault(item =>
item.Domain.Equals(this.HttpContext.Request.Host.Host, StringComparison.InvariantCultureIgnoreCase));

View file

@ -39,7 +39,7 @@ namespace BTCPayServer.Controllers
{
public class UIHomeController : Controller
{
private readonly ISettingsRepository _settingsRepository;
private readonly ThemeSettings _theme;
private readonly StoreRepository _storeRepository;
private readonly BTCPayNetworkProvider _networkProvider;
private IHttpClientFactory HttpClientFactory { get; }
@ -47,13 +47,13 @@ namespace BTCPayServer.Controllers
public LanguageService LanguageService { get; }
public UIHomeController(IHttpClientFactory httpClientFactory,
ISettingsRepository settingsRepository,
ThemeSettings theme,
LanguageService languageService,
StoreRepository storeRepository,
BTCPayNetworkProvider networkProvider,
SignInManager<ApplicationUser> signInManager)
{
_settingsRepository = settingsRepository;
_theme = theme;
HttpClientFactory = httpClientFactory;
LanguageService = languageService;
_networkProvider = networkProvider;
@ -71,7 +71,7 @@ namespace BTCPayServer.Controllers
[DomainMappingConstraint]
public async Task<IActionResult> Index()
{
if ((await _settingsRepository.GetTheme()).FirstRun)
if (_theme.FirstRun)
{
return RedirectToAction(nameof(UIAccountController.Register), "UIAccount");
}

View file

@ -121,10 +121,9 @@ namespace BTCPayServer.Controllers
[Route("server/users/new")]
[HttpGet]
public async Task<IActionResult> CreateUser()
public IActionResult CreateUser()
{
ViewData["AllowRequestEmailConfirmation"] = (await _SettingsRepository.GetPolicies()).RequiresConfirmedEmail;
ViewData["AllowRequestEmailConfirmation"] = _policiesSettings.RequiresConfirmedEmail;
return View();
}
@ -132,7 +131,7 @@ namespace BTCPayServer.Controllers
[HttpPost]
public async Task<IActionResult> CreateUser(RegisterFromAdminViewModel model)
{
var requiresConfirmedEmail = (await _SettingsRepository.GetPolicies()).RequiresConfirmedEmail;
var requiresConfirmedEmail = _policiesSettings.RequiresConfirmedEmail;
ViewData["AllowRequestEmailConfirmation"] = requiresConfirmedEmail;
if (!_Options.CheatMode)
model.IsAdmin = false;

View file

@ -48,6 +48,7 @@ namespace BTCPayServer.Controllers
private readonly UserManager<ApplicationUser> _UserManager;
private readonly UserService _userService;
readonly SettingsRepository _SettingsRepository;
readonly PoliciesSettings _policiesSettings;
private readonly NBXplorerDashboard _dashBoard;
private readonly StoreRepository _StoreRepository;
readonly LightningConfigurationProvider _LnConfigProvider;
@ -70,6 +71,7 @@ namespace BTCPayServer.Controllers
IEnumerable<IStorageProviderService> storageProviderServices,
BTCPayServerOptions options,
SettingsRepository settingsRepository,
PoliciesSettings policiesSettings,
NBXplorerDashboard dashBoard,
IHttpClientFactory httpClientFactory,
LightningConfigurationProvider lnConfigProvider,
@ -81,6 +83,7 @@ namespace BTCPayServer.Controllers
IOptions<ExternalServicesOptions> externalServiceOptions,
Logs logs)
{
_policiesSettings = policiesSettings;
_Options = options;
_StoredFileRepository = storedFileRepository;
_FileService = fileService;
@ -278,10 +281,9 @@ namespace BTCPayServer.Controllers
[Route("server/policies")]
public async Task<IActionResult> Policies()
{
var data = (await _SettingsRepository.GetSettingAsync<PoliciesSettings>()) ?? new PoliciesSettings();
ViewBag.AppsList = await GetAppSelectList();
ViewBag.UpdateUrlPresent = _Options.UpdateUrl != null;
return View(data);
return View(_policiesSettings);
}
[Route("server/policies")]
@ -358,7 +360,7 @@ namespace BTCPayServer.Controllers
Link = this.Request.GetAbsoluteUriNoPathBase(externalService.Value).AbsoluteUri
});
}
if (await CanShowSSHService())
if (CanShowSSHService())
{
result.OtherExternalServices.Add(new ServicesViewModel.OtherExternalService()
{
@ -845,7 +847,7 @@ namespace BTCPayServer.Controllers
[HttpGet("server/services/ssh")]
public async Task<IActionResult> SSHService()
{
if (!await CanShowSSHService())
if (!CanShowSSHService())
return NotFound();
var settings = _Options.SSHSettings;
@ -881,10 +883,9 @@ namespace BTCPayServer.Controllers
return View(vm);
}
async Task<bool> CanShowSSHService()
bool CanShowSSHService()
{
var policies = await _SettingsRepository.GetSettingAsync<PoliciesSettings>();
return !(policies?.DisableSSHService is true) &&
return !_policiesSettings.DisableSSHService &&
_Options.SSHSettings != null && (_sshState.CanUseSSH || CanAccessAuthorizedKeyFile());
}
@ -896,7 +897,7 @@ namespace BTCPayServer.Controllers
[HttpPost("server/services/ssh")]
public async Task<IActionResult> SSHService(SSHServiceViewModel viewModel, string? command = null)
{
if (!await CanShowSSHService())
if (!CanShowSSHService())
return NotFound();
if (command is "Save")

View file

@ -32,7 +32,7 @@ namespace BTCPayServer.Controllers
};
[HttpGet("{storeId}/lightning/{cryptoCode}")]
public async Task<IActionResult> Lightning(string storeId, string cryptoCode)
public IActionResult Lightning(string storeId, string cryptoCode)
{
var store = HttpContext.GetStoreData();
if (store == null)
@ -43,7 +43,7 @@ namespace BTCPayServer.Controllers
CryptoCode = cryptoCode,
StoreId = storeId
};
await SetExistingValues(store, vm);
SetExistingValues(store, vm);
if (vm.LightningNodeType == LightningNodeType.Internal)
{
@ -93,7 +93,7 @@ namespace BTCPayServer.Controllers
}
[HttpGet("{storeId}/lightning/{cryptoCode}/setup")]
public async Task<IActionResult> SetupLightningNode(string storeId, string cryptoCode)
public IActionResult SetupLightningNode(string storeId, string cryptoCode)
{
var store = HttpContext.GetStoreData();
if (store == null)
@ -104,7 +104,7 @@ namespace BTCPayServer.Controllers
CryptoCode = cryptoCode,
StoreId = storeId
};
await SetExistingValues(store, vm);
SetExistingValues(store, vm);
return View(vm);
}
@ -116,7 +116,7 @@ namespace BTCPayServer.Controllers
if (store == null)
return NotFound();
vm.CanUseInternalNode = await CanUseInternalLightning();
vm.CanUseInternalNode = CanUseInternalLightning();
if (vm.CryptoCode == null)
{
@ -130,7 +130,7 @@ namespace BTCPayServer.Controllers
LightningSupportedPaymentMethod? paymentMethod = null;
if (vm.LightningNodeType == LightningNodeType.Internal)
{
if (!await CanUseInternalLightning())
if (!CanUseInternalLightning())
{
ModelState.AddModelError(nameof(vm.ConnectionString), "You are not authorized to use the internal lightning node");
return View(vm);
@ -213,7 +213,7 @@ namespace BTCPayServer.Controllers
}
[HttpGet("{storeId}/lightning/{cryptoCode}/settings")]
public async Task<IActionResult> LightningSettings(string storeId, string cryptoCode)
public IActionResult LightningSettings(string storeId, string cryptoCode)
{
var store = HttpContext.GetStoreData();
if (store == null)
@ -239,7 +239,7 @@ namespace BTCPayServer.Controllers
LightningPrivateRouteHints = storeBlob.LightningPrivateRouteHints,
OnChainWithLnInvoiceFallback = storeBlob.OnChainWithLnInvoiceFallback
};
await SetExistingValues(store, vm);
SetExistingValues(store, vm);
if (lightning != null)
{
@ -360,14 +360,14 @@ namespace BTCPayServer.Controllers
return RedirectToAction(nameof(LightningSettings), new { storeId, cryptoCode });
}
private async Task<bool> CanUseInternalLightning()
private bool CanUseInternalLightning()
{
return User.IsInRole(Roles.ServerAdmin) || (await _settingsRepository.GetPolicies()).AllowLightningInternalNodeForAll;
return User.IsInRole(Roles.ServerAdmin) || _policiesSettings.AllowLightningInternalNodeForAll;
}
private async Task SetExistingValues(StoreData store, LightningNodeViewModel vm)
private void SetExistingValues(StoreData store, LightningNodeViewModel vm)
{
vm.CanUseInternalNode = await CanUseInternalLightning();
vm.CanUseInternalNode = CanUseInternalLightning();
var lightning = GetExistingLightningSupportedPaymentMethod(vm.CryptoCode, store);
if (lightning != null)

View file

@ -787,8 +787,7 @@ namespace BTCPayServer.Controllers
private async Task<(bool HotWallet, bool RPCImport)> CanUseHotWallet()
{
var policies = await _settingsRepository.GetSettingAsync<PoliciesSettings>();
return await _authorizationService.CanUseHotWallet(policies, User);
return await _authorizationService.CanUseHotWallet(_policiesSettings, User);
}
private async Task<string> ReadAllText(IFormFile file)

View file

@ -55,7 +55,7 @@ namespace BTCPayServer.Controllers
ExplorerClientProvider explorerProvider,
LanguageService langService,
PaymentMethodHandlerDictionary paymentMethodHandlerDictionary,
SettingsRepository settingsRepository,
PoliciesSettings policiesSettings,
IAuthorizationService authorizationService,
EventAggregator eventAggregator,
AppService appService,
@ -72,7 +72,7 @@ namespace BTCPayServer.Controllers
_TokenController = tokenController;
_WalletProvider = walletProvider;
_paymentMethodHandlerDictionary = paymentMethodHandlerDictionary;
_settingsRepository = settingsRepository;
_policiesSettings = policiesSettings;
_authorizationService = authorizationService;
_appService = appService;
DataProtector = dataProtector.CreateProtector("ConfigProtector");
@ -100,7 +100,7 @@ namespace BTCPayServer.Controllers
private readonly ExplorerClientProvider _ExplorerProvider;
private readonly LanguageService _LangService;
private readonly PaymentMethodHandlerDictionary _paymentMethodHandlerDictionary;
private readonly SettingsRepository _settingsRepository;
private readonly PoliciesSettings _policiesSettings;
private readonly IAuthorizationService _authorizationService;
private readonly AppService _appService;
private readonly EventAggregator _EventAggregator;

View file

@ -1,18 +0,0 @@
using System.Threading.Tasks;
using BTCPayServer.Abstractions.Contracts;
using BTCPayServer.Services;
namespace BTCPayServer
{
public static class SettingsRepositoryExtensions
{
public static async Task<PoliciesSettings> GetPolicies(this ISettingsRepository settingsRepository)
{
return await settingsRepository.GetSettingAsync<PoliciesSettings>() ?? new PoliciesSettings();
}
public static async Task<ThemeSettings> GetTheme(this ISettingsRepository settingsRepository)
{
return await settingsRepository.GetSettingAsync<ThemeSettings>() ?? new ThemeSettings();
}
}
}

View file

@ -28,8 +28,7 @@ namespace BTCPayServer.Filters
{
if (context.RouteContext.RouteData.Values.ContainsKey("appId"))
return true;
var settingsRepository = context.RouteContext.HttpContext.RequestServices.GetService<ISettingsRepository>();
var policies = settingsRepository.GetPolicies().GetAwaiter().GetResult();
var policies = context.RouteContext.HttpContext.RequestServices.GetService<PoliciesSettings>();
if (policies?.DomainToAppMapping is { } mapping)
{
var matchedDomainMapping = mapping.FirstOrDefault(item =>

View file

@ -14,7 +14,7 @@ namespace BTCPayServer.Filters
public bool Accept(ActionConstraintContext context)
{
return context.RouteContext.HttpContext.RequestServices.GetRequiredService<BTCPayServerEnvironment>().Experimental;
return context.RouteContext.HttpContext.RequestServices.GetRequiredService<PoliciesSettings>().Experimental;
}
}
}

View file

@ -110,7 +110,10 @@ namespace BTCPayServer.Hosting
o.GetRequiredService<IOptions<BTCPayServerOptions>>().Value);
// Don't move this StartupTask, we depend on it being right here
services.AddStartupTask<MigrationStartupTask>();
//
//
AddSettingsAccessor<PoliciesSettings>(services);
AddSettingsAccessor<ThemeSettings>(services);
//
services.AddStartupTask<BlockExplorerLinkStartupTask>();
services.TryAddSingleton<InvoiceRepository>();
services.AddSingleton<PaymentService>();
@ -477,6 +480,15 @@ namespace BTCPayServer.Hosting
return services;
}
private static void AddSettingsAccessor<T>(IServiceCollection services) where T : class, new()
{
services.TryAddSingleton<ISettingsAccessor<T>, SettingsAccessor<T>>();
services.AddSingleton<IHostedService>(provider => (SettingsAccessor<T>)provider.GetRequiredService<ISettingsAccessor<T>>());
services.AddSingleton<IStartupTask>(provider => (SettingsAccessor<T>)provider.GetRequiredService<ISettingsAccessor<T>>());
// Singletons shouldn't reference the settings directly, but ISettingsAccessor<T>, since singletons won't have refreshed values of the setting
services.AddTransient<T>(provider => provider.GetRequiredService<ISettingsAccessor<T>>().Settings);
}
public static void SkipModelValidation<T>(this IServiceCollection services)
{
services.AddSingleton<SkippableObjectValidatorProvider.ISkipValidation, SkippableObjectValidatorProvider.SkipValidationType<T>>();

View file

@ -35,7 +35,6 @@ namespace BTCPayServer.Services
NetworkType = provider.NetworkType;
this.torServices = torServices;
CheatMode = opts.CheatMode;
Experimental = opts.Experimental;
}
public IWebHostEnvironment Environment
{
@ -81,8 +80,6 @@ namespace BTCPayServer.Services
public HttpContext Context => httpContext.HttpContext;
public bool Experimental { get; set; }
public override string ToString()
{
StringBuilder txt = new StringBuilder();

View file

@ -11,14 +11,15 @@ public class DefaultSwaggerProvider: ISwaggerProvider
{
private readonly IFileProvider _fileProvider;
public DefaultSwaggerProvider(IWebHostEnvironment webHostEnvironment, BTCPayServerEnvironment env)
public DefaultSwaggerProvider(IWebHostEnvironment webHostEnvironment, ISettingsAccessor<PoliciesSettings> policies)
{
_fileProvider = webHostEnvironment.WebRootFileProvider;
Env = env;
Policies = policies;
}
public BTCPayServerEnvironment Env { get; }
public ISettingsAccessor<PoliciesSettings> Policies { get; }
public async Task<JObject> Fetch()
{
@ -30,7 +31,7 @@ public class DefaultSwaggerProvider: ISwaggerProvider
await using var stream = fi.CreateReadStream();
using var reader = new StreamReader(fi.CreateReadStream());
var jObject = JObject.Parse(await reader.ReadToEndAsync());
if (jObject.Remove("x_experimental") && !Env.Experimental)
if (jObject.Remove("x_experimental") && !Policies.Settings.Experimental)
continue;
json.Merge(jObject);
}

View file

@ -0,0 +1,55 @@
#nullable enable
using System;
using System.Threading;
using System.Threading.Tasks;
using BTCPayServer.Abstractions.Contracts;
using BTCPayServer.Events;
using Microsoft.Extensions.Hosting;
using Newtonsoft.Json;
namespace BTCPayServer.Services
{
public interface ISettingsAccessor<T>
{
T Settings { get; }
}
class SettingsAccessor<T> : ISettingsAccessor<T>, IStartupTask, IHostedService where T : class, new()
{
T? _Settings;
public T Settings => _Settings ?? throw new InvalidOperationException($"Settings {typeof(T)} not yet initialized");
public EventAggregator Aggregator { get; }
public ISettingsRepository SettingsRepository { get; }
IDisposable? disposable;
public SettingsAccessor(EventAggregator aggregator, ISettingsRepository settings)
{
Aggregator = aggregator;
SettingsRepository = settings;
}
public async Task StartAsync(CancellationToken cancellationToken)
{
if (_Settings != null)
return;
_Settings = await SettingsRepository.GetSettingAsync<T>() ?? new T();
disposable = Aggregator.Subscribe<SettingsChanged<T>>(v => _Settings = Clone(v.Settings));
}
private T Clone(T settings)
{
return JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(settings))!;
}
public Task StopAsync(CancellationToken cancellationToken)
{
disposable?.Dispose();
return Task.CompletedTask;
}
public async Task ExecuteAsync(CancellationToken cancellationToken = default)
{
await StartAsync(cancellationToken);
}
}
}

View file

@ -7,6 +7,7 @@ namespace BTCPayServer.Services.Mails
{
public class EmailSenderFactory
{
public ISettingsAccessor<PoliciesSettings> PoliciesSettings { get; }
public Logs Logs { get; }
private readonly IBackgroundJobClient _jobClient;
@ -15,23 +16,25 @@ namespace BTCPayServer.Services.Mails
public EmailSenderFactory(IBackgroundJobClient jobClient,
SettingsRepository settingsSettingsRepository,
ISettingsAccessor<PoliciesSettings> policiesSettings,
StoreRepository storeRepository,
Logs logs)
{
Logs = logs;
_jobClient = jobClient;
_settingsRepository = settingsSettingsRepository;
PoliciesSettings = policiesSettings;
_storeRepository = storeRepository;
}
public async Task<IEmailSender> GetEmailSender(string storeId = null)
public Task<IEmailSender> GetEmailSender(string storeId = null)
{
var serverSender = new ServerEmailSender(_settingsRepository, _jobClient, Logs);
if (string.IsNullOrEmpty(storeId))
return serverSender;
return new StoreEmailSender(_storeRepository,
!(await _settingsRepository.GetPolicies()).DisableStoresToUseServerEmailSettings ? serverSender : null, _jobClient,
storeId, Logs);
return Task.FromResult<IEmailSender>(serverSender);
return Task.FromResult<IEmailSender>(new StoreEmailSender(_storeRepository,
!PoliciesSettings.Settings.DisableStoresToUseServerEmailSettings ? serverSender : null, _jobClient,
storeId, Logs));
}
}
}

View file

@ -44,6 +44,8 @@ namespace BTCPayServer.Services
public List<BlockExplorerOverrideItem> BlockExplorerLinks { get; set; } = new List<BlockExplorerOverrideItem>();
public List<DomainToAppMappingItem> DomainToAppMapping { get; set; } = new List<DomainToAppMappingItem>();
[Display(Name = "Enable experimental features")]
public bool Experimental { get; set; }
public class BlockExplorerOverrideItem
{

View file

@ -1,11 +1,11 @@
@using BTCPayServer.Abstractions.Contracts
@inject ISettingsRepository _settingsRepository
@inject BTCPayServer.Services.PoliciesSettings PoliciesSettings
@addTagHelper *, BundlerMinifier.TagHelpers
@{ var policies = await _settingsRepository.GetPolicies(); }
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="icon" href="~/favicon.ico" type="image/x-icon">
@if (policies.DiscourageSearchEngines)
@if (PoliciesSettings.DiscourageSearchEngines)
{
<meta name="robots" content="noindex">
}

View file

@ -1,12 +1,13 @@
@using BTCPayServer.Abstractions.Contracts
@using BTCPayServer.Abstractions.Contracts
@using BTCPayServer.Abstractions.Extensions
@using BTCPayServer.Services
@inject ISettingsRepository _settingsRepository
@inject ThemeSettings Theme
@addTagHelper *, BundlerMinifier.TagHelpers
@{ var theme = await _settingsRepository.GetTheme(); }
@if (theme.CustomTheme)
@if (Theme.CustomTheme)
{
<link href="@Context.Request.GetRelativePathOrAbsolute(theme.CssUri)" rel="stylesheet" asp-append-version="true" />
<link href="@Context.Request.GetRelativePathOrAbsolute(Theme.CssUri)" rel="stylesheet" asp-append-version="true" />
}
else
{

View file

@ -2,11 +2,11 @@
@inject BTCPayServer.Services.BTCPayServerEnvironment _env
@inject SignInManager<ApplicationUser> _signInManager
@inject UserManager<ApplicationUser> _userManager
@inject ISettingsRepository _settingsRepository
@inject LinkGenerator _linkGenerator
@inject BTCPayServer.Services.PoliciesSettings PoliciesSettings
@{
var notificationDisabled = (await _settingsRepository.GetPolicies()).DisableInstantNotifications;
var notificationDisabled = PoliciesSettings.DisableInstantNotifications;
if (!notificationDisabled)
{
var user = await _userManager.GetUserAsync(User);

View file

@ -1,14 +1,14 @@
@addTagHelper *, BundlerMinifier.TagHelpers
@inject ISettingsRepository _settingsRepository
@addTagHelper *, BundlerMinifier.TagHelpers
@inject BTCPayServer.Services.ThemeSettings Theme
@using BTCPayServer.Services.Apps
@using BTCPayServer.Abstractions.Contracts
@using BTCPayServer.Abstractions.Extensions
@model BTCPayServer.Models.AppViewModels.ViewPointOfSaleViewModel
@{
ViewData["Title"] = Model.Title;
Layout = null;
var theme = await _settingsRepository.GetTheme();
}
<!DOCTYPE html>
@ -22,7 +22,7 @@
<link rel="apple-touch-startup-image" href="~/img/splash.png">
<link rel="manifest" href="~/manifest.json">
<bundle name="wwwroot/bundles/main-bundle.min.css" asp-append-version="true" />
<link href="@Context.Request.GetRelativePathOrAbsolute(theme.CssUri)" rel="stylesheet" asp-append-version="true"/>
<link href="@Context.Request.GetRelativePathOrAbsolute(Theme.CssUri)" rel="stylesheet" asp-append-version="true"/>
@if (Model.CustomCSSLink != null)
{
<link href="@Model.CustomCSSLink" rel="stylesheet" />

View file

@ -1,6 +1,6 @@
@using BTCPayServer.Abstractions.Contracts
@model LoginViewModel
@inject ISettingsRepository _settingsRepository
@inject BTCPayServer.Services.PoliciesSettings PoliciesSettings
@{
ViewData["Title"] = "Sign in";
Layout = "_LayoutSignedOut";
@ -35,7 +35,7 @@
<form asp-action="LoginWithCode" asp-route-returnurl="@ViewData["ReturnUrl"]" method="post" id="logincode-form">
<input asp-for="LoginCode" type="hidden" class="form-control"/>
</form>
@if (!(await _settingsRepository.GetPolicies()).LockSubscription)
@if (!PoliciesSettings.LockSubscription)
{
<p class="text-center mt-2 mb-0">
<a id="Register" style="font-size:1.15rem" asp-action="Register" asp-route-returnurl="@ViewData["ReturnUrl"]" tabindex="4">Create your account</a>

View file

@ -3,10 +3,10 @@
@inject ISettingsRepository SettingsRepository
@using BTCPayServer.Abstractions.Contracts
@using BTCPayServer.Models.AppViewModels
@inject BTCPayServer.Services.ThemeSettings Theme
@{
ViewData["Title"] = Model.Title;
Layout = null;
var theme = await SettingsRepository.GetTheme();
}
<!DOCTYPE html>
@ -264,7 +264,7 @@
</div>
<div class="card-footer text-muted d-flex flex-wrap align-items-center">
<div class="me-3" v-text="`Updated ${lastUpdated}`">Updated @Model.Info.LastUpdated</div>
@if (!theme.CustomTheme)
@if (!Theme.CustomTheme)
{
<vc:theme-switch css-class="text-muted me-3" responsive="none"/>
}

View file

@ -5,10 +5,10 @@
@addTagHelper *, BundlerMinifier.TagHelpers
@inject BTCPayServer.Services.BTCPayServerEnvironment env
@inject ISettingsRepository _settingsRepository
@inject BTCPayServer.Services.ThemeSettings Theme
@{
ViewData["Title"] = Model.Title;
Layout = null;
var theme = await _settingsRepository.GetTheme();
string StatusClass(InvoiceState state)
{
switch (state.Status.ToModernStatus())
@ -365,7 +365,7 @@
<span class="text-muted mx-2">
Powered by <a href="https://btcpayserver.org" target="_blank" rel="noreferrer noopener">BTCPay Server</a>
</span>
@if (!theme.CustomTheme)
@if (!Theme.CustomTheme)
{
<vc:theme-switch css-class="text-muted mx-2" responsive="none"/>
}

View file

@ -5,9 +5,9 @@
@using BTCPayServer.Abstractions.Extensions
@using BTCPayServer.Lightning
@model BTCPayServer.Controllers.ShowLightningNodeInfoViewModel
@inject BTCPayServer.Services.ThemeSettings Theme
@{
Layout = null;
var theme = await _settingsRepository.GetTheme();
}
<!DOCTYPE html>
<html>
@ -20,7 +20,7 @@
<link rel="apple-touch-startup-image" href="~/img/splash.png" asp-append-version="true">
<link rel="manifest" href="~/manifest.json">
<bundle name="wwwroot/bundles/main-bundle.min.css" asp-append-version="true" />
<link href="@Context.Request.GetRelativePathOrAbsolute(theme.CssUri)" rel="stylesheet" asp-append-version="true"/>
<link href="@Context.Request.GetRelativePathOrAbsolute(Theme.CssUri)" rel="stylesheet" asp-append-version="true"/>
<link href="~/main/qrcode.css" rel="stylesheet" asp-append-version="true" />
</head>
<body>

View file

@ -1,15 +1,15 @@
@inject BTCPayServer.Services.BTCPayServerEnvironment env
@inject BTCPayServer.Services.ThemeSettings Theme
@using NUglify.Helpers
@using BTCPayServer.Abstractions.Contracts
@using BTCPayServer.Abstractions.Extensions
@model BTCPayServer.Models.ViewPullPaymentModel
@addTagHelper *, BundlerMinifier.TagHelpers
@inject BTCPayServer.Services.BTCPayServerEnvironment env
@inject ISettingsRepository _settingsRepository
@{
ViewData["Title"] = Model.Title;
Layout = null;
var theme = await _settingsRepository.GetTheme();
string StatusTextClass(string status)
{
switch (status)
@ -196,7 +196,7 @@
<span class="text-muted mx-2">
Powered by <a href="https://btcpayserver.org" target="_blank" rel="noreferrer noopener">BTCPay Server</a>
</span>
@if (!theme.CustomTheme)
@if (!Theme.CustomTheme)
{
<vc:theme-switch css-class="text-muted mx-2" responsive="none"/>
}

View file

@ -111,6 +111,10 @@
<span class="fa fa-question-circle-o text-secondary" title="More information..."></span>
</a>
<span asp-validation-for="DiscourageSearchEngines" class="text-danger"></span>
</div>
<div class="form-check my-3">
<input asp-for="Experimental" type="checkbox" class="form-check-input"/>
<label asp-for="Experimental" class="form-check-label"></label>
</div>
</div>