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; }