diff --git a/src/ServarrAPI/Cloudflare/CloudflareProxy.cs b/src/ServarrAPI/Cloudflare/CloudflareProxy.cs index 6db10df..0a6f548 100644 --- a/src/ServarrAPI/Cloudflare/CloudflareProxy.cs +++ b/src/ServarrAPI/Cloudflare/CloudflareProxy.cs @@ -19,7 +19,7 @@ public interface ICloudflareProxy public class CloudflareProxy : ICloudflareProxy { - private static readonly JsonSerializerOptions JsonOptions = new JsonSerializerOptions + private static readonly JsonSerializerOptions JsonOptions = new () { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }; @@ -60,6 +60,8 @@ public async Task InvalidateBranches(IEnumerable branches) { if (!_enabled) { + _logger.LogTrace("CloudflareProxy is disabled"); + return; } @@ -68,14 +70,15 @@ public async Task InvalidateBranches(IEnumerable branches) while (paged.Any()) { - _logger.LogTrace($"Invalidating page {page} with {paged.Count()} ids"); + _logger.LogTrace("Invalidating page {0} with {1} ids", page, paged.Count); + var files = paged .SelectMany(x => new[] - { - $"{_config.BaseUrl}/update/{x}", - $"{_config.BaseUrl}/update/{x}/changes", - $"{_config.BaseUrl}/update/{x}/updatefile" - }) + { + $"{_config.BaseUrl}/update/{x}", + $"{_config.BaseUrl}/update/{x}/changes", + $"{_config.BaseUrl}/update/{x}/updatefile" + }) .ToList(); var payload = new CloudflareInvalidationRequest @@ -108,7 +111,7 @@ public async Task InvalidateBranches(IEnumerable branches) _logger.LogError(e, "Invalidation failed"); } - _logger.LogTrace($"Invalidation failed, retrying"); + _logger.LogTrace("Invalidation failed, retrying"); retries--; } @@ -117,9 +120,9 @@ public async Task InvalidateBranches(IEnumerable branches) } } - private IEnumerable GetPage(IEnumerable items, int page) + private List GetPage(IEnumerable items, int page) { - return items.Skip(page * 10).Take(10); + return items.Skip(page * 10).Take(10).ToList(); } private HttpRequestMessage GetInvalidationMessage(CloudflareInvalidationRequest payload) diff --git a/src/ServarrAPI/Controllers/WebhookController.cs b/src/ServarrAPI/Controllers/WebhookController.cs index f49d432..f87c48e 100644 --- a/src/ServarrAPI/Controllers/WebhookController.cs +++ b/src/ServarrAPI/Controllers/WebhookController.cs @@ -2,6 +2,7 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; +using ServarrAPI.Cloudflare; using ServarrAPI.Release; using ServarrAPI.Release.Azure; using ServarrAPI.Release.Github; @@ -15,13 +16,16 @@ public class WebhookController private readonly Config _config; private readonly IBackgroundTaskQueue _queue; private readonly IServiceScopeFactory _serviceScopeFactory; + private readonly ICloudflareProxy _cloudflare; public WebhookController(IBackgroundTaskQueue queue, IServiceScopeFactory serviceScopeFactory, + ICloudflareProxy cloudflare, IOptions optionsConfig) { _queue = queue; _serviceScopeFactory = serviceScopeFactory; + _cloudflare = cloudflare; _config = optionsConfig.Value; } @@ -58,5 +62,24 @@ public string Refresh([FromQuery] string source, [FromQuery(Name = "api_key")] s return "Thank you."; } + + [Route("branch/{branch}/refresh")] + [HttpGet] + public async Task InvalidateCache([FromQuery(Name = "api_key")] string apiKey, [FromRoute(Name = "branch")] string branch) + { + if (!_config.ApiKey.Equals(apiKey)) + { + return "No, thank you."; + } + + if (string.IsNullOrWhiteSpace(branch)) + { + return "Unknown branch."; + } + + await _cloudflare.InvalidateBranches(new[] { branch }).ConfigureAwait(false); + + return "Thank you."; + } } } diff --git a/src/ServarrAPI/Datastore/Migration/002_break_up_version.cs b/src/ServarrAPI/Datastore/Migration/002_break_up_version.cs index 1d42e3c..56b3485 100644 --- a/src/ServarrAPI/Datastore/Migration/002_break_up_version.cs +++ b/src/ServarrAPI/Datastore/Migration/002_break_up_version.cs @@ -19,9 +19,9 @@ protected override void MainDbUpgrade() // Create a sortable integer for version that can easily be compared against a max version, // supports any major, minor to 99, patch to 99, builds up to 999,999. Execute.Sql("UPDATE update SET intversion = " + - "((string_to_array(version, '.')::int[])[1] * 10000000000) + " + - "((string_to_array(version, '.')::int[])[2] * 100000000) + " + - "((string_to_array(version, '.')::int[])[3] * 1000000) + " + + "((string_to_array(version, '.')::int[])[1] * 10000000000L) + " + + "((string_to_array(version, '.')::int[])[2] * 100000000L) + " + + "((string_to_array(version, '.')::int[])[3] * 1000000L) + " + "((string_to_array(version, '.')::int[])[4])"); } } diff --git a/src/ServarrAPI/Extensions/VersionExtensions.cs b/src/ServarrAPI/Extensions/VersionExtensions.cs index a2eec53..d1d99b4 100644 --- a/src/ServarrAPI/Extensions/VersionExtensions.cs +++ b/src/ServarrAPI/Extensions/VersionExtensions.cs @@ -6,7 +6,7 @@ public static class VersionExtensions { public static long ToIntVersion(this Version version) { - return (version.Major * 10000000000) + (version.Minor * 100000000) + (version.Build * 1000000) + version.Revision; + return (version.Major * 10000000000L) + (version.Minor * 100000000L) + (version.Build * 1000000L) + version.Revision; } } } diff --git a/src/ServarrAPI/Release/Azure/AzureReleaseSource.cs b/src/ServarrAPI/Release/Azure/AzureReleaseSource.cs index 4d972d7..6f9241b 100644 --- a/src/ServarrAPI/Release/Azure/AzureReleaseSource.cs +++ b/src/ServarrAPI/Release/Azure/AzureReleaseSource.cs @@ -247,40 +247,48 @@ private async Task ProcessFile(AzureFile file, string branch, int updateId) // Calculate the hash of the zip file. var releaseFileName = Path.GetFileName(file.Path); - var releaseZip = Path.Combine(_config.DataDirectory, branch, releaseFileName); - string releaseHash; + var releaseZip = Path.Combine(_config.DataDirectory, branch.ToLowerInvariant(), releaseFileName); - if (!File.Exists(releaseZip)) + try { - Directory.CreateDirectory(Path.GetDirectoryName(releaseZip)); + if (!File.Exists(releaseZip)) + { + Directory.CreateDirectory(Path.GetDirectoryName(releaseZip)); - using var fileStream = File.OpenWrite(releaseZip); - using var artifactStream = await _httpClient.GetStreamAsync(file.Url).ConfigureAwait(false); - await artifactStream.CopyToAsync(fileStream).ConfigureAwait(false); - } + await using var fileStream = File.OpenWrite(releaseZip); + await using var artifactStream = await _httpClient.GetStreamAsync(file.Url).ConfigureAwait(false); + await artifactStream.CopyToAsync(fileStream).ConfigureAwait(false); + } - using (var stream = File.OpenRead(releaseZip)) - { - using var sha = SHA256.Create(); - releaseHash = BitConverter.ToString(sha.ComputeHash(stream)).Replace("-", "").ToLower(); - } + string releaseHash; + await using (var stream = File.OpenRead(releaseZip)) + using (var sha = SHA256.Create()) + { + releaseHash = BitConverter.ToString(await sha.ComputeHashAsync(stream)).Replace("-", "").ToLowerInvariant(); + } - File.Delete(releaseZip); + // Add to database. + var updateFile = new UpdateFileEntity + { + UpdateId = updateId, + OperatingSystem = operatingSystem.Value, + Architecture = arch, + Runtime = runtime, + Filename = releaseFileName, + Url = file.Url, + Hash = releaseHash, + Installer = installer + }; - // Add to database. - var updateFile = new UpdateFileEntity + await _updateFileService.Insert(updateFile).ConfigureAwait(false); + } + finally { - UpdateId = updateId, - OperatingSystem = operatingSystem.Value, - Architecture = arch, - Runtime = runtime, - Filename = releaseFileName, - Url = file.Url, - Hash = releaseHash, - Installer = installer - }; - - await _updateFileService.Insert(updateFile).ConfigureAwait(false); + if (File.Exists(releaseZip)) + { + File.Delete(releaseZip); + } + } } } } diff --git a/src/ServarrAPI/Release/Github/GithubReleaseSource.cs b/src/ServarrAPI/Release/Github/GithubReleaseSource.cs index 472ebff..a3e1cca 100644 --- a/src/ServarrAPI/Release/Github/GithubReleaseSource.cs +++ b/src/ServarrAPI/Release/Github/GithubReleaseSource.cs @@ -42,24 +42,28 @@ protected override async Task> DoFetchReleasesAsync() var updated = new HashSet(); var githubOrg = _config.GithubOrg ?? _config.Project; - var releases = (await _gitHubClient.Repository.Release.GetAll(githubOrg, _config.Project)).ToArray(); - var validReleases = releases - .Where(r => r.TagName.StartsWith("v") && VersionUtil.IsValid(r.TagName.Substring(1))) + var releases = await _gitHubClient.Repository.Release.GetAll( + githubOrg, + _config.Project, + new ApiOptions + { + PageSize = 100, + PageCount = 1 + }) + .ConfigureAwait(false); + + var validReleases = releases.ToArray() + .Where(r => r.PublishedAt.HasValue && r.TagName.StartsWith("v") && VersionUtil.IsValid(r.TagName.Substring(1))) .Take(3) - .Reverse(); + .Reverse() + .ToArray(); foreach (var release in validReleases) { - // Check if release has been published. - if (!release.PublishedAt.HasValue) - { - continue; - } - var version = release.TagName.Substring(1); // determine the branch - var branch = release.Assets.Any(a => a.Name.StartsWith(string.Format("{0}.master", _config.Project))) ? "master" : "develop"; + var branch = release.Assets.Any(a => a.Name.StartsWith($"{_config.Project}.master")) ? "master" : "develop"; if (await ProcessRelease(release, branch, version)) { @@ -135,7 +139,7 @@ private async Task ProcessRelease(Octokit.Release release, string branch, return isNewRelease; } - private async Task ProcessAsset(Octokit.ReleaseAsset releaseAsset, string branch, int updateId) + private async Task ProcessAsset(ReleaseAsset releaseAsset, string branch, int updateId) { var operatingSystem = Parser.ParseOS(releaseAsset.Name); if (!operatingSystem.HasValue) @@ -148,40 +152,48 @@ private async Task ProcessAsset(Octokit.ReleaseAsset releaseAsset, string branch var installer = Parser.ParseInstaller(releaseAsset.Name); // Calculate the hash of the zip file. - var releaseZip = Path.Combine(_config.DataDirectory, branch.ToString().ToLower(), releaseAsset.Name); - string releaseHash; + var releaseZip = Path.Combine(_config.DataDirectory, branch.ToLowerInvariant(), releaseAsset.Name); - if (!File.Exists(releaseZip)) + try { - Directory.CreateDirectory(Path.GetDirectoryName(releaseZip)); - - using var fileStream = File.OpenWrite(releaseZip); - using var artifactStream = await _httpClient.GetStreamAsync(releaseAsset.BrowserDownloadUrl); - await artifactStream.CopyToAsync(fileStream); - } + if (!File.Exists(releaseZip)) + { + Directory.CreateDirectory(Path.GetDirectoryName(releaseZip)); - using (var stream = File.OpenRead(releaseZip)) - using (var sha = SHA256.Create()) - { - releaseHash = BitConverter.ToString(sha.ComputeHash(stream)).Replace("-", "").ToLower(); - } + await using var fileStream = File.OpenWrite(releaseZip); + await using var artifactStream = await _httpClient.GetStreamAsync(releaseAsset.BrowserDownloadUrl); + await artifactStream.CopyToAsync(fileStream); + } - File.Delete(releaseZip); + string releaseHash; + await using (var stream = File.OpenRead(releaseZip)) + using (var sha = SHA256.Create()) + { + releaseHash = BitConverter.ToString(await sha.ComputeHashAsync(stream)).Replace("-", "").ToLowerInvariant(); + } - // Add to database. - var updateFile = new UpdateFileEntity + // Add to database. + var updateFile = new UpdateFileEntity + { + UpdateId = updateId, + OperatingSystem = operatingSystem.Value, + Architecture = arch, + Runtime = runtime, + Filename = releaseAsset.Name, + Url = releaseAsset.BrowserDownloadUrl, + Hash = releaseHash, + Installer = installer + }; + + await _updateFileService.Insert(updateFile).ConfigureAwait(false); + } + finally { - UpdateId = updateId, - OperatingSystem = operatingSystem.Value, - Architecture = arch, - Runtime = runtime, - Filename = releaseAsset.Name, - Url = releaseAsset.BrowserDownloadUrl, - Hash = releaseHash, - Installer = installer - }; - - await _updateFileService.Insert(updateFile).ConfigureAwait(false); + if (File.Exists(releaseZip)) + { + File.Delete(releaseZip); + } + } } } } diff --git a/src/ServarrAPI/Release/ReleaseService.cs b/src/ServarrAPI/Release/ReleaseService.cs index e3f986c..ead16c3 100644 --- a/src/ServarrAPI/Release/ReleaseService.cs +++ b/src/ServarrAPI/Release/ReleaseService.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Net.Http; using System.Text; using System.Text.Json; @@ -46,7 +45,7 @@ public async Task UpdateReleasesAsync(Type releaseSource) var updatedBranches = await releaseSourceInstance.StartFetchReleasesAsync().ConfigureAwait(false); - if (updatedBranches != null && updatedBranches.Count > 0) + if (updatedBranches is { Count: > 0 }) { foreach (var branch in updatedBranches) { @@ -74,7 +73,7 @@ private async Task CallTriggers(string branch) request.Headers.Add("Authorization", "Bearer " + trigger.AuthToken); } - string json = JsonSerializer.Serialize(new { Application = _config.Project, Branch = branch }, JsonOptions); + var json = JsonSerializer.Serialize(new { Application = _config.Project, Branch = branch }, JsonOptions); var httpContent = new StringContent(json, Encoding.UTF8, "application/json"); request.Content = httpContent;