From 4c582c7a6c18e8150d8c6ae389a4698f66d1bda0 Mon Sep 17 00:00:00 2001 From: akuntsch Date: Mon, 21 Oct 2024 17:15:11 +0200 Subject: [PATCH] Fix reachable and ipleak test (#47) * Fix reachable check Fixes failing reachable checks when Basic Authentication is enabled in Sonarr, Radarr, etc. * Add option to disable IP leak test --------- Co-authored-by: Jonas F --- UmlautAdaptarr/Program.cs | 23 ++++++---- UmlautAdaptarr/Utilities/Helper.cs | 4 +- .../Utilities/ServicesExtensions.cs | 12 +++--- .../GlobalInstanceOptionsValidator.cs | 42 +++++++------------ UmlautAdaptarr/appsettings.json | 3 ++ 5 files changed, 41 insertions(+), 43 deletions(-) diff --git a/UmlautAdaptarr/Program.cs b/UmlautAdaptarr/Program.cs index c6e42f4..5df6506 100644 --- a/UmlautAdaptarr/Program.cs +++ b/UmlautAdaptarr/Program.cs @@ -8,10 +8,12 @@ using UmlautAdaptarr.Utilities; internal class Program { - private static void Main(string[] args) + private static void Main(string[] args) { + MainAsync(args).Wait(); + } + + private static async Task MainAsync(string[] args) { - Helper.ShowLogo(); - Helper.ShowInformation(); // TODO: // add option to sort by nzb age var builder = WebApplication.CreateBuilder(args); @@ -43,9 +45,9 @@ internal class Program builder.AddTitleLookupService(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); - builder.AddSonarrSupport(); - builder.AddLidarrSupport(); - builder.AddReadarrSupport(); + await builder.AddSonarrSupport(); + await builder.AddLidarrSupport(); + await builder.AddReadarrSupport(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); @@ -54,6 +56,13 @@ internal class Program var app = builder.Build(); + Helper.ShowLogo(); + + if (app.Configuration.GetValue("IpLeakTest:Enabled")) + { + await Helper.ShowInformation(); + } + GlobalStaticLogger.Initialize(app.Services.GetService()!); app.UseHttpsRedirection(); app.UseAuthorization(); @@ -109,4 +118,4 @@ internal class Program //.Enrich.With(new ApiKeyMaskingEnricher("appsettings.json")) // TODO - Not working currently .CreateLogger(); } -} \ No newline at end of file +} diff --git a/UmlautAdaptarr/Utilities/Helper.cs b/UmlautAdaptarr/Utilities/Helper.cs index 002310e..ff6a26c 100644 --- a/UmlautAdaptarr/Utilities/Helper.cs +++ b/UmlautAdaptarr/Utilities/Helper.cs @@ -11,10 +11,10 @@ public static class Helper "\r\n _ _ _ _ ___ _ _ \r\n| | | | | | | | / _ \\ | | | | \r\n| | | |_ __ ___ | | __ _ _ _| |_/ /_\\ \\ __| | __ _ _ __ | |_ __ _ _ __ _ __ \r\n| | | | '_ ` _ \\| |/ _` | | | | __| _ |/ _` |/ _` | '_ \\| __/ _` | '__| '__|\r\n| |_| | | | | | | | (_| | |_| | |_| | | | (_| | (_| | |_) | || (_| | | | | \r\n \\___/|_| |_| |_|_|\\__,_|\\__,_|\\__\\_| |_/\\__,_|\\__,_| .__/ \\__\\__,_|_| |_| \r\n | | \r\n |_| \r\n"); } - public static void ShowInformation() + public static async Task ShowInformation() { Console.WriteLine("--------------------------[IP Leak Test]-----------------------------"); - var ipInfo = GetPublicIpAddressInfoAsync().GetAwaiter().GetResult(); + var ipInfo = await GetPublicIpAddressInfoAsync(); if (ipInfo != null) { diff --git a/UmlautAdaptarr/Utilities/ServicesExtensions.cs b/UmlautAdaptarr/Utilities/ServicesExtensions.cs index 7037172..3c7184d 100644 --- a/UmlautAdaptarr/Utilities/ServicesExtensions.cs +++ b/UmlautAdaptarr/Utilities/ServicesExtensions.cs @@ -29,7 +29,7 @@ public static class ServicesExtensions /// The to configure the service collection. /// The name of the configuration section containing service options. /// The configured . - private static WebApplicationBuilder AddServicesWithOptions( + private static async Task AddServicesWithOptions( this WebApplicationBuilder builder, string sectionName) where TOptions : class, new() where TService : class, TInterface @@ -57,9 +57,9 @@ public static class ServicesExtensions foreach (var option in optionsArray) { - GlobalInstanceOptionsValidator validator = new GlobalInstanceOptionsValidator(); + GlobalInstanceOptionsValidator validator = new(); - var results = validator.Validate(option as GlobalInstanceOptions); + var results = await validator.ValidateAsync(option as GlobalInstanceOptions); if (!results.IsValid) { @@ -143,7 +143,7 @@ public static class ServicesExtensions /// /// The to configure the service collection. /// The configured . - public static WebApplicationBuilder AddSonarrSupport(this WebApplicationBuilder builder) + public static Task AddSonarrSupport(this WebApplicationBuilder builder) { // builder.Serviceses.AddSingleton, OptionsMonitoSonarrInstanceOptionsns>>(); return builder.AddServicesWithOptions("Sonarr"); @@ -154,7 +154,7 @@ public static class ServicesExtensions /// /// The to configure the service collection. /// The configured . - public static WebApplicationBuilder AddLidarrSupport(this WebApplicationBuilder builder) + public static Task AddLidarrSupport(this WebApplicationBuilder builder) { return builder.AddServicesWithOptions("Lidarr"); } @@ -164,7 +164,7 @@ public static class ServicesExtensions /// /// The to configure the service collection. /// The configured . - public static WebApplicationBuilder AddReadarrSupport(this WebApplicationBuilder builder) + public static Task AddReadarrSupport(this WebApplicationBuilder builder) { return builder.AddServicesWithOptions("Readarr"); } diff --git a/UmlautAdaptarr/Validator/GlobalInstanceOptionsValidator.cs b/UmlautAdaptarr/Validator/GlobalInstanceOptionsValidator.cs index eca7e94..de6d087 100644 --- a/UmlautAdaptarr/Validator/GlobalInstanceOptionsValidator.cs +++ b/UmlautAdaptarr/Validator/GlobalInstanceOptionsValidator.cs @@ -6,6 +6,10 @@ namespace UmlautAdaptarr.Validator; public class GlobalInstanceOptionsValidator : AbstractValidator { + private readonly static HttpClient httpClient = new() { + Timeout = TimeSpan.FromSeconds(3) + }; + public GlobalInstanceOptionsValidator() { RuleFor(x => x.Enabled).NotNull(); @@ -14,12 +18,14 @@ public class GlobalInstanceOptionsValidator : AbstractValidator x.Host) .NotEmpty().WithMessage("Host is required when Enabled is true.") - .Must(BeAValidUrl).WithMessage("Host/Url must start with http:// or https:// and be a valid address.") - .Must(BeReachable) - .WithMessage("Host/Url is not reachable. Please check your Host or your UmlautAdaptrr Settings"); + .Must(BeAValidUrl).WithMessage("Host/Url must start with http:// or https:// and be a valid address."); RuleFor(x => x.ApiKey) .NotEmpty().WithMessage("ApiKey is required when Enabled is true."); + + RuleFor(x => x) + .MustAsync(BeReachable) + .WithMessage("Host/Url is not reachable. Please check your Host or your UmlautAdaptrr Settings"); }); } @@ -29,42 +35,22 @@ public class GlobalInstanceOptionsValidator : AbstractValidator BeReachable(GlobalInstanceOptions opts, CancellationToken cancellationToken) { var endTime = DateTime.Now.AddMinutes(3); var reachable = false; + var url = $"{opts.Host}/api?apikey={opts.ApiKey}"; while (DateTime.Now < endTime) { try { - // TODO use HttpClient here - var request = (HttpWebRequest)WebRequest.Create(url); - request.AllowAutoRedirect = false; - request.Timeout = 3000; - using var response = (HttpWebResponse)request.GetResponse(); + using var response = await httpClient.GetAsync(url, cancellationToken); reachable = response.StatusCode == HttpStatusCode.OK; - if (reachable) + if (response.IsSuccessStatusCode) { break; } - // If status is 301/302 (Found), follow the redirect manually - else if (response.StatusCode == HttpStatusCode.MovedPermanently || response.StatusCode == HttpStatusCode.Found) - { - var redirectUrl = response.Headers["Location"]; // Get the redirect URL - if (!string.IsNullOrEmpty(redirectUrl)) - { - // Create a new request for the redirected URL - var redirectRequest = (HttpWebRequest)WebRequest.Create(redirectUrl); - redirectRequest.Timeout = 3000; - using var redirectResponse = (HttpWebResponse)redirectRequest.GetResponse(); - reachable = redirectResponse.StatusCode == HttpStatusCode.OK; - if (reachable) - { - break; - } - } - } } catch { @@ -72,7 +58,7 @@ public class GlobalInstanceOptionsValidator : AbstractValidator