Add Config Validator + Bug Fixing
This commit is contained in:
@@ -33,6 +33,7 @@ Einige Beispiele finden sich [weiter unten](https://github.com/PCJones/UmlautAda
|
|||||||
| Anfragen-Caching für 12 Minuten zur Reduzierung der API-Zugriffe | ✓ |
|
| Anfragen-Caching für 12 Minuten zur Reduzierung der API-Zugriffe | ✓ |
|
||||||
| Usenet (newznab) Support |✓|
|
| Usenet (newznab) Support |✓|
|
||||||
| Torrent (torznab) Support |✓|
|
| Torrent (torznab) Support |✓|
|
||||||
|
| Support von meheren *arrs Instanzen | ✓
|
||||||
| Radarr Support | Geplant |
|
| Radarr Support | Geplant |
|
||||||
| Prowlarr Unterstützung für "DE" SceneNZBs Kategorien | Geplant |
|
| Prowlarr Unterstützung für "DE" SceneNZBs Kategorien | Geplant |
|
||||||
| Unterstützung weiterer Sprachen neben Deutsch | Geplant |
|
| Unterstützung weiterer Sprachen neben Deutsch | Geplant |
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ internal class Program
|
|||||||
//options.SizeLimit = 20000;
|
//options.SizeLimit = 20000;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
builder.Services.AllowResolvingKeyedServicesAsDictionary();
|
||||||
builder.Services.AddControllers();
|
builder.Services.AddControllers();
|
||||||
builder.AddTitleLookupService();
|
builder.AddTitleLookupService();
|
||||||
builder.Services.AddSingleton<SearchItemLookupService>();
|
builder.Services.AddSingleton<SearchItemLookupService>();
|
||||||
@@ -63,7 +63,7 @@ internal class Program
|
|||||||
builder.AddReadarrSupport();
|
builder.AddReadarrSupport();
|
||||||
builder.Services.AddSingleton<CacheService>();
|
builder.Services.AddSingleton<CacheService>();
|
||||||
builder.Services.AddSingleton<ProxyRequestService>();
|
builder.Services.AddSingleton<ProxyRequestService>();
|
||||||
builder.Services.AddSingleton<RrApplicationFactory>();
|
builder.Services.AddSingleton<ArrApplicationFactory>();
|
||||||
builder.Services.AddHostedService<ArrSyncBackgroundService>();
|
builder.Services.AddHostedService<ArrSyncBackgroundService>();
|
||||||
builder.Services.AddSingleton<IHostedService, HttpProxyService>();
|
builder.Services.AddSingleton<IHostedService, HttpProxyService>();
|
||||||
|
|
||||||
|
|||||||
@@ -4,12 +4,12 @@ using UmlautAdaptarr.Services.Factory;
|
|||||||
namespace UmlautAdaptarr.Services;
|
namespace UmlautAdaptarr.Services;
|
||||||
|
|
||||||
public class ArrSyncBackgroundService(
|
public class ArrSyncBackgroundService(
|
||||||
RrApplicationFactory rrApplicationFactory,
|
ArrApplicationFactory arrApplicationFactory,
|
||||||
CacheService cacheService,
|
CacheService cacheService,
|
||||||
ILogger<ArrSyncBackgroundService> logger)
|
ILogger<ArrSyncBackgroundService> logger)
|
||||||
: BackgroundService
|
: BackgroundService
|
||||||
{
|
{
|
||||||
public RrApplicationFactory RrApplicationFactory { get; } = rrApplicationFactory;
|
public ArrApplicationFactory ArrApplicationFactory { get; } = arrApplicationFactory;
|
||||||
|
|
||||||
|
|
||||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||||
@@ -56,19 +56,19 @@ public class ArrSyncBackgroundService(
|
|||||||
var success = true;
|
var success = true;
|
||||||
|
|
||||||
|
|
||||||
if (RrApplicationFactory.SonarrInstances.Any())
|
if (ArrApplicationFactory.SonarrInstances.Any())
|
||||||
{
|
{
|
||||||
var syncSuccess = await FetchItemsFromSonarrAsync();
|
var syncSuccess = await FetchItemsFromSonarrAsync();
|
||||||
success = success && syncSuccess;
|
success = success && syncSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (RrApplicationFactory.ReadarrInstances.Any())
|
if (ArrApplicationFactory.ReadarrInstances.Any())
|
||||||
{
|
{
|
||||||
var syncSuccess = await FetchItemsFromReadarrAsync();
|
var syncSuccess = await FetchItemsFromReadarrAsync();
|
||||||
success = success && syncSuccess;
|
success = success && syncSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (RrApplicationFactory.ReadarrInstances.Any())
|
if (ArrApplicationFactory.ReadarrInstances.Any())
|
||||||
{
|
{
|
||||||
var syncSuccess = await FetchItemsFromLidarrAsync();
|
var syncSuccess = await FetchItemsFromLidarrAsync();
|
||||||
success = success && syncSuccess;
|
success = success && syncSuccess;
|
||||||
@@ -91,7 +91,7 @@ public class ArrSyncBackgroundService(
|
|||||||
{
|
{
|
||||||
var items = new List<SearchItem>();
|
var items = new List<SearchItem>();
|
||||||
|
|
||||||
foreach (var sonarrClient in RrApplicationFactory.SonarrInstances)
|
foreach (var sonarrClient in ArrApplicationFactory.SonarrInstances)
|
||||||
{
|
{
|
||||||
var result = await sonarrClient.FetchAllItemsAsync();
|
var result = await sonarrClient.FetchAllItemsAsync();
|
||||||
items = items.Union(result).ToList();
|
items = items.Union(result).ToList();
|
||||||
@@ -115,7 +115,7 @@ public class ArrSyncBackgroundService(
|
|||||||
{
|
{
|
||||||
var items = new List<SearchItem>();
|
var items = new List<SearchItem>();
|
||||||
|
|
||||||
foreach (var lidarrClient in RrApplicationFactory.LidarrInstances)
|
foreach (var lidarrClient in ArrApplicationFactory.LidarrInstances)
|
||||||
{
|
{
|
||||||
var result = await lidarrClient.FetchAllItemsAsync();
|
var result = await lidarrClient.FetchAllItemsAsync();
|
||||||
items = items.Union(result).ToList();
|
items = items.Union(result).ToList();
|
||||||
@@ -138,7 +138,7 @@ public class ArrSyncBackgroundService(
|
|||||||
{
|
{
|
||||||
var items = new List<SearchItem>();
|
var items = new List<SearchItem>();
|
||||||
|
|
||||||
foreach (var readarrClient in RrApplicationFactory.ReadarrInstances)
|
foreach (var readarrClient in ArrApplicationFactory.ReadarrInstances)
|
||||||
{
|
{
|
||||||
var result = await readarrClient.FetchAllItemsAsync();
|
var result = await readarrClient.FetchAllItemsAsync();
|
||||||
items = items.Union(result).ToList();
|
items = items.Union(result).ToList();
|
||||||
|
|||||||
@@ -6,9 +6,9 @@ namespace UmlautAdaptarr.Services.Factory
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Factory for creating RrApplication instances.
|
/// Factory for creating RrApplication instances.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class RrApplicationFactory
|
public class ArrApplicationFactory
|
||||||
{
|
{
|
||||||
private readonly ILogger<RrApplicationFactory> _logger;
|
private readonly ILogger<ArrApplicationFactory> _logger;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get all IArrApplication instances.
|
/// Get all IArrApplication instances.
|
||||||
@@ -31,10 +31,11 @@ namespace UmlautAdaptarr.Services.Factory
|
|||||||
public IEnumerable<ReadarrClient> ReadarrInstances { get; init; }
|
public IEnumerable<ReadarrClient> ReadarrInstances { get; init; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Constructor for the RrApplicationFactory.
|
/// Constructor for the ArrApplicationFactory.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="rrArrApplications">A dictionary of IArrApplication instances.</param>
|
/// <param name="rrArrApplications">A dictionary of IArrApplication instances.</param>
|
||||||
public RrApplicationFactory(IDictionary<string, IArrApplication> rrArrApplications, ILogger<RrApplicationFactory> logger)
|
/// <param name="logger">Logger Instanz</param>
|
||||||
|
public ArrApplicationFactory(IDictionary<string, IArrApplication> rrArrApplications, ILogger<ArrApplicationFactory> logger)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
try
|
try
|
||||||
@@ -51,7 +52,7 @@ namespace UmlautAdaptarr.Services.Factory
|
|||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
_logger.LogError("Register RrFactory", e.Message);
|
_logger.LogError("Error while Register ArrFactory. This might be a Config Problem", e.Message);
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5,7 +5,7 @@ using UmlautAdaptarr.Services.Factory;
|
|||||||
namespace UmlautAdaptarr.Services
|
namespace UmlautAdaptarr.Services
|
||||||
{
|
{
|
||||||
public class SearchItemLookupService(CacheService cacheService,
|
public class SearchItemLookupService(CacheService cacheService,
|
||||||
RrApplicationFactory rrApplicationFactory)
|
ArrApplicationFactory arrApplicationFactory)
|
||||||
{
|
{
|
||||||
public async Task<SearchItem?> GetOrFetchSearchItemByExternalId(string mediaType, string externalId)
|
public async Task<SearchItem?> GetOrFetchSearchItemByExternalId(string mediaType, string externalId)
|
||||||
{
|
{
|
||||||
@@ -22,7 +22,7 @@ namespace UmlautAdaptarr.Services
|
|||||||
{
|
{
|
||||||
case "tv":
|
case "tv":
|
||||||
|
|
||||||
var sonarrInstances = rrApplicationFactory.SonarrInstances;
|
var sonarrInstances = arrApplicationFactory.SonarrInstances;
|
||||||
|
|
||||||
if (sonarrInstances.Any())
|
if (sonarrInstances.Any())
|
||||||
{
|
{
|
||||||
@@ -34,7 +34,7 @@ namespace UmlautAdaptarr.Services
|
|||||||
break;
|
break;
|
||||||
case "audio":
|
case "audio":
|
||||||
|
|
||||||
var lidarrInstances = rrApplicationFactory.LidarrInstances;
|
var lidarrInstances = arrApplicationFactory.LidarrInstances;
|
||||||
|
|
||||||
if (lidarrInstances.Any())
|
if (lidarrInstances.Any())
|
||||||
{
|
{
|
||||||
@@ -47,7 +47,7 @@ namespace UmlautAdaptarr.Services
|
|||||||
break;
|
break;
|
||||||
case "book":
|
case "book":
|
||||||
|
|
||||||
var readarrInstances = rrApplicationFactory.ReadarrInstances;
|
var readarrInstances = arrApplicationFactory.ReadarrInstances;
|
||||||
if (readarrInstances.Any())
|
if (readarrInstances.Any())
|
||||||
{
|
{
|
||||||
foreach (var readarrClient in readarrInstances)
|
foreach (var readarrClient in readarrInstances)
|
||||||
@@ -83,7 +83,7 @@ namespace UmlautAdaptarr.Services
|
|||||||
{
|
{
|
||||||
case "tv":
|
case "tv":
|
||||||
|
|
||||||
var sonarrInstances = rrApplicationFactory.SonarrInstances;
|
var sonarrInstances = arrApplicationFactory.SonarrInstances;
|
||||||
foreach (var sonarrClient in sonarrInstances)
|
foreach (var sonarrClient in sonarrInstances)
|
||||||
{
|
{
|
||||||
fetchedItem = await sonarrClient.FetchItemByTitleAsync(title);
|
fetchedItem = await sonarrClient.FetchItemByTitleAsync(title);
|
||||||
|
|||||||
@@ -9,6 +9,8 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="FluentValidation.DependencyInjectionExtensions" Version="11.9.1" />
|
||||||
|
<PackageReference Include="IL.FluentValidation.Extensions.Options" Version="11.0.2" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Options" Version="8.0.2" />
|
<PackageReference Include="Microsoft.Extensions.Options" Version="8.0.2" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="8.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="8.0.0" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
using System.Linq.Expressions;
|
using FluentValidation;
|
||||||
|
using System.Linq.Expressions;
|
||||||
using UmlautAdaptarr.Interfaces;
|
using UmlautAdaptarr.Interfaces;
|
||||||
using UmlautAdaptarr.Options;
|
using UmlautAdaptarr.Options;
|
||||||
using UmlautAdaptarr.Options.ArrOptions.InstanceOptions;
|
using UmlautAdaptarr.Options.ArrOptions.InstanceOptions;
|
||||||
using UmlautAdaptarr.Providers;
|
using UmlautAdaptarr.Providers;
|
||||||
using UmlautAdaptarr.Services;
|
using UmlautAdaptarr.Services;
|
||||||
|
using UmlautAdaptarr.Validator;
|
||||||
|
|
||||||
namespace UmlautAdaptarr.Utilities;
|
namespace UmlautAdaptarr.Utilities;
|
||||||
|
|
||||||
@@ -12,6 +14,12 @@ namespace UmlautAdaptarr.Utilities;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static class ServicesExtensions
|
public static class ServicesExtensions
|
||||||
{
|
{
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Logger instance for logging proxy configurations.
|
||||||
|
/// </summary>
|
||||||
|
private static ILogger Logger = GlobalStaticLogger.Logger;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds a service with specified options and service to the service collection.
|
/// Adds a service with specified options and service to the service collection.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -26,6 +34,9 @@ public static class ServicesExtensions
|
|||||||
where TOptions : class, new()
|
where TOptions : class, new()
|
||||||
where TService : class, TInterface
|
where TService : class, TInterface
|
||||||
where TInterface : class
|
where TInterface : class
|
||||||
|
{
|
||||||
|
|
||||||
|
try
|
||||||
{
|
{
|
||||||
if (builder.Services == null) throw new ArgumentNullException(nameof(builder), "Service collection is null.");
|
if (builder.Services == null) throw new ArgumentNullException(nameof(builder), "Service collection is null.");
|
||||||
|
|
||||||
@@ -48,6 +59,21 @@ public static class ServicesExtensions
|
|||||||
|
|
||||||
foreach (var option in optionsArray)
|
foreach (var option in optionsArray)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
GlobalInstanceOptionsValidator validator = new GlobalInstanceOptionsValidator();
|
||||||
|
|
||||||
|
var results = validator.Validate(option as GlobalInstanceOptions);
|
||||||
|
|
||||||
|
if (!results.IsValid)
|
||||||
|
{
|
||||||
|
foreach (var failure in results.Errors)
|
||||||
|
{
|
||||||
|
Console.WriteLine(($"Property {failure.PropertyName } failed validation. Error was: {failure.ErrorMessage}"));
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Exception("Please fix first you config and then Start UmlautAdaptarr again");
|
||||||
|
}
|
||||||
|
|
||||||
var instanceState = (bool)(typeof(TOptions).GetProperty("Enabled")?.GetValue(option, null) ?? false);
|
var instanceState = (bool)(typeof(TOptions).GetProperty("Enabled")?.GetValue(option, null) ?? false);
|
||||||
|
|
||||||
// We only want to create instances that are enabled in the Configs
|
// We only want to create instances that are enabled in the Configs
|
||||||
@@ -74,18 +100,23 @@ public static class ServicesExtensions
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Console.WriteLine(prop.PropertyType + "No Support");
|
Logger.LogWarning((prop.PropertyType + "No Support"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
builder.Services.AllowResolvingKeyedServicesAsDictionary();
|
|
||||||
builder.Services.AddKeyedSingleton<TInterface, TService>(instanceName);
|
builder.Services.AddKeyedSingleton<TInterface, TService>(instanceName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Error while Init UmlautAdaptrr");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds a service with specified options and service to the service collection.
|
/// Adds a service with specified options and service to the service collection.
|
||||||
|
|||||||
46
UmlautAdaptarr/Validator/GlobalInstanceOptionsValidator.cs
Normal file
46
UmlautAdaptarr/Validator/GlobalInstanceOptionsValidator.cs
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
using FluentValidation;
|
||||||
|
using System.Net;
|
||||||
|
using UmlautAdaptarr.Options.ArrOptions.InstanceOptions;
|
||||||
|
|
||||||
|
namespace UmlautAdaptarr.Validator
|
||||||
|
{
|
||||||
|
public class GlobalInstanceOptionsValidator : AbstractValidator<GlobalInstanceOptions>
|
||||||
|
{
|
||||||
|
public GlobalInstanceOptionsValidator()
|
||||||
|
{
|
||||||
|
RuleFor(x => x.Enabled).NotNull();
|
||||||
|
|
||||||
|
When(x => x.Enabled, () =>
|
||||||
|
{
|
||||||
|
|
||||||
|
RuleFor(x => x.Host)
|
||||||
|
.NotEmpty().WithMessage("Host is required when Enabled is true.")
|
||||||
|
.Must(BeAValidUrl).WithMessage("Host must start with http:// or https:// and be a valid address.")
|
||||||
|
.Must(BeReachable).WithMessage("Host is not reachable. Please check your Host or your UmlautAdaptrr Settings");
|
||||||
|
|
||||||
|
RuleFor(x => x.ApiKey)
|
||||||
|
.NotEmpty().WithMessage("ApiKey is required when Enabled is true.");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool BeAValidUrl(string url)
|
||||||
|
{
|
||||||
|
return Uri.TryCreate(url, UriKind.Absolute, out var uriResult)
|
||||||
|
&& (uriResult.Scheme == Uri.UriSchemeHttp || uriResult.Scheme == Uri.UriSchemeHttps);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool BeReachable(string url)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var request = WebRequest.Create(url);
|
||||||
|
var response = (HttpWebResponse)request.GetResponse();
|
||||||
|
return response.StatusCode == HttpStatusCode.OK;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -26,6 +26,7 @@
|
|||||||
{
|
{
|
||||||
// Docker Environment Variables:
|
// Docker Environment Variables:
|
||||||
// - Sonarr__0__Enabled: true (set to false to disable)
|
// - Sonarr__0__Enabled: true (set to false to disable)
|
||||||
|
// - Sonarr__0__Name: Name of the Instance (Optional)
|
||||||
// - Sonarr__0__Host: your_sonarr_host_url
|
// - Sonarr__0__Host: your_sonarr_host_url
|
||||||
// - Sonarr__0__ApiKey: your_sonarr_api_key
|
// - Sonarr__0__ApiKey: your_sonarr_api_key
|
||||||
"Enabled": false,
|
"Enabled": false,
|
||||||
@@ -36,6 +37,7 @@
|
|||||||
{
|
{
|
||||||
// Docker Environment Variables:
|
// Docker Environment Variables:
|
||||||
// - Sonarr__1__Enabled: true (set to false to disable)
|
// - Sonarr__1__Enabled: true (set to false to disable)
|
||||||
|
// - Sonarr__0__Name: Name of the Instance (Optional)
|
||||||
// - Sonarr__1__Host: your_sonarr_host_url
|
// - Sonarr__1__Host: your_sonarr_host_url
|
||||||
// - Sonarr__1__ApiKey: your_sonarr_api_key
|
// - Sonarr__1__ApiKey: your_sonarr_api_key
|
||||||
"Enabled": false,
|
"Enabled": false,
|
||||||
|
|||||||
Reference in New Issue
Block a user