diff --git a/src/NzbDrone.Common.Test/ConfigFileProviderTest.cs b/src/NzbDrone.Common.Test/ConfigFileProviderTest.cs index dd32e187d..e9d4aa3b0 100644 --- a/src/NzbDrone.Common.Test/ConfigFileProviderTest.cs +++ b/src/NzbDrone.Common.Test/ConfigFileProviderTest.cs @@ -1,10 +1,12 @@ using System.Collections.Generic; using FluentAssertions; +using Microsoft.Extensions.Options; using Moq; using NUnit.Framework; using NzbDrone.Common.Disk; using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Common.Extensions; +using NzbDrone.Common.Options; using NzbDrone.Core.Authentication; using NzbDrone.Core.Configuration; using NzbDrone.Test.Common; @@ -43,6 +45,26 @@ namespace NzbDrone.Common.Test Mocker.GetMock() .Setup(v => v.WriteAllText(configFile, It.IsAny())) .Callback((p, t) => _configFileContents = t); + + Mocker.GetMock>() + .Setup(v => v.Value) + .Returns(new AuthOptions()); + + Mocker.GetMock>() + .Setup(v => v.Value) + .Returns(new AppOptions()); + + Mocker.GetMock>() + .Setup(v => v.Value) + .Returns(new ServerOptions()); + + Mocker.GetMock>() + .Setup(v => v.Value) + .Returns(new LogOptions()); + + Mocker.GetMock>() + .Setup(v => v.Value) + .Returns(new UpdateOptions()); } [Test] diff --git a/src/NzbDrone.Common.Test/ServiceFactoryFixture.cs b/src/NzbDrone.Common.Test/ServiceFactoryFixture.cs index 9c348316f..5ed08a3db 100644 --- a/src/NzbDrone.Common.Test/ServiceFactoryFixture.cs +++ b/src/NzbDrone.Common.Test/ServiceFactoryFixture.cs @@ -10,6 +10,7 @@ using NUnit.Framework; using NzbDrone.Common.Composition.Extensions; using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Common.Instrumentation.Extensions; +using NzbDrone.Common.Options; using NzbDrone.Core.Datastore; using NzbDrone.Core.Datastore.Extensions; using NzbDrone.Core.Lifecycle; @@ -33,6 +34,11 @@ namespace NzbDrone.Common.Test container.RegisterInstance(new Mock().Object); container.RegisterInstance(new Mock>().Object); + container.RegisterInstance(new Mock>().Object); + container.RegisterInstance(new Mock>().Object); + container.RegisterInstance(new Mock>().Object); + container.RegisterInstance(new Mock>().Object); + container.RegisterInstance(new Mock>().Object); var serviceProvider = container.GetServiceProvider(); diff --git a/src/NzbDrone.Common/Options/AppOptions.cs b/src/NzbDrone.Common/Options/AppOptions.cs new file mode 100644 index 000000000..74cdf1d29 --- /dev/null +++ b/src/NzbDrone.Common/Options/AppOptions.cs @@ -0,0 +1,8 @@ +namespace NzbDrone.Common.Options; + +public class AppOptions +{ + public string InstanceName { get; set; } + public string Theme { get; set; } + public bool? LaunchBrowser { get; set; } +} diff --git a/src/NzbDrone.Common/Options/AuthOptions.cs b/src/NzbDrone.Common/Options/AuthOptions.cs new file mode 100644 index 000000000..2b63308d3 --- /dev/null +++ b/src/NzbDrone.Common/Options/AuthOptions.cs @@ -0,0 +1,9 @@ +namespace NzbDrone.Common.Options; + +public class AuthOptions +{ + public string ApiKey { get; set; } + public bool? Enabled { get; set; } + public string Method { get; set; } + public string Required { get; set; } +} diff --git a/src/NzbDrone.Common/Options/LogOptions.cs b/src/NzbDrone.Common/Options/LogOptions.cs new file mode 100644 index 000000000..c36533cb4 --- /dev/null +++ b/src/NzbDrone.Common/Options/LogOptions.cs @@ -0,0 +1,14 @@ +namespace NzbDrone.Common.Options; + +public class LogOptions +{ + public string Level { get; set; } + public bool? FilterSentryEvents { get; set; } + public int? Rotate { get; set; } + public bool? Sql { get; set; } + public string ConsoleLevel { get; set; } + public bool? AnalyticsEnabled { get; set; } + public string SyslogServer { get; set; } + public int? SyslogPort { get; set; } + public string SyslogLevel { get; set; } +} diff --git a/src/NzbDrone.Common/Options/ServerOptions.cs b/src/NzbDrone.Common/Options/ServerOptions.cs new file mode 100644 index 000000000..d21e12b2a --- /dev/null +++ b/src/NzbDrone.Common/Options/ServerOptions.cs @@ -0,0 +1,12 @@ +namespace NzbDrone.Common.Options; + +public class ServerOptions +{ + public string UrlBase { get; set; } + public string BindAddress { get; set; } + public int? Port { get; set; } + public bool? EnableSsl { get; set; } + public int? SslPort { get; set; } + public string SslCertPath { get; set; } + public string SslCertPassword { get; set; } +} diff --git a/src/NzbDrone.Common/Options/UpdateOptions.cs b/src/NzbDrone.Common/Options/UpdateOptions.cs new file mode 100644 index 000000000..a8eaad8fb --- /dev/null +++ b/src/NzbDrone.Common/Options/UpdateOptions.cs @@ -0,0 +1,9 @@ +namespace NzbDrone.Common.Options; + +public class UpdateOptions +{ + public string Mechanism { get; set; } + public bool? Automatically { get; set; } + public string ScriptPath { get; set; } + public string Branch { get; set; } +} diff --git a/src/NzbDrone.Core/Configuration/ConfigFileProvider.cs b/src/NzbDrone.Core/Configuration/ConfigFileProvider.cs index e2163d524..66f092e0f 100644 --- a/src/NzbDrone.Core/Configuration/ConfigFileProvider.cs +++ b/src/NzbDrone.Core/Configuration/ConfigFileProvider.cs @@ -10,6 +10,7 @@ using NzbDrone.Common.Cache; using NzbDrone.Common.Disk; using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Common.Extensions; +using NzbDrone.Common.Options; using NzbDrone.Core.Authentication; using NzbDrone.Core.Configuration.Events; using NzbDrone.Core.Datastore; @@ -53,13 +54,13 @@ namespace NzbDrone.Core.Configuration string SyslogServer { get; } int SyslogPort { get; } string SyslogLevel { get; } + string Theme { get; } string PostgresHost { get; } int PostgresPort { get; } string PostgresUser { get; } string PostgresPassword { get; } string PostgresMainDb { get; } string PostgresLogDb { get; } - string Theme { get; } } public class ConfigFileProvider : IConfigFileProvider @@ -72,6 +73,11 @@ namespace NzbDrone.Core.Configuration private readonly IDiskProvider _diskProvider; private readonly ICached _cache; private readonly PostgresOptions _postgresOptions; + private readonly AuthOptions _authOptions; + private readonly AppOptions _appOptions; + private readonly ServerOptions _serverOptions; + private readonly UpdateOptions _updateOptions; + private readonly LogOptions _logOptions; private readonly string _configFile; private static readonly Regex HiddenCharacterRegex = new Regex("[^a-z0-9]", RegexOptions.Compiled | RegexOptions.IgnoreCase); @@ -82,13 +88,23 @@ namespace NzbDrone.Core.Configuration ICacheManager cacheManager, IEventAggregator eventAggregator, IDiskProvider diskProvider, - IOptions postgresOptions) + IOptions postgresOptions, + IOptions authOptions, + IOptions appOptions, + IOptions serverOptions, + IOptions updateOptions, + IOptions logOptions) { _cache = cacheManager.GetCache(GetType()); _eventAggregator = eventAggregator; _diskProvider = diskProvider; _configFile = appFolderInfo.GetConfigPath(); _postgresOptions = postgresOptions.Value; + _authOptions = authOptions.Value; + _appOptions = appOptions.Value; + _serverOptions = serverOptions.Value; + _updateOptions = updateOptions.Value; + _logOptions = logOptions.Value; } public Dictionary GetConfigDictionary() @@ -144,7 +160,7 @@ namespace NzbDrone.Core.Configuration { const string defaultValue = "*"; - var bindAddress = GetValue("BindAddress", defaultValue); + var bindAddress = _serverOptions.BindAddress ?? GetValue("BindAddress", defaultValue); if (string.IsNullOrWhiteSpace(bindAddress)) { return defaultValue; @@ -154,19 +170,19 @@ namespace NzbDrone.Core.Configuration } } - public int Port => GetValueInt("Port", DEFAULT_PORT); + public int Port => _serverOptions.Port ?? GetValueInt("Port", DEFAULT_PORT); - public int SslPort => GetValueInt("SslPort", DEFAULT_SSL_PORT); + public int SslPort => _serverOptions.SslPort ?? GetValueInt("SslPort", DEFAULT_SSL_PORT); - public bool EnableSsl => GetValueBoolean("EnableSsl", false); + public bool EnableSsl => _serverOptions.EnableSsl ?? GetValueBoolean("EnableSsl", false); - public bool LaunchBrowser => GetValueBoolean("LaunchBrowser", true); + public bool LaunchBrowser => _appOptions.LaunchBrowser ?? GetValueBoolean("LaunchBrowser", true); public string ApiKey { get { - var apiKey = GetValue("ApiKey", GenerateApiKey()); + var apiKey = _authOptions.ApiKey ?? GetValue("ApiKey", GenerateApiKey()); if (apiKey.IsNullOrWhiteSpace()) { @@ -182,7 +198,7 @@ namespace NzbDrone.Core.Configuration { get { - var enabled = GetValueBoolean("AuthenticationEnabled", false, false); + var enabled = _authOptions.Enabled ?? GetValueBoolean("AuthenticationEnabled", false, false); if (enabled) { @@ -190,37 +206,44 @@ namespace NzbDrone.Core.Configuration return AuthenticationType.Basic; } - return GetValueEnum("AuthenticationMethod", AuthenticationType.None); + return Enum.TryParse(_authOptions.Method, out var enumValue) + ? enumValue + : GetValueEnum("AuthenticationMethod", AuthenticationType.None); } } - public AuthenticationRequiredType AuthenticationRequired => GetValueEnum("AuthenticationRequired", AuthenticationRequiredType.Enabled); + public AuthenticationRequiredType AuthenticationRequired => + Enum.TryParse(_authOptions.Required, out var enumValue) + ? enumValue + : GetValueEnum("AuthenticationRequired", AuthenticationRequiredType.Enabled); + + public bool AnalyticsEnabled => _logOptions.AnalyticsEnabled ?? GetValueBoolean("AnalyticsEnabled", true, persist: false); + + public string Branch => _updateOptions.Branch ?? GetValue("Branch", "master").ToLowerInvariant(); - public bool AnalyticsEnabled => GetValueBoolean("AnalyticsEnabled", true, persist: false); + public string LogLevel => _logOptions.Level ?? GetValue("LogLevel", "info").ToLowerInvariant(); + public string ConsoleLogLevel => _logOptions.ConsoleLevel ?? GetValue("ConsoleLogLevel", string.Empty, persist: false); - // TODO: Change back to "master" for the first stable release. - public string Branch => GetValue("Branch", "master").ToLowerInvariant(); + public string Theme => _appOptions.Theme ?? GetValue("Theme", "auto", persist: false); - public string LogLevel => GetValue("LogLevel", "info").ToLowerInvariant(); - public string ConsoleLogLevel => GetValue("ConsoleLogLevel", string.Empty, persist: false); public string PostgresHost => _postgresOptions?.Host ?? GetValue("PostgresHost", string.Empty, persist: false); public string PostgresUser => _postgresOptions?.User ?? GetValue("PostgresUser", string.Empty, persist: false); public string PostgresPassword => _postgresOptions?.Password ?? GetValue("PostgresPassword", string.Empty, persist: false); public string PostgresMainDb => _postgresOptions?.MainDb ?? GetValue("PostgresMainDb", "prowlarr-main", persist: false); public string PostgresLogDb => _postgresOptions?.LogDb ?? GetValue("PostgresLogDb", "prowlarr-log", persist: false); public int PostgresPort => (_postgresOptions?.Port ?? 0) != 0 ? _postgresOptions.Port : GetValueInt("PostgresPort", 5432, persist: false); - public string Theme => GetValue("Theme", "auto", persist: false); - public bool LogSql => GetValueBoolean("LogSql", false, persist: false); - public int LogRotate => GetValueInt("LogRotate", 50, persist: false); - public bool FilterSentryEvents => GetValueBoolean("FilterSentryEvents", true, persist: false); - public string SslCertPath => GetValue("SslCertPath", ""); - public string SslCertPassword => GetValue("SslCertPassword", ""); + + public bool LogSql => _logOptions.Sql ?? GetValueBoolean("LogSql", false, persist: false); + public int LogRotate => _logOptions.Rotate ?? GetValueInt("LogRotate", 50, persist: false); + public bool FilterSentryEvents => _logOptions.FilterSentryEvents ?? GetValueBoolean("FilterSentryEvents", true, persist: false); + public string SslCertPath => _serverOptions.SslCertPath ?? GetValue("SslCertPath", ""); + public string SslCertPassword => _serverOptions.SslCertPassword ?? GetValue("SslCertPassword", ""); public string UrlBase { get { - var urlBase = GetValue("UrlBase", "").Trim('/'); + var urlBase = _serverOptions.UrlBase ?? GetValue("UrlBase", "").Trim('/'); if (urlBase.IsNullOrWhiteSpace()) { @@ -232,19 +255,36 @@ namespace NzbDrone.Core.Configuration } public string UiFolder => BuildInfo.IsDebug ? Path.Combine("..", "UI") : "UI"; - public string InstanceName => GetValue("InstanceName", BuildInfo.AppName); - public bool UpdateAutomatically => GetValueBoolean("UpdateAutomatically", false, false); + public string InstanceName + { + get + { + var instanceName = _appOptions.InstanceName ?? GetValue("InstanceName", BuildInfo.AppName); + + if (instanceName.ContainsIgnoreCase(BuildInfo.AppName)) + { + return instanceName; + } + + return BuildInfo.AppName; + } + } + + public bool UpdateAutomatically => _updateOptions.Automatically ?? GetValueBoolean("UpdateAutomatically", false, false); - public UpdateMechanism UpdateMechanism => GetValueEnum("UpdateMechanism", UpdateMechanism.BuiltIn, false); + public UpdateMechanism UpdateMechanism => + Enum.TryParse(_updateOptions.Mechanism, out var enumValue) + ? enumValue + : GetValueEnum("UpdateMechanism", UpdateMechanism.BuiltIn, false); - public string UpdateScriptPath => GetValue("UpdateScriptPath", "", false); + public string UpdateScriptPath => _updateOptions.ScriptPath ?? GetValue("UpdateScriptPath", "", false); - public string SyslogServer => GetValue("SyslogServer", "", persist: false); + public string SyslogServer => _logOptions.SyslogServer ?? GetValue("SyslogServer", "", persist: false); - public int SyslogPort => GetValueInt("SyslogPort", 514, persist: false); + public int SyslogPort => _logOptions.SyslogPort ?? GetValueInt("SyslogPort", 514, persist: false); - public string SyslogLevel => GetValue("SyslogLevel", LogLevel, false).ToLowerInvariant(); + public string SyslogLevel => _logOptions.SyslogLevel ?? GetValue("SyslogLevel", LogLevel, persist: false).ToLowerInvariant(); public int GetValueInt(string key, int defaultValue, bool persist = true) { @@ -277,13 +317,13 @@ namespace NzbDrone.Core.Configuration return valueHolder.First().Value.Trim(); } - //Save the value + // Save the value if (persist) { SetValue(key, defaultValue); } - //return the default value + // return the default value return defaultValue.ToString(); }); } diff --git a/src/NzbDrone.Host.Test/ContainerFixture.cs b/src/NzbDrone.Host.Test/ContainerFixture.cs index 874c3e576..c04a0e705 100644 --- a/src/NzbDrone.Host.Test/ContainerFixture.cs +++ b/src/NzbDrone.Host.Test/ContainerFixture.cs @@ -12,6 +12,7 @@ using NzbDrone.Common; using NzbDrone.Common.Composition.Extensions; using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Common.Instrumentation.Extensions; +using NzbDrone.Common.Options; using NzbDrone.Core.Datastore; using NzbDrone.Core.Datastore.Extensions; using NzbDrone.Core.Download; @@ -46,6 +47,11 @@ namespace NzbDrone.App.Test container.RegisterInstance(new Mock().Object); container.RegisterInstance(new Mock().Object); container.RegisterInstance>(new Mock>().Object); + container.RegisterInstance>(new Mock>().Object); + container.RegisterInstance>(new Mock>().Object); + container.RegisterInstance>(new Mock>().Object); + container.RegisterInstance>(new Mock>().Object); + container.RegisterInstance>(new Mock>().Object); _container = container.GetServiceProvider(); } diff --git a/src/NzbDrone.Host/Bootstrap.cs b/src/NzbDrone.Host/Bootstrap.cs index 5f4a750e2..b446dbc4c 100644 --- a/src/NzbDrone.Host/Bootstrap.cs +++ b/src/NzbDrone.Host/Bootstrap.cs @@ -22,6 +22,7 @@ using NzbDrone.Common.Exceptions; using NzbDrone.Common.Extensions; using NzbDrone.Common.Instrumentation; using NzbDrone.Common.Instrumentation.Extensions; +using NzbDrone.Common.Options; using NzbDrone.Core.Configuration; using NzbDrone.Core.Datastore.Extensions; using PostgresOptions = NzbDrone.Core.Datastore.PostgresOptions; @@ -97,6 +98,11 @@ namespace NzbDrone.Host .ConfigureServices(services => { services.Configure(config.GetSection("Prowlarr:Postgres")); + services.Configure(config.GetSection("Prowlarr:App")); + services.Configure(config.GetSection("Prowlarr:Auth")); + services.Configure(config.GetSection("Prowlarr:Server")); + services.Configure(config.GetSection("Prowlarr:Log")); + services.Configure(config.GetSection("Prowlarr:Update")); }).Build(); break; @@ -124,12 +130,12 @@ namespace NzbDrone.Host { var config = GetConfiguration(context); - var bindAddress = config.GetValue(nameof(ConfigFileProvider.BindAddress), "*"); - var port = config.GetValue(nameof(ConfigFileProvider.Port), ConfigFileProvider.DEFAULT_PORT); - var sslPort = config.GetValue(nameof(ConfigFileProvider.SslPort), ConfigFileProvider.DEFAULT_SSL_PORT); - var enableSsl = config.GetValue(nameof(ConfigFileProvider.EnableSsl), false); - var sslCertPath = config.GetValue(nameof(ConfigFileProvider.SslCertPath)); - var sslCertPassword = config.GetValue(nameof(ConfigFileProvider.SslCertPassword)); + var bindAddress = config.GetValue($"Prowlarr:Server:{nameof(ServerOptions.BindAddress)}") ?? config.GetValue(nameof(ConfigFileProvider.BindAddress), "*"); + var port = config.GetValue($"Prowlarr:Server:{nameof(ServerOptions.Port)}") ?? config.GetValue(nameof(ConfigFileProvider.Port), ConfigFileProvider.DEFAULT_PORT); + var sslPort = config.GetValue($"Prowlarr:Server:{nameof(ServerOptions.SslPort)}") ?? config.GetValue(nameof(ConfigFileProvider.SslPort), ConfigFileProvider.DEFAULT_SSL_PORT); + var enableSsl = config.GetValue($"Prowlarr:Server:{nameof(ServerOptions.EnableSsl)}") ?? config.GetValue(nameof(ConfigFileProvider.EnableSsl), false); + var sslCertPath = config.GetValue($"Prowlarr:Server:{nameof(ServerOptions.SslCertPath)}") ?? config.GetValue(nameof(ConfigFileProvider.SslCertPath)); + var sslCertPassword = config.GetValue($"Prowlarr:Server:{nameof(ServerOptions.SslCertPassword)}") ?? config.GetValue(nameof(ConfigFileProvider.SslCertPassword)); var urls = new List { BuildUrl("http", bindAddress, port) }; @@ -151,6 +157,11 @@ namespace NzbDrone.Host .ConfigureServices(services => { services.Configure(config.GetSection("Prowlarr:Postgres")); + services.Configure(config.GetSection("Prowlarr:App")); + services.Configure(config.GetSection("Prowlarr:Auth")); + services.Configure(config.GetSection("Prowlarr:Server")); + services.Configure(config.GetSection("Prowlarr:Log")); + services.Configure(config.GetSection("Prowlarr:Update")); services.Configure(x => { //Double the default multipart body length from 128 MB to 256 MB