From ea7231ff261c6f8d6de3121faca8bc28f2ce7d2f Mon Sep 17 00:00:00 2001 From: rockstardev Date: Thu, 30 Jul 2020 19:12:14 -0500 Subject: [PATCH 01/12] Adding hosted service that will optionally check for new version once a day --- .../NewVersionCheckerHostedService.cs | 91 +++++++++++++++++++ BTCPayServer/Hosting/BTCPayServerServices.cs | 3 + BTCPayServer/Services/PoliciesSettings.cs | 2 + 3 files changed, 96 insertions(+) create mode 100644 BTCPayServer/HostedServices/NewVersionCheckerHostedService.cs diff --git a/BTCPayServer/HostedServices/NewVersionCheckerHostedService.cs b/BTCPayServer/HostedServices/NewVersionCheckerHostedService.cs new file mode 100644 index 000000000..6679e0fab --- /dev/null +++ b/BTCPayServer/HostedServices/NewVersionCheckerHostedService.cs @@ -0,0 +1,91 @@ +using System; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using BTCPayServer.Services; +using BTCPayServer.Services.Notifications; +using BTCPayServer.Services.Notifications.Blobs; +using Newtonsoft.Json.Linq; + +namespace BTCPayServer.HostedServices +{ + public class NewVersionCheckerHostedService : BaseAsyncService + { + private readonly SettingsRepository _settingsRepository; + private readonly BTCPayServerEnvironment _env; + private readonly NotificationSender _notificationSender; + private readonly IVersionFetcher _versionFetcher; + + public NewVersionCheckerHostedService(SettingsRepository settingsRepository, BTCPayServerEnvironment env, + NotificationSender notificationSender, IVersionFetcher versionFetcher) + { + _settingsRepository = settingsRepository; + _env = env; + _notificationSender = notificationSender; + _versionFetcher = versionFetcher; + } + + internal override Task[] InitializeTasks() + { + return new Task[] { CreateLoopTask(CheckForNewVersion) }; + } + + protected async Task CheckForNewVersion() + { + var policies = await _settingsRepository.GetSettingAsync() ?? new PoliciesSettings(); + if (policies.CheckForNewVersions && !_env.IsDevelopping) + { + var tag = await _versionFetcher.Fetch(Cancellation); + if (tag != null && tag != _env.Version) + { + var dh = await _settingsRepository.GetSettingAsync() ?? new NewVersionCheckerDataHolder(); + if (dh.LastVersion != tag) + { + await _notificationSender.SendNotification(new AdminScope(), new NewVersionNotification(tag)); + + dh.LastVersion = tag; + await _settingsRepository.UpdateSetting(dh); + } + } + } + + await Task.Delay(TimeSpan.FromDays(1)); + } + + public class NewVersionCheckerDataHolder + { + public string LastVersion { get; set; } + } + + public interface IVersionFetcher + { + Task Fetch(CancellationToken cancellation); + } + + public class GithubVersionFetcher : IVersionFetcher + { + private readonly HttpClient _httpClient; + public GithubVersionFetcher() + { + _httpClient = new HttpClient(); + _httpClient.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json"); + _httpClient.DefaultRequestHeaders.Add("User-Agent", "BTCPayServer/NewVersionChecker"); + } + + public async Task Fetch(CancellationToken cancellation) + { + const string url = "https://api.github.com/repos/btcpayserver/btcpayserver/releases/latest"; + var resp = await _httpClient.GetAsync(url, cancellation); + + if (resp.IsSuccessStatusCode) + { + var jobj = await resp.Content.ReadAsAsync(cancellation); + var tag = jobj["name"].ToString(); + return tag; + } + + return null; + } + } + } +} diff --git a/BTCPayServer/Hosting/BTCPayServerServices.cs b/BTCPayServer/Hosting/BTCPayServerServices.cs index f0e86abc2..e1b9b78f0 100644 --- a/BTCPayServer/Hosting/BTCPayServerServices.cs +++ b/BTCPayServer/Hosting/BTCPayServerServices.cs @@ -239,7 +239,10 @@ namespace BTCPayServer.Hosting services.AddSingleton(); services.AddScoped(); services.AddScoped(); + + services.AddSingleton(); services.AddSingleton(); + services.AddSingleton(); services.AddSingleton(); diff --git a/BTCPayServer/Services/PoliciesSettings.cs b/BTCPayServer/Services/PoliciesSettings.cs index 382992bab..39793ad46 100644 --- a/BTCPayServer/Services/PoliciesSettings.cs +++ b/BTCPayServer/Services/PoliciesSettings.cs @@ -24,6 +24,8 @@ namespace BTCPayServer.Services public bool AllowHotWalletForAll { get; set; } [Display(Name = "Allow non-admins to import their hot wallets to the node wallet")] public bool AllowHotWalletRPCImportForAll { get; set; } + [Display(Name = "Check releases on GitHub and alert when new BTCPayServer versions is available")] + public bool CheckForNewVersions { get; set; } [Display(Name = "Display app on website root")] public string RootAppId { get; set; } From dc3c130162b01dbd27ba0182b8b99d597a20a181 Mon Sep 17 00:00:00 2001 From: rockstardev Date: Thu, 30 Jul 2020 19:35:58 -0500 Subject: [PATCH 02/12] Initializing Cts with class, preventing race condition --- BTCPayServer/HostedServices/BaseAsyncService.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/BTCPayServer/HostedServices/BaseAsyncService.cs b/BTCPayServer/HostedServices/BaseAsyncService.cs index 52efbb80a..4512d1c5f 100644 --- a/BTCPayServer/HostedServices/BaseAsyncService.cs +++ b/BTCPayServer/HostedServices/BaseAsyncService.cs @@ -10,12 +10,11 @@ namespace BTCPayServer.HostedServices { public abstract class BaseAsyncService : IHostedService { - private CancellationTokenSource _Cts; + private CancellationTokenSource _Cts = new CancellationTokenSource(); protected Task[] _Tasks; public virtual Task StartAsync(CancellationToken cancellationToken) { - _Cts = new CancellationTokenSource(); _Tasks = InitializeTasks(); return Task.CompletedTask; } From 1b3e40fd7000378d956ab94c463d7e0956ec5932 Mon Sep 17 00:00:00 2001 From: rockstardev Date: Thu, 30 Jul 2020 20:43:44 -0500 Subject: [PATCH 03/12] Fixing typo --- BTCPayServer/Controllers/AccountController.cs | 2 +- .../Controllers/GreenField/LightningNodeApiController.cs | 2 +- BTCPayServer/Controllers/StoresController.LightningLike.cs | 2 +- BTCPayServer/HostedServices/NewVersionCheckerHostedService.cs | 2 +- BTCPayServer/Hosting/BTCPayServerServices.cs | 3 ++- BTCPayServer/Payments/PayJoin/PayJoinEndpointController.cs | 2 +- .../Services/Altcoins/Monero/Services/MoneroRPCProvider.cs | 2 +- BTCPayServer/Services/BTCPayServerEnvironment.cs | 2 +- 8 files changed, 9 insertions(+), 8 deletions(-) diff --git a/BTCPayServer/Controllers/AccountController.cs b/BTCPayServer/Controllers/AccountController.cs index b8c47a313..6888927e7 100644 --- a/BTCPayServer/Controllers/AccountController.cs +++ b/BTCPayServer/Controllers/AccountController.cs @@ -626,7 +626,7 @@ namespace BTCPayServer.Controllers private bool CanLoginOrRegister() { - return _btcPayServerEnvironment.IsDevelopping || _btcPayServerEnvironment.IsSecure; + return _btcPayServerEnvironment.IsDeveloping || _btcPayServerEnvironment.IsSecure; } private void SetInsecureFlags() diff --git a/BTCPayServer/Controllers/GreenField/LightningNodeApiController.cs b/BTCPayServer/Controllers/GreenField/LightningNodeApiController.cs index 6979fcfff..5e10b25ab 100644 --- a/BTCPayServer/Controllers/GreenField/LightningNodeApiController.cs +++ b/BTCPayServer/Controllers/GreenField/LightningNodeApiController.cs @@ -288,7 +288,7 @@ namespace BTCPayServer.Controllers.GreenField protected bool CanUseInternalLightning(bool doingAdminThings) { - return (_btcPayServerEnvironment.IsDevelopping || User.IsInRole(Roles.ServerAdmin) || + return (_btcPayServerEnvironment.IsDeveloping || User.IsInRole(Roles.ServerAdmin) || (_cssThemeManager.AllowLightningInternalNodeForAll && !doingAdminThings)); } diff --git a/BTCPayServer/Controllers/StoresController.LightningLike.cs b/BTCPayServer/Controllers/StoresController.LightningLike.cs index 46b892494..0abf1cc47 100644 --- a/BTCPayServer/Controllers/StoresController.LightningLike.cs +++ b/BTCPayServer/Controllers/StoresController.LightningLike.cs @@ -172,7 +172,7 @@ namespace BTCPayServer.Controllers private bool CanUseInternalLightning() { - return (_BTCPayEnv.IsDevelopping || User.IsInRole(Roles.ServerAdmin) || _CssThemeManager.AllowLightningInternalNodeForAll); + return (_BTCPayEnv.IsDeveloping || User.IsInRole(Roles.ServerAdmin) || _CssThemeManager.AllowLightningInternalNodeForAll); } } } diff --git a/BTCPayServer/HostedServices/NewVersionCheckerHostedService.cs b/BTCPayServer/HostedServices/NewVersionCheckerHostedService.cs index 6679e0fab..73e26a845 100644 --- a/BTCPayServer/HostedServices/NewVersionCheckerHostedService.cs +++ b/BTCPayServer/HostedServices/NewVersionCheckerHostedService.cs @@ -33,7 +33,7 @@ namespace BTCPayServer.HostedServices protected async Task CheckForNewVersion() { var policies = await _settingsRepository.GetSettingAsync() ?? new PoliciesSettings(); - if (policies.CheckForNewVersions && !_env.IsDevelopping) + if (policies.CheckForNewVersions && !_env.IsDeveloping) { var tag = await _versionFetcher.Fetch(Cancellation); if (tag != null && tag != _env.Version) diff --git a/BTCPayServer/Hosting/BTCPayServerServices.cs b/BTCPayServer/Hosting/BTCPayServerServices.cs index e1b9b78f0..1cadb52b0 100644 --- a/BTCPayServer/Hosting/BTCPayServerServices.cs +++ b/BTCPayServer/Hosting/BTCPayServerServices.cs @@ -240,6 +240,7 @@ namespace BTCPayServer.Hosting services.AddScoped(); services.AddScoped(); + services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); @@ -287,7 +288,7 @@ namespace BTCPayServer.Hosting { var btcPayEnv = provider.GetService(); var rateLimits = new RateLimitService(); - if (btcPayEnv.IsDevelopping) + if (btcPayEnv.IsDeveloping) { rateLimits.SetZone($"zone={ZoneLimits.Login} rate=1000r/min burst=100 nodelay"); rateLimits.SetZone($"zone={ZoneLimits.Register} rate=1000r/min burst=100 nodelay"); diff --git a/BTCPayServer/Payments/PayJoin/PayJoinEndpointController.cs b/BTCPayServer/Payments/PayJoin/PayJoinEndpointController.cs index 2154d8335..56666857e 100644 --- a/BTCPayServer/Payments/PayJoin/PayJoinEndpointController.cs +++ b/BTCPayServer/Payments/PayJoin/PayJoinEndpointController.cs @@ -502,7 +502,7 @@ namespace BTCPayServer.Payments.PayJoin { var o = new JObject(); o.Add(new JProperty("errorCode", PayjoinReceiverHelper.GetErrorCode(error))); - if (string.IsNullOrEmpty(debug) || !_env.IsDevelopping) + if (string.IsNullOrEmpty(debug) || !_env.IsDeveloping) { o.Add(new JProperty("message", PayjoinReceiverHelper.GetMessage(error))); } diff --git a/BTCPayServer/Services/Altcoins/Monero/Services/MoneroRPCProvider.cs b/BTCPayServer/Services/Altcoins/Monero/Services/MoneroRPCProvider.cs index a0d08cd64..78137020a 100644 --- a/BTCPayServer/Services/Altcoins/Monero/Services/MoneroRPCProvider.cs +++ b/BTCPayServer/Services/Altcoins/Monero/Services/MoneroRPCProvider.cs @@ -64,7 +64,7 @@ namespace BTCPayServer.Services.Altcoins.Monero.Services await daemonRpcClient.SendCommandAsync("sync_info", JsonRpcClient.NoRequestModel.Instance); summary.TargetHeight = daemonResult.TargetHeight ?? daemonResult.Height; - summary.Synced = daemonResult.Height >= summary.TargetHeight && (summary.TargetHeight > 0 || _btcPayServerEnvironment.IsDevelopping); + summary.Synced = daemonResult.Height >= summary.TargetHeight && (summary.TargetHeight > 0 || _btcPayServerEnvironment.IsDeveloping); summary.CurrentHeight = daemonResult.Height; summary.UpdatedAt = DateTime.Now; summary.DaemonAvailable = true; diff --git a/BTCPayServer/Services/BTCPayServerEnvironment.cs b/BTCPayServer/Services/BTCPayServerEnvironment.cs index eb625133c..175c4ccbe 100644 --- a/BTCPayServer/Services/BTCPayServerEnvironment.cs +++ b/BTCPayServer/Services/BTCPayServerEnvironment.cs @@ -54,7 +54,7 @@ namespace BTCPayServer.Services } public bool AltcoinsVersion { get; set; } - public bool IsDevelopping + public bool IsDeveloping { get { From 53f9c22fb7142c82a370d0a4c76edba686501330 Mon Sep 17 00:00:00 2001 From: rockstardev Date: Thu, 30 Jul 2020 20:44:37 -0500 Subject: [PATCH 04/12] Refactoring to exposing related classes --- .../NewVersionCheckerHostedService.cs | 68 ++++++++++--------- 1 file changed, 36 insertions(+), 32 deletions(-) diff --git a/BTCPayServer/HostedServices/NewVersionCheckerHostedService.cs b/BTCPayServer/HostedServices/NewVersionCheckerHostedService.cs index 73e26a845..752132218 100644 --- a/BTCPayServer/HostedServices/NewVersionCheckerHostedService.cs +++ b/BTCPayServer/HostedServices/NewVersionCheckerHostedService.cs @@ -27,10 +27,16 @@ namespace BTCPayServer.HostedServices internal override Task[] InitializeTasks() { - return new Task[] { CreateLoopTask(CheckForNewVersion) }; + return new Task[] { CreateLoopTask(LoopVersionCheck) }; } - protected async Task CheckForNewVersion() + protected async Task LoopVersionCheck() + { + await ProcessVersionCheck(); + await Task.Delay(TimeSpan.FromDays(1), Cancellation); + } + + public async Task ProcessVersionCheck() { var policies = await _settingsRepository.GetSettingAsync() ?? new PoliciesSettings(); if (policies.CheckForNewVersions && !_env.IsDeveloping) @@ -48,44 +54,42 @@ namespace BTCPayServer.HostedServices } } } + } + } - await Task.Delay(TimeSpan.FromDays(1)); + public class NewVersionCheckerDataHolder + { + public string LastVersion { get; set; } + } + + public interface IVersionFetcher + { + Task Fetch(CancellationToken cancellation); + } + + public class GithubVersionFetcher : IVersionFetcher + { + private readonly HttpClient _httpClient; + public GithubVersionFetcher() + { + _httpClient = new HttpClient(); + _httpClient.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json"); + _httpClient.DefaultRequestHeaders.Add("User-Agent", "BTCPayServer/NewVersionChecker"); } - public class NewVersionCheckerDataHolder + public async Task Fetch(CancellationToken cancellation) { - public string LastVersion { get; set; } - } + const string url = "https://api.github.com/repos/btcpayserver/btcpayserver/releases/latest"; + var resp = await _httpClient.GetAsync(url, cancellation); - public interface IVersionFetcher - { - Task Fetch(CancellationToken cancellation); - } - - public class GithubVersionFetcher : IVersionFetcher - { - private readonly HttpClient _httpClient; - public GithubVersionFetcher() + if (resp.IsSuccessStatusCode) { - _httpClient = new HttpClient(); - _httpClient.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json"); - _httpClient.DefaultRequestHeaders.Add("User-Agent", "BTCPayServer/NewVersionChecker"); + var jobj = await resp.Content.ReadAsAsync(cancellation); + var tag = jobj["name"].ToString(); + return tag; } - public async Task Fetch(CancellationToken cancellation) - { - const string url = "https://api.github.com/repos/btcpayserver/btcpayserver/releases/latest"; - var resp = await _httpClient.GetAsync(url, cancellation); - - if (resp.IsSuccessStatusCode) - { - var jobj = await resp.Content.ReadAsAsync(cancellation); - var tag = jobj["name"].ToString(); - return tag; - } - - return null; - } + return null; } } } From 51211dccb00b67bee7ff13b7ed8791500d047ab6 Mon Sep 17 00:00:00 2001 From: rockstardev Date: Thu, 30 Jul 2020 20:52:33 -0500 Subject: [PATCH 05/12] Adding test to verify new version check --- BTCPayServer.Tests/UnitTest1.cs | 60 +++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/BTCPayServer.Tests/UnitTest1.cs b/BTCPayServer.Tests/UnitTest1.cs index b5f6fc803..ca29fccd5 100644 --- a/BTCPayServer.Tests/UnitTest1.cs +++ b/BTCPayServer.Tests/UnitTest1.cs @@ -3103,5 +3103,65 @@ namespace BTCPayServer.Tests .GetResult()) .Where(i => i.GetAddress() == h).Any(); } + + + class MockVersionFetcher : IVersionFetcher + { + public const string MOCK_NEW_VERSION = "9.9.9.9"; + public Task Fetch(CancellationToken cancellation) + { + return Task.FromResult(MOCK_NEW_VERSION); + } + } + + [Fact(Timeout = TestTimeout)] + [Trait("Integration", "Integration")] + public async Task CanCheckForNewVersion() + { + using (var tester = ServerTester.Create(newDb: true)) + { + await tester.StartAsync(); + + var acc = tester.NewAccount(); + acc.GrantAccess(true); + + var settings = tester.PayTester.GetService(); + await settings.UpdateSetting(new PoliciesSettings() { CheckForNewVersions = true }); + + var envMock = tester.PayTester.GetService(); + // modifying environment to simulate production + envMock.NetworkType = NetworkType.Mainnet; + envMock.Environment.EnvironmentName = "Production"; + + var notificationSender = tester.PayTester.GetService(); + + var svc = new NewVersionCheckerHostedService(settings, envMock, notificationSender, new MockVersionFetcher()); + await svc.ProcessVersionCheck(); + + // since last version present in database was null, it should've been updated with version mock returned + var lastVersion = await settings.GetSettingAsync(); + Assert.Equal(MockVersionFetcher.MOCK_NEW_VERSION, lastVersion.LastVersion); + + // we should also have notification in UI + var ctrl = acc.GetController(); + var newVersion = MockVersionFetcher.MOCK_NEW_VERSION; + + var vm = Assert.IsType( + Assert.IsType(ctrl.Index()).Model); + + Assert.True(vm.Skip == 0); + Assert.True(vm.Count == 50); + Assert.True(vm.Total == 1); + Assert.True(vm.Items.Count == 1); + + var fn = vm.Items.First(); + var now = DateTimeOffset.UtcNow; + Assert.True(fn.Created >= now.AddSeconds(-3)); + Assert.True(fn.Created <= now); + Assert.Equal($"New version {newVersion} released!", fn.Body); + Assert.Equal($"https://github.com/btcpayserver/btcpayserver/releases/tag/v{newVersion}", fn.ActionLink); + Assert.False(fn.Seen); + } + } } } From 6c7d3ae0bfdd6d03f8e697cc71041b83d00aa23d Mon Sep 17 00:00:00 2001 From: rockstardev Date: Thu, 30 Jul 2020 20:57:12 -0500 Subject: [PATCH 06/12] Adding checkbox for check new version policy --- BTCPayServer.Tests/UnitTest1.cs | 1 - BTCPayServer/Services/PoliciesSettings.cs | 2 +- BTCPayServer/Views/Server/Policies.cshtml | 5 +++++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/BTCPayServer.Tests/UnitTest1.cs b/BTCPayServer.Tests/UnitTest1.cs index ca29fccd5..1b90ed83c 100644 --- a/BTCPayServer.Tests/UnitTest1.cs +++ b/BTCPayServer.Tests/UnitTest1.cs @@ -3130,7 +3130,6 @@ namespace BTCPayServer.Tests var envMock = tester.PayTester.GetService(); // modifying environment to simulate production - envMock.NetworkType = NetworkType.Mainnet; envMock.Environment.EnvironmentName = "Production"; var notificationSender = tester.PayTester.GetService(); diff --git a/BTCPayServer/Services/PoliciesSettings.cs b/BTCPayServer/Services/PoliciesSettings.cs index 39793ad46..a2305e671 100644 --- a/BTCPayServer/Services/PoliciesSettings.cs +++ b/BTCPayServer/Services/PoliciesSettings.cs @@ -24,7 +24,7 @@ namespace BTCPayServer.Services public bool AllowHotWalletForAll { get; set; } [Display(Name = "Allow non-admins to import their hot wallets to the node wallet")] public bool AllowHotWalletRPCImportForAll { get; set; } - [Display(Name = "Check releases on GitHub and alert when new BTCPayServer versions is available")] + [Display(Name = "Check releases on GitHub and alert when new BTCPayServer version is available")] public bool CheckForNewVersions { get; set; } [Display(Name = "Display app on website root")] diff --git a/BTCPayServer/Views/Server/Policies.cshtml b/BTCPayServer/Views/Server/Policies.cshtml index f28c0e2d8..a097216ec 100644 --- a/BTCPayServer/Views/Server/Policies.cshtml +++ b/BTCPayServer/Views/Server/Policies.cshtml @@ -42,6 +42,11 @@ +
+ + + +
From 9e70bb448a897b24d0c65bad9b6b79f10aaf80c8 Mon Sep 17 00:00:00 2001 From: rockstardev Date: Fri, 31 Jul 2020 00:20:17 -0500 Subject: [PATCH 07/12] Fixes based on feedback --- BTCPayServer.Tests/UnitTest1.cs | 9 +++------ .../NewVersionCheckerHostedService.cs | 13 +++++++++---- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/BTCPayServer.Tests/UnitTest1.cs b/BTCPayServer.Tests/UnitTest1.cs index 1b90ed83c..248ad9d66 100644 --- a/BTCPayServer.Tests/UnitTest1.cs +++ b/BTCPayServer.Tests/UnitTest1.cs @@ -3128,13 +3128,10 @@ namespace BTCPayServer.Tests var settings = tester.PayTester.GetService(); await settings.UpdateSetting(new PoliciesSettings() { CheckForNewVersions = true }); - var envMock = tester.PayTester.GetService(); - // modifying environment to simulate production - envMock.Environment.EnvironmentName = "Production"; + var mockEnv = tester.PayTester.GetService(); + var mockSender = tester.PayTester.GetService(); - var notificationSender = tester.PayTester.GetService(); - - var svc = new NewVersionCheckerHostedService(settings, envMock, notificationSender, new MockVersionFetcher()); + var svc = new NewVersionCheckerHostedService(settings, mockEnv, mockSender, new MockVersionFetcher()); await svc.ProcessVersionCheck(); // since last version present in database was null, it should've been updated with version mock returned diff --git a/BTCPayServer/HostedServices/NewVersionCheckerHostedService.cs b/BTCPayServer/HostedServices/NewVersionCheckerHostedService.cs index 752132218..131e65c64 100644 --- a/BTCPayServer/HostedServices/NewVersionCheckerHostedService.cs +++ b/BTCPayServer/HostedServices/NewVersionCheckerHostedService.cs @@ -39,7 +39,7 @@ namespace BTCPayServer.HostedServices public async Task ProcessVersionCheck() { var policies = await _settingsRepository.GetSettingAsync() ?? new PoliciesSettings(); - if (policies.CheckForNewVersions && !_env.IsDeveloping) + if (policies.CheckForNewVersions) { var tag = await _versionFetcher.Fetch(Cancellation); if (tag != null && tag != _env.Version) @@ -67,16 +67,21 @@ namespace BTCPayServer.HostedServices Task Fetch(CancellationToken cancellation); } - public class GithubVersionFetcher : IVersionFetcher + public class GithubVersionFetcher : IVersionFetcher, IDisposable { private readonly HttpClient _httpClient; - public GithubVersionFetcher() + public GithubVersionFetcher(IHttpClientFactory httpClientFactory) { - _httpClient = new HttpClient(); + _httpClient = httpClientFactory.CreateClient(nameof(GithubVersionFetcher)); _httpClient.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json"); _httpClient.DefaultRequestHeaders.Add("User-Agent", "BTCPayServer/NewVersionChecker"); } + public void Dispose() + { + _httpClient.Dispose(); + } + public async Task Fetch(CancellationToken cancellation) { const string url = "https://api.github.com/repos/btcpayserver/btcpayserver/releases/latest"; From c9d830f9ae6fd8de7b22a3ae413e27ef874683ac Mon Sep 17 00:00:00 2001 From: rockstardev Date: Sat, 1 Aug 2020 07:58:27 -0500 Subject: [PATCH 08/12] Logging exceptions and unsuccessful http calls --- .../NewVersionCheckerHostedService.cs | 30 ++++++++++++++----- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/BTCPayServer/HostedServices/NewVersionCheckerHostedService.cs b/BTCPayServer/HostedServices/NewVersionCheckerHostedService.cs index 131e65c64..8921ec7e3 100644 --- a/BTCPayServer/HostedServices/NewVersionCheckerHostedService.cs +++ b/BTCPayServer/HostedServices/NewVersionCheckerHostedService.cs @@ -2,9 +2,11 @@ using System; using System.Net.Http; using System.Threading; using System.Threading.Tasks; +using BTCPayServer.Logging; using BTCPayServer.Services; using BTCPayServer.Services.Notifications; using BTCPayServer.Services.Notifications.Blobs; +using Microsoft.Extensions.Logging; using Newtonsoft.Json.Linq; namespace BTCPayServer.HostedServices @@ -32,7 +34,14 @@ namespace BTCPayServer.HostedServices protected async Task LoopVersionCheck() { - await ProcessVersionCheck(); + try + { + await ProcessVersionCheck(); + } + catch (Exception ex) + { + Logs.Events.LogError(ex, "Error while performing new version check"); + } await Task.Delay(TimeSpan.FromDays(1), Cancellation); } @@ -85,13 +94,20 @@ namespace BTCPayServer.HostedServices public async Task Fetch(CancellationToken cancellation) { const string url = "https://api.github.com/repos/btcpayserver/btcpayserver/releases/latest"; - var resp = await _httpClient.GetAsync(url, cancellation); - - if (resp.IsSuccessStatusCode) + using (var resp = await _httpClient.GetAsync(url, cancellation)) { - var jobj = await resp.Content.ReadAsAsync(cancellation); - var tag = jobj["name"].ToString(); - return tag; + var strResp = await resp.Content.ReadAsStringAsync(); + if (resp.IsSuccessStatusCode) + { + var jobj = new JObject(strResp); + var tag = jobj["name"].ToString(); + return tag; + } + else + { + Logs.Events.LogWarning($"Unsuccessful status code returned during new version check. " + + $"Url: {url}, HTTP Code: {resp.StatusCode}, Response Body: {strResp}"); + } } return null; From adefaf2fa832d25c4ad3abcad99dbfdf6390cdae Mon Sep 17 00:00:00 2001 From: rockstardev Date: Sat, 1 Aug 2020 08:38:09 -0500 Subject: [PATCH 09/12] Ensuring that only tags that match release format raise notification Utilizing the same regex used on circleci --- .../NewVersionCheckerHostedService.cs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/BTCPayServer/HostedServices/NewVersionCheckerHostedService.cs b/BTCPayServer/HostedServices/NewVersionCheckerHostedService.cs index 8921ec7e3..5103789ec 100644 --- a/BTCPayServer/HostedServices/NewVersionCheckerHostedService.cs +++ b/BTCPayServer/HostedServices/NewVersionCheckerHostedService.cs @@ -1,5 +1,6 @@ using System; using System.Net.Http; +using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using BTCPayServer.Logging; @@ -76,7 +77,7 @@ namespace BTCPayServer.HostedServices Task Fetch(CancellationToken cancellation); } - public class GithubVersionFetcher : IVersionFetcher, IDisposable + public class GithubVersionFetcher : IVersionFetcher { private readonly HttpClient _httpClient; public GithubVersionFetcher(IHttpClientFactory httpClientFactory) @@ -86,11 +87,7 @@ namespace BTCPayServer.HostedServices _httpClient.DefaultRequestHeaders.Add("User-Agent", "BTCPayServer/NewVersionChecker"); } - public void Dispose() - { - _httpClient.Dispose(); - } - + private static readonly Regex _releaseVersionTag = new Regex("^(v[1-9]+(\\.[0-9]+)*(-[0-9]+)?)$"); public async Task Fetch(CancellationToken cancellation) { const string url = "https://api.github.com/repos/btcpayserver/btcpayserver/releases/latest"; @@ -99,9 +96,11 @@ namespace BTCPayServer.HostedServices var strResp = await resp.Content.ReadAsStringAsync(); if (resp.IsSuccessStatusCode) { - var jobj = new JObject(strResp); - var tag = jobj["name"].ToString(); - return tag; + var jobj = JObject.Parse(strResp); + var tag = jobj["tag_name"].ToString(); + + var isReleaseVersionTag = _releaseVersionTag.IsMatch(tag); + return isReleaseVersionTag ? tag : null; } else { From c18167889d15bcb1c99b6bf67751c1dd182e4a6c Mon Sep 17 00:00:00 2001 From: rockstardev Date: Sat, 1 Aug 2020 09:10:05 -0500 Subject: [PATCH 10/12] Adding update related options and using them in HostedService --- BTCPayServer/Configuration/BTCPayServerOptions.cs | 4 ++++ BTCPayServer/Configuration/DefaultConfiguration.cs | 2 ++ .../HostedServices/NewVersionCheckerHostedService.cs | 11 +++++++---- BTCPayServer/Properties/launchSettings.json | 4 +++- 4 files changed, 16 insertions(+), 5 deletions(-) diff --git a/BTCPayServer/Configuration/BTCPayServerOptions.cs b/BTCPayServer/Configuration/BTCPayServerOptions.cs index 2eae346b7..8665be4cd 100644 --- a/BTCPayServer/Configuration/BTCPayServerOptions.cs +++ b/BTCPayServer/Configuration/BTCPayServerOptions.cs @@ -176,6 +176,8 @@ namespace BTCPayServer.Configuration SocksEndpoint = endpoint; } + UpdateCheck = conf.GetOrDefault("updatecheck", false); + UpdateUrl = conf.GetOrDefault("updateurl", new Uri("https://api.github.com/repos/btcpayserver/btcpayserver/releases/latest")); var sshSettings = ParseSSHConfiguration(conf); if ((!string.IsNullOrEmpty(sshSettings.Password) || !string.IsNullOrEmpty(sshSettings.KeyFile)) && !string.IsNullOrEmpty(sshSettings.Server)) @@ -301,5 +303,7 @@ namespace BTCPayServer.Configuration set; } public string TorrcFile { get; set; } + public bool UpdateCheck { get; set; } + public Uri UpdateUrl { get; set; } } } diff --git a/BTCPayServer/Configuration/DefaultConfiguration.cs b/BTCPayServer/Configuration/DefaultConfiguration.cs index e24a31bd2..6b86adcaa 100644 --- a/BTCPayServer/Configuration/DefaultConfiguration.cs +++ b/BTCPayServer/Configuration/DefaultConfiguration.cs @@ -38,6 +38,8 @@ namespace BTCPayServer.Configuration app.Option("--sshtrustedfingerprints", "SSH Host public key fingerprint or sha256 (default: empty, it will allow untrusted connections)", CommandOptionType.SingleValue); app.Option("--torrcfile", "Path to torrc file containing hidden services directories (default: empty)", CommandOptionType.SingleValue); app.Option("--socksendpoint", "Socks endpoint to connect to onion urls (default: empty)", CommandOptionType.SingleValue); + app.Option("--updatecheck", $"Enabling once a day check for new releases (default: false)", CommandOptionType.SingleValue); + app.Option("--updateurl", $"Url location used for updatecheck (default: https://api.github.com/repos/btcpayserver/btcpayserver/releases/latest)", CommandOptionType.SingleValue); app.Option("--debuglog", "A rolling log file for debug messages.", CommandOptionType.SingleValue); app.Option("--debugloglevel", "The severity you log (default:information)", CommandOptionType.SingleValue); app.Option("--disable-registration", "Disables new user registrations (default:true)", CommandOptionType.SingleValue); diff --git a/BTCPayServer/HostedServices/NewVersionCheckerHostedService.cs b/BTCPayServer/HostedServices/NewVersionCheckerHostedService.cs index 5103789ec..1ea989c1d 100644 --- a/BTCPayServer/HostedServices/NewVersionCheckerHostedService.cs +++ b/BTCPayServer/HostedServices/NewVersionCheckerHostedService.cs @@ -3,6 +3,7 @@ using System.Net.Http; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; +using BTCPayServer.Configuration; using BTCPayServer.Logging; using BTCPayServer.Services; using BTCPayServer.Services.Notifications; @@ -80,18 +81,20 @@ namespace BTCPayServer.HostedServices public class GithubVersionFetcher : IVersionFetcher { private readonly HttpClient _httpClient; - public GithubVersionFetcher(IHttpClientFactory httpClientFactory) + private readonly Uri _updateurl; + public GithubVersionFetcher(IHttpClientFactory httpClientFactory, BTCPayServerOptions options) { _httpClient = httpClientFactory.CreateClient(nameof(GithubVersionFetcher)); _httpClient.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json"); _httpClient.DefaultRequestHeaders.Add("User-Agent", "BTCPayServer/NewVersionChecker"); + + _updateurl = options.UpdateUrl; } private static readonly Regex _releaseVersionTag = new Regex("^(v[1-9]+(\\.[0-9]+)*(-[0-9]+)?)$"); public async Task Fetch(CancellationToken cancellation) { - const string url = "https://api.github.com/repos/btcpayserver/btcpayserver/releases/latest"; - using (var resp = await _httpClient.GetAsync(url, cancellation)) + using (var resp = await _httpClient.GetAsync(_updateurl, cancellation)) { var strResp = await resp.Content.ReadAsStringAsync(); if (resp.IsSuccessStatusCode) @@ -105,7 +108,7 @@ namespace BTCPayServer.HostedServices else { Logs.Events.LogWarning($"Unsuccessful status code returned during new version check. " + - $"Url: {url}, HTTP Code: {resp.StatusCode}, Response Body: {strResp}"); + $"Url: {_updateurl}, HTTP Code: {resp.StatusCode}, Response Body: {strResp}"); } } diff --git a/BTCPayServer/Properties/launchSettings.json b/BTCPayServer/Properties/launchSettings.json index 79d84c25c..c2a8d00a4 100644 --- a/BTCPayServer/Properties/launchSettings.json +++ b/BTCPayServer/Properties/launchSettings.json @@ -19,7 +19,9 @@ "BTCPAY_POSTGRES": "User ID=postgres;Host=127.0.0.1;Port=39372;Database=btcpayserver", "BTCPAY_DEBUGLOG": "debug.log", "BTCPAY_TORRCFILE": "../BTCPayServer.Tests/TestData/Tor/torrc", - "BTCPAY_SOCKSENDPOINT": "localhost:9050" + "BTCPAY_SOCKSENDPOINT": "localhost:9050", + "BTCPAY_UPDATECHECK": "false", + "BTCPAY_UPDATEURL": "" }, "applicationUrl": "http://127.0.0.1:14142/" }, From ce87d2e45c7e9e7f864be7ee4d682c5ff3a2258d Mon Sep 17 00:00:00 2001 From: rockstardev Date: Sat, 1 Aug 2020 09:17:17 -0500 Subject: [PATCH 11/12] Making use of options to initalize update check on first admin registration --- BTCPayServer/Controllers/AccountController.cs | 9 +---- .../Controllers/GreenField/UsersController.cs | 8 +--- .../Extensions/ActionLogicExtensions.cs | 39 +++++++++++++++++++ 3 files changed, 42 insertions(+), 14 deletions(-) create mode 100644 BTCPayServer/Extensions/ActionLogicExtensions.cs diff --git a/BTCPayServer/Controllers/AccountController.cs b/BTCPayServer/Controllers/AccountController.cs index 6888927e7..cac9f0e36 100644 --- a/BTCPayServer/Controllers/AccountController.cs +++ b/BTCPayServer/Controllers/AccountController.cs @@ -443,13 +443,8 @@ namespace BTCPayServer.Controllers var settings = await _SettingsRepository.GetSettingAsync(); settings.FirstRun = false; await _SettingsRepository.UpdateSetting(settings); - if (_Options.DisableRegistration) - { - // Once the admin user has been created lock subsequent user registrations (needs to be disabled for unit tests that require multiple users). - Logs.PayServer.LogInformation("First admin created, disabling subscription (disable-registration is set to true)"); - policies.LockSubscription = true; - await _SettingsRepository.UpdateSetting(policies); - } + + await _SettingsRepository.FirstAdminRegistered(policies, _Options.UpdateCheck, _Options.DisableRegistration); RegisteredAdmin = true; } diff --git a/BTCPayServer/Controllers/GreenField/UsersController.cs b/BTCPayServer/Controllers/GreenField/UsersController.cs index 94adf201f..8a3dbb9cb 100644 --- a/BTCPayServer/Controllers/GreenField/UsersController.cs +++ b/BTCPayServer/Controllers/GreenField/UsersController.cs @@ -148,13 +148,7 @@ namespace BTCPayServer.Controllers.GreenField await _userManager.AddToRoleAsync(user, Roles.ServerAdmin); if (!anyAdmin) { - if (_options.DisableRegistration) - { - // automatically lock subscriptions now that we have our first admin - Logs.PayServer.LogInformation("First admin created, disabling subscription (disable-registration is set to true)"); - policies.LockSubscription = true; - await _settingsRepository.UpdateSetting(policies); - } + await _settingsRepository.FirstAdminRegistered(policies, _options.UpdateCheck, _options.DisableRegistration); } } _eventAggregator.Publish(new UserRegisteredEvent() { RequestUri = Request.GetAbsoluteRootUri(), User = user, Admin = request.IsAdministrator is true }); diff --git a/BTCPayServer/Extensions/ActionLogicExtensions.cs b/BTCPayServer/Extensions/ActionLogicExtensions.cs new file mode 100644 index 000000000..9e5d247f2 --- /dev/null +++ b/BTCPayServer/Extensions/ActionLogicExtensions.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using BTCPayServer.Configuration; +using BTCPayServer.Logging; +using BTCPayServer.Services; +using Microsoft.Extensions.Logging; + +namespace BTCPayServer +{ + // All logic that would otherwise be duplicated across solution goes into this utility class + // ~If~ Once this starts growing out of control, begin extracting action logic classes out of here + // Also some of logic in here may be result of parallel development of Greenfield API + // It's much better that we extract those common methods then copy paste and maintain same code across codebase + internal static class ActionLogicExtensions + { + internal static async Task FirstAdminRegistered(this SettingsRepository settingsRepository, PoliciesSettings policies, + bool updateCheck, bool disableRegistrations) + { + if (updateCheck) + { + Logs.PayServer.LogInformation("First admin created, enabling checks for new versions"); + policies.CheckForNewVersions = updateCheck; + } + + if (disableRegistrations) + { + // Once the admin user has been created lock subsequent user registrations (needs to be disabled for unit tests that require multiple users). + Logs.PayServer.LogInformation("First admin created, disabling subscription (disable-registration is set to true)"); + policies.LockSubscription = true; + } + + if (updateCheck || disableRegistrations) + await settingsRepository.UpdateSetting(policies); + } + } +} From 16eedf4153d82d26afc9ff1294b4a27fdcbb5d97 Mon Sep 17 00:00:00 2001 From: rockstardev Date: Mon, 3 Aug 2020 03:16:29 -0500 Subject: [PATCH 12/12] Deducing if to perform update check from update url configuration --- BTCPayServer/Configuration/BTCPayServerOptions.cs | 4 +--- BTCPayServer/Configuration/DefaultConfiguration.cs | 3 +-- BTCPayServer/Controllers/AccountController.cs | 2 +- BTCPayServer/Controllers/GreenField/UsersController.cs | 2 +- BTCPayServer/HostedServices/NewVersionCheckerHostedService.cs | 3 +++ BTCPayServer/Properties/launchSettings.json | 1 - 6 files changed, 7 insertions(+), 8 deletions(-) diff --git a/BTCPayServer/Configuration/BTCPayServerOptions.cs b/BTCPayServer/Configuration/BTCPayServerOptions.cs index 8665be4cd..3cd189a7b 100644 --- a/BTCPayServer/Configuration/BTCPayServerOptions.cs +++ b/BTCPayServer/Configuration/BTCPayServerOptions.cs @@ -176,8 +176,7 @@ namespace BTCPayServer.Configuration SocksEndpoint = endpoint; } - UpdateCheck = conf.GetOrDefault("updatecheck", false); - UpdateUrl = conf.GetOrDefault("updateurl", new Uri("https://api.github.com/repos/btcpayserver/btcpayserver/releases/latest")); + UpdateUrl = conf.GetOrDefault("updateurl", null); var sshSettings = ParseSSHConfiguration(conf); if ((!string.IsNullOrEmpty(sshSettings.Password) || !string.IsNullOrEmpty(sshSettings.KeyFile)) && !string.IsNullOrEmpty(sshSettings.Server)) @@ -303,7 +302,6 @@ namespace BTCPayServer.Configuration set; } public string TorrcFile { get; set; } - public bool UpdateCheck { get; set; } public Uri UpdateUrl { get; set; } } } diff --git a/BTCPayServer/Configuration/DefaultConfiguration.cs b/BTCPayServer/Configuration/DefaultConfiguration.cs index 6b86adcaa..d16245d9a 100644 --- a/BTCPayServer/Configuration/DefaultConfiguration.cs +++ b/BTCPayServer/Configuration/DefaultConfiguration.cs @@ -38,8 +38,7 @@ namespace BTCPayServer.Configuration app.Option("--sshtrustedfingerprints", "SSH Host public key fingerprint or sha256 (default: empty, it will allow untrusted connections)", CommandOptionType.SingleValue); app.Option("--torrcfile", "Path to torrc file containing hidden services directories (default: empty)", CommandOptionType.SingleValue); app.Option("--socksendpoint", "Socks endpoint to connect to onion urls (default: empty)", CommandOptionType.SingleValue); - app.Option("--updatecheck", $"Enabling once a day check for new releases (default: false)", CommandOptionType.SingleValue); - app.Option("--updateurl", $"Url location used for updatecheck (default: https://api.github.com/repos/btcpayserver/btcpayserver/releases/latest)", CommandOptionType.SingleValue); + app.Option("--updateurl", $"Url used for once a day new release version check. Check performed only if value is not empty (default: empty)", CommandOptionType.SingleValue); app.Option("--debuglog", "A rolling log file for debug messages.", CommandOptionType.SingleValue); app.Option("--debugloglevel", "The severity you log (default:information)", CommandOptionType.SingleValue); app.Option("--disable-registration", "Disables new user registrations (default:true)", CommandOptionType.SingleValue); diff --git a/BTCPayServer/Controllers/AccountController.cs b/BTCPayServer/Controllers/AccountController.cs index cac9f0e36..6d1b8d3c0 100644 --- a/BTCPayServer/Controllers/AccountController.cs +++ b/BTCPayServer/Controllers/AccountController.cs @@ -444,7 +444,7 @@ namespace BTCPayServer.Controllers settings.FirstRun = false; await _SettingsRepository.UpdateSetting(settings); - await _SettingsRepository.FirstAdminRegistered(policies, _Options.UpdateCheck, _Options.DisableRegistration); + await _SettingsRepository.FirstAdminRegistered(policies, _Options.UpdateUrl != null, _Options.DisableRegistration); RegisteredAdmin = true; } diff --git a/BTCPayServer/Controllers/GreenField/UsersController.cs b/BTCPayServer/Controllers/GreenField/UsersController.cs index 8a3dbb9cb..350b7cf02 100644 --- a/BTCPayServer/Controllers/GreenField/UsersController.cs +++ b/BTCPayServer/Controllers/GreenField/UsersController.cs @@ -148,7 +148,7 @@ namespace BTCPayServer.Controllers.GreenField await _userManager.AddToRoleAsync(user, Roles.ServerAdmin); if (!anyAdmin) { - await _settingsRepository.FirstAdminRegistered(policies, _options.UpdateCheck, _options.DisableRegistration); + await _settingsRepository.FirstAdminRegistered(policies, _options.UpdateUrl != null, _options.DisableRegistration); } } _eventAggregator.Publish(new UserRegisteredEvent() { RequestUri = Request.GetAbsoluteRootUri(), User = user, Admin = request.IsAdministrator is true }); diff --git a/BTCPayServer/HostedServices/NewVersionCheckerHostedService.cs b/BTCPayServer/HostedServices/NewVersionCheckerHostedService.cs index 1ea989c1d..c7dd75637 100644 --- a/BTCPayServer/HostedServices/NewVersionCheckerHostedService.cs +++ b/BTCPayServer/HostedServices/NewVersionCheckerHostedService.cs @@ -94,6 +94,9 @@ namespace BTCPayServer.HostedServices private static readonly Regex _releaseVersionTag = new Regex("^(v[1-9]+(\\.[0-9]+)*(-[0-9]+)?)$"); public async Task Fetch(CancellationToken cancellation) { + if (_updateurl == null) + return null; + using (var resp = await _httpClient.GetAsync(_updateurl, cancellation)) { var strResp = await resp.Content.ReadAsStringAsync(); diff --git a/BTCPayServer/Properties/launchSettings.json b/BTCPayServer/Properties/launchSettings.json index c2a8d00a4..15805eab9 100644 --- a/BTCPayServer/Properties/launchSettings.json +++ b/BTCPayServer/Properties/launchSettings.json @@ -20,7 +20,6 @@ "BTCPAY_DEBUGLOG": "debug.log", "BTCPAY_TORRCFILE": "../BTCPayServer.Tests/TestData/Tor/torrc", "BTCPAY_SOCKSENDPOINT": "localhost:9050", - "BTCPAY_UPDATECHECK": "false", "BTCPAY_UPDATEURL": "" }, "applicationUrl": "http://127.0.0.1:14142/"