diff --git a/BTCPayServer/Controllers/ServerController.cs b/BTCPayServer/Controllers/ServerController.cs index b13839ab7..08c41e878 100644 --- a/BTCPayServer/Controllers/ServerController.cs +++ b/BTCPayServer/Controllers/ServerController.cs @@ -25,17 +25,14 @@ namespace BTCPayServer.Controllers private UserManager _UserManager; SettingsRepository _SettingsRepository; private IRateProviderFactory _RateProviderFactory; - private CssThemeManager _CssThemeManager; public ServerController(UserManager userManager, IRateProviderFactory rateProviderFactory, - SettingsRepository settingsRepository, - CssThemeManager cssThemeManager) + SettingsRepository settingsRepository) { _UserManager = userManager; _SettingsRepository = settingsRepository; _RateProviderFactory = rateProviderFactory; - _CssThemeManager = cssThemeManager; } [Route("server/rates")] @@ -234,9 +231,6 @@ namespace BTCPayServer.Controllers public async Task Theme(ThemeSettings settings) { await _SettingsRepository.UpdateSetting(settings); - // TODO: remove controller/class-level property and have only reference to - // CssThemeManager here in this method - _CssThemeManager.Update(settings); TempData["StatusMessage"] = "Theme settings updated successfully"; return View(settings); } diff --git a/BTCPayServer/HostedServices/BaseAsyncService.cs b/BTCPayServer/HostedServices/BaseAsyncService.cs new file mode 100644 index 000000000..4b8c85512 --- /dev/null +++ b/BTCPayServer/HostedServices/BaseAsyncService.cs @@ -0,0 +1,66 @@ +using System; +using Microsoft.Extensions.Logging; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using BTCPayServer.Services; +using BTCPayServer.Services.Rates; +using Microsoft.Extensions.Hosting; +using BTCPayServer.Logging; +using System.Runtime.CompilerServices; +using System.IO; +using System.Text; + +namespace BTCPayServer.HostedServices +{ + public abstract class BaseAsyncService : IHostedService + { + private CancellationTokenSource _Cts; + protected Task[] _Tasks; + + public Task StartAsync(CancellationToken cancellationToken) + { + _Cts = new CancellationTokenSource(); + _Tasks = InitializeTasks(); + return Task.CompletedTask; + } + + internal abstract Task[] InitializeTasks(); + + protected CancellationToken Cancellation + { + get { return _Cts.Token; } + } + + protected async Task CreateLoopTask(Func act, [CallerMemberName]string caller = null) + { + await new SynchronizationContextRemover(); + while (!_Cts.IsCancellationRequested) + { + try + { + await act(); + } + catch (OperationCanceledException) when (_Cts.IsCancellationRequested) + { + } + catch (Exception ex) + { + Logs.PayServer.LogWarning(ex, caller + " failed"); + try + { + await Task.Delay(TimeSpan.FromMinutes(1), _Cts.Token); + } + catch (OperationCanceledException) when (_Cts.IsCancellationRequested) { } + } + } + } + + public Task StopAsync(CancellationToken cancellationToken) + { + _Cts.Cancel(); + return Task.WhenAll(_Tasks); + } + } +} diff --git a/BTCPayServer/HostedServices/CssThemeManager.cs b/BTCPayServer/HostedServices/CssThemeManager.cs index 09f733d44..e3afcf35b 100644 --- a/BTCPayServer/HostedServices/CssThemeManager.cs +++ b/BTCPayServer/HostedServices/CssThemeManager.cs @@ -16,47 +16,59 @@ namespace BTCPayServer.HostedServices { public class CssThemeManager { - public CssThemeManager(SettingsRepository settingsRepository) - { - Update(settingsRepository); - } - - private async void Update(SettingsRepository settingsRepository) - { - var data = (await settingsRepository.GetSettingAsync()) ?? new ThemeSettings(); - Update(data); - } - public void Update(ThemeSettings data) { - UpdateBootstrap(data.BootstrapCssUri); - UpdateCreativeStart(data.CreativeStartCssUri); - } + if (String.IsNullOrWhiteSpace(data.BootstrapCssUri)) + _bootstrapUri = "/vendor/bootstrap4/css/bootstrap.css?v=" + DateTime.Now.Ticks; + else + _bootstrapUri = data.BootstrapCssUri; - private string _bootstrapUri = "/vendor/bootstrap4/css/bootstrap.css?v=" + DateTime.Now.Ticks; + + if (String.IsNullOrWhiteSpace(data.CreativeStartCssUri)) + _creativeStartUri = "/vendor/bootstrap4-creativestart/creative.css?v=" + DateTime.Now.Ticks; + else + _creativeStartUri = data.CreativeStartCssUri; + } + + private string _bootstrapUri; public string BootstrapUri { get { return _bootstrapUri; } } - public void UpdateBootstrap(string newUri) - { - if (String.IsNullOrWhiteSpace(newUri)) - _bootstrapUri = "/vendor/bootstrap4/css/bootstrap.css?v="+ DateTime.Now.Ticks; - else - _bootstrapUri = newUri; - } - private string _creativeStartUri = "/vendor/bootstrap4-creativestart/creative.css?v=" + DateTime.Now.Ticks; + private string _creativeStartUri; public string CreativeStartUri { get { return _creativeStartUri; } } - public void UpdateCreativeStart(string newUri) + } + + public class CssThemeManagerHostedService : BaseAsyncService + { + private SettingsRepository _SettingsRepository; + private CssThemeManager _CssThemeManager; + + public CssThemeManagerHostedService(SettingsRepository settingsRepository, CssThemeManager cssThemeManager) { - if (String.IsNullOrWhiteSpace(newUri)) - _creativeStartUri = "/vendor/bootstrap4-creativestart/creative.css?v=" + DateTime.Now.Ticks; - else - _creativeStartUri = newUri; + _SettingsRepository = settingsRepository; + _CssThemeManager = cssThemeManager; + } + + internal override Task[] InitializeTasks() + { + return new[] + { + CreateLoopTask(ListenForThemeChanges) + }; + } + + async Task ListenForThemeChanges() + { + await new SynchronizationContextRemover(); + var data = (await _SettingsRepository.GetSettingAsync()) ?? new ThemeSettings(); + _CssThemeManager.Update(data); + + await _SettingsRepository.WaitSettingsChanged(Cancellation); } } } diff --git a/BTCPayServer/HostedServices/RatesHostedService.cs b/BTCPayServer/HostedServices/RatesHostedService.cs index aeee582b3..dcb0b404d 100644 --- a/BTCPayServer/HostedServices/RatesHostedService.cs +++ b/BTCPayServer/HostedServices/RatesHostedService.cs @@ -14,7 +14,7 @@ using System.Text; namespace BTCPayServer.HostedServices { - public class RatesHostedService : IHostedService + public class RatesHostedService : BaseAsyncService { private SettingsRepository _SettingsRepository; private IRateProviderFactory _RateProviderFactory; @@ -28,79 +28,40 @@ namespace BTCPayServer.HostedServices _coinAverageSettings = coinAverageSettings; } - - CancellationTokenSource _Cts = new CancellationTokenSource(); - - List _Tasks = new List(); - - public Task StartAsync(CancellationToken cancellationToken) + internal override Task[] InitializeTasks() { - _Tasks.Add(RefreshCoinAverageSupportedExchanges(_Cts.Token)); - _Tasks.Add(RefreshCoinAverageSettings(_Cts.Token)); - return Task.CompletedTask; + return new[] + { + CreateLoopTask(RefreshCoinAverageSupportedExchanges), + CreateLoopTask(RefreshCoinAverageSettings) + }; } - - async Task Timer(Func act, CancellationToken cancellation, [CallerMemberName]string caller = null) + async Task RefreshCoinAverageSupportedExchanges() { await new SynchronizationContextRemover(); - while (!cancellation.IsCancellationRequested) + var tickers = await new CoinAverageRateProvider("BTC") { Authenticator = _coinAverageSettings }.GetExchangeTickersAsync(); + _coinAverageSettings.AvailableExchanges = tickers + .Exchanges + .Select(c => (c.DisplayName, c.Name)) + .ToArray(); + await Task.Delay(TimeSpan.FromHours(5), Cancellation); + } + + async Task RefreshCoinAverageSettings() + { + await new SynchronizationContextRemover(); + var rates = (await _SettingsRepository.GetSettingAsync()) ?? new RatesSetting(); + _RateProviderFactory.CacheSpan = TimeSpan.FromMinutes(rates.CacheInMinutes); + if (!string.IsNullOrWhiteSpace(rates.PrivateKey) && !string.IsNullOrWhiteSpace(rates.PublicKey)) { - try - { - await act(); - } - catch (OperationCanceledException) when (cancellation.IsCancellationRequested) - { - } - catch (Exception ex) - { - Logs.PayServer.LogWarning(ex, caller + " failed"); - try - { - await Task.Delay(TimeSpan.FromMinutes(1), cancellation); - } - catch (OperationCanceledException) when (cancellation.IsCancellationRequested) { } - } + _coinAverageSettings.KeyPair = (rates.PublicKey, rates.PrivateKey); } - } - Task RefreshCoinAverageSupportedExchanges(CancellationToken cancellation) - { - return Timer(async () => + else { - await new SynchronizationContextRemover(); - var tickers = await new CoinAverageRateProvider("BTC") { Authenticator = _coinAverageSettings }.GetExchangeTickersAsync(); - _coinAverageSettings.AvailableExchanges = tickers - .Exchanges - .Select(c => (c.DisplayName, c.Name)) - .ToArray(); - await Task.Delay(TimeSpan.FromHours(5), cancellation); - }, cancellation); - } - - Task RefreshCoinAverageSettings(CancellationToken cancellation) - { - return Timer(async () => - { - await new SynchronizationContextRemover(); - var rates = (await _SettingsRepository.GetSettingAsync()) ?? new RatesSetting(); - _RateProviderFactory.CacheSpan = TimeSpan.FromMinutes(rates.CacheInMinutes); - if (!string.IsNullOrWhiteSpace(rates.PrivateKey) && !string.IsNullOrWhiteSpace(rates.PublicKey)) - { - _coinAverageSettings.KeyPair = (rates.PublicKey, rates.PrivateKey); - } - else - { - _coinAverageSettings.KeyPair = null; - } - await _SettingsRepository.WaitSettingsChanged(cancellation); - }, cancellation); - } - - public Task StopAsync(CancellationToken cancellationToken) - { - _Cts.Cancel(); - return Task.WhenAll(_Tasks.ToArray()); + _coinAverageSettings.KeyPair = null; + } + await _SettingsRepository.WaitSettingsChanged(Cancellation); } } } diff --git a/BTCPayServer/Hosting/BTCPayServerServices.cs b/BTCPayServer/Hosting/BTCPayServerServices.cs index b9c4b36b1..f2ce5b78a 100644 --- a/BTCPayServer/Hosting/BTCPayServerServices.cs +++ b/BTCPayServer/Hosting/BTCPayServerServices.cs @@ -138,7 +138,6 @@ namespace BTCPayServer.Hosting services.TryAddSingleton(); services.TryAddSingleton(); - services.TryAddSingleton(); services.TryAddSingleton(); services.TryAddSingleton(); services.TryAddSingleton(); @@ -148,6 +147,9 @@ namespace BTCPayServer.Hosting BlockTarget = 20 }); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton, Payments.Bitcoin.BitcoinLikePaymentHandler>(); services.AddSingleton();