diff --git a/src/NzbDrone.Common.Test/ConfigFileProviderTest.cs b/src/NzbDrone.Common.Test/ConfigFileProviderTest.cs index 82c2f854e4..565da73382 100644 --- a/src/NzbDrone.Common.Test/ConfigFileProviderTest.cs +++ b/src/NzbDrone.Common.Test/ConfigFileProviderTest.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using FluentAssertions; using Moq; using NUnit.Framework; @@ -13,7 +13,7 @@ namespace NzbDrone.Common.Test { [TestFixture] - public class ConfigFileProviderTest : TestBase + public class ConfigFileWriterTest : TestBase { private string _configFileContents; private string _configFilePath; @@ -45,56 +45,7 @@ protected void WithMockConfigFile(string configFile) .Callback((p, t) => _configFileContents = t); } - [Test] - public void GetValue_Success() - { - const string key = "Port"; - const string value = "7878"; - - var result = Subject.GetValue(key, value); - - result.Should().Be(value); - } - - [Test] - public void GetInt_Success() - { - const string key = "Port"; - const int value = 7878; - - var result = Subject.GetValueInt(key, value); - - result.Should().Be(value); - } - - [Test] - public void GetBool_Success() - { - const string key = "LaunchBrowser"; - const bool value = true; - - var result = Subject.GetValueBoolean(key, value); - - result.Should().BeTrue(); - } - - [Test] - public void GetLaunchBrowser_Success() - { - var result = Subject.LaunchBrowser; - - result.Should().Be(true); - } - - [Test] - public void GetPort_Success() - { - const int value = 7878; - - var result = Subject.Port; - - result.Should().Be(value); - } + /* [Test] public void SetValue_bool() @@ -120,17 +71,6 @@ public void SetValue_int() result.Should().Be(value); } - [Test] - public void GetValue_New_Key() - { - const string key = "Hello"; - const string value = "World"; - - var result = Subject.GetValue(key, value); - - result.Should().Be(value); - } - [Test] public void GetAuthenticationType_No_Existing_Value() { @@ -139,6 +79,7 @@ public void GetAuthenticationType_No_Existing_Value() result.Should().Be(AuthenticationType.None); } + /* [Test] public void SaveDictionary_should_save_proper_value() { @@ -170,32 +111,6 @@ public void SaveDictionary_should_only_save_specified_values() Subject.Port.Should().Be(port); Subject.SslPort.Should().Be(sslPort); - } - - [Test] - public void should_throw_if_config_file_is_empty() - { - Mocker.GetMock() - .Setup(v => v.FileExists(_configFilePath)) - .Returns(true); - - Assert.Throws(() => Subject.GetValue("key", "value")); - } - - [Test] - public void should_throw_if_config_file_contains_only_null_character() - { - _configFileContents = "\0"; - - Assert.Throws(() => Subject.GetValue("key", "value")); - } - - [Test] - public void should_throw_if_config_file_contains_invalid_xml() - { - _configFileContents = "{ \"key\": \"value\" }"; - - Assert.Throws(() => Subject.GetValue("key", "value")); - } + }*/ } } diff --git a/src/NzbDrone.Common.Test/ServiceFactoryFixture.cs b/src/NzbDrone.Common.Test/ServiceFactoryFixture.cs index 06e75831b5..d37d6c6ffa 100644 --- a/src/NzbDrone.Common.Test/ServiceFactoryFixture.cs +++ b/src/NzbDrone.Common.Test/ServiceFactoryFixture.cs @@ -10,7 +10,7 @@ using NzbDrone.Common.Composition.Extensions; using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Common.Instrumentation.Extensions; -using NzbDrone.Core.Datastore; +using NzbDrone.Core.Configuration; using NzbDrone.Core.Datastore.Extensions; using NzbDrone.Core.Lifecycle; using NzbDrone.Core.Messaging.Events; @@ -32,7 +32,7 @@ public void event_handlers_should_be_unique() .AddStartupContext(new StartupContext("first", "second")); container.RegisterInstance(new Mock().Object); - container.RegisterInstance(new Mock>().Object); + container.RegisterInstance(new Mock>().Object); var serviceProvider = container.GetServiceProvider(); diff --git a/src/NzbDrone.Core.Test/Configuration/ConfigServiceFixture.cs b/src/NzbDrone.Core.Test/Configuration/ConfigServiceFixture.cs index 2ed847f4e7..e818aef8f4 100644 --- a/src/NzbDrone.Core.Test/Configuration/ConfigServiceFixture.cs +++ b/src/NzbDrone.Core.Test/Configuration/ConfigServiceFixture.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using FluentAssertions; diff --git a/src/NzbDrone.Core.Test/Framework/DbTest.cs b/src/NzbDrone.Core.Test/Framework/DbTest.cs index fba791b152..c9bd2bc4e5 100644 --- a/src/NzbDrone.Core.Test/Framework/DbTest.cs +++ b/src/NzbDrone.Core.Test/Framework/DbTest.cs @@ -3,9 +3,11 @@ using System.Data.SQLite; using System.IO; using System.Linq; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Options; +using Moq; using Npgsql; using NUnit.Framework; using NzbDrone.Common.Extensions; @@ -124,13 +126,13 @@ private IDatabase CreateDatabase(MigrationContext migrationContext) private void CreatePostgresDb() { - var options = Mocker.Resolve>().Value; + var options = Mocker.Resolve>().CurrentValue; PostgresDatabase.Create(options, MigrationType); } private void DropPostgresDb() { - var options = Mocker.Resolve>().Value; + var options = Mocker.Resolve>().CurrentValue; PostgresDatabase.Drop(options, MigrationType); } @@ -174,12 +176,11 @@ protected void SetupContainer() SetupLogging(); // populate the possible postgres options - var postgresOptions = PostgresDatabase.GetTestOptions(); - _databaseType = postgresOptions.Host.IsNotNullOrWhiteSpace() ? DatabaseType.PostgreSQL : DatabaseType.SQLite; + var options = PostgresDatabase.GetTestOptions(); + _databaseType = options.PostgresHost.IsNotNullOrWhiteSpace() ? DatabaseType.PostgreSQL : DatabaseType.SQLite; // Set up remaining container services - Mocker.SetConstant(Options.Create(postgresOptions)); - Mocker.SetConstant(Mocker.Resolve()); + Mocker.GetMock>().Setup(x => x.CurrentValue).Returns(options); Mocker.SetConstant(Mocker.Resolve()); Mocker.SetConstant(Mocker.Resolve()); diff --git a/src/NzbDrone.Core.Test/HealthCheck/Checks/ReleaseBranchCheckFixture.cs b/src/NzbDrone.Core.Test/HealthCheck/Checks/ReleaseBranchCheckFixture.cs index 5a7899e00a..7392cb91e5 100644 --- a/src/NzbDrone.Core.Test/HealthCheck/Checks/ReleaseBranchCheckFixture.cs +++ b/src/NzbDrone.Core.Test/HealthCheck/Checks/ReleaseBranchCheckFixture.cs @@ -1,9 +1,11 @@ +using Microsoft.Extensions.Options; using Moq; using NUnit.Framework; using NzbDrone.Core.Configuration; using NzbDrone.Core.HealthCheck.Checks; using NzbDrone.Core.Localization; using NzbDrone.Core.Test.Framework; +using NzbDrone.Core.Update; namespace NzbDrone.Core.Test.HealthCheck.Checks { @@ -20,9 +22,9 @@ public void Setup() private void GivenValidBranch(string branch) { - Mocker.GetMock() - .SetupGet(s => s.Branch) - .Returns(branch); + Mocker.GetMock>() + .Setup(s => s.CurrentValue) + .Returns(new ConfigFileOptions { Branch = branch }); } [TestCase("aphrodite")] diff --git a/src/NzbDrone.Core.Test/HealthCheck/Checks/UpdateCheckFixture.cs b/src/NzbDrone.Core.Test/HealthCheck/Checks/UpdateCheckFixture.cs index 64eeb91691..555a453933 100644 --- a/src/NzbDrone.Core.Test/HealthCheck/Checks/UpdateCheckFixture.cs +++ b/src/NzbDrone.Core.Test/HealthCheck/Checks/UpdateCheckFixture.cs @@ -1,3 +1,4 @@ +using Microsoft.Extensions.Options; using Moq; using NUnit.Framework; using NzbDrone.Common.Disk; @@ -19,6 +20,10 @@ public void Setup() Mocker.GetMock() .Setup(s => s.GetLocalizedString(It.IsAny())) .Returns("Some Warning Message"); + + Mocker.GetMock>() + .Setup(c => c.CurrentValue) + .Returns(new ConfigFileOptions()); } [Test] @@ -44,9 +49,9 @@ public void should_return_error_when_app_folder_is_write_protected_and_update_au const string startupFolder = @"/opt/nzbdrone"; - Mocker.GetMock() - .Setup(s => s.UpdateAutomatically) - .Returns(true); + Mocker.GetMock>() + .Setup(s => s.CurrentValue) + .Returns(new ConfigFileOptions { UpdateAutomatically = true }); Mocker.GetMock() .Setup(s => s.StartUpFolder) @@ -67,9 +72,9 @@ public void should_return_error_when_ui_folder_is_write_protected_and_update_aut const string startupFolder = @"/opt/nzbdrone"; const string uiFolder = @"/opt/nzbdrone/UI"; - Mocker.GetMock() - .Setup(s => s.UpdateAutomatically) - .Returns(true); + Mocker.GetMock>() + .Setup(s => s.CurrentValue) + .Returns(new ConfigFileOptions { UpdateAutomatically = true }); Mocker.GetMock() .Setup(s => s.StartUpFolder) @@ -91,13 +96,9 @@ public void should_not_return_error_when_app_folder_is_write_protected_and_exter { PosixOnly(); - Mocker.GetMock() - .Setup(s => s.UpdateAutomatically) - .Returns(true); - - Mocker.GetMock() - .Setup(s => s.UpdateMechanism) - .Returns(UpdateMechanism.Script); + Mocker.GetMock>() + .Setup(s => s.CurrentValue) + .Returns(new ConfigFileOptions { UpdateAutomatically = true, UpdateMechanism = UpdateMechanism.Script }); Mocker.GetMock() .Setup(s => s.StartUpFolder) diff --git a/src/NzbDrone.Core.Test/MediaCoverTests/MediaCoverServiceFixture.cs b/src/NzbDrone.Core.Test/MediaCoverTests/MediaCoverServiceFixture.cs index ba11c4355d..8c1f5d51b4 100644 --- a/src/NzbDrone.Core.Test/MediaCoverTests/MediaCoverServiceFixture.cs +++ b/src/NzbDrone.Core.Test/MediaCoverTests/MediaCoverServiceFixture.cs @@ -4,10 +4,12 @@ using System.Linq; using FizzWare.NBuilder; using FluentAssertions; +using Microsoft.Extensions.Options; using Moq; using NUnit.Framework; using NzbDrone.Common.Disk; using NzbDrone.Common.EnvironmentInfo; +using NzbDrone.Core.Configuration; using NzbDrone.Core.MediaCover; using NzbDrone.Core.Movies; using NzbDrone.Core.Movies.Events; @@ -31,6 +33,10 @@ public void Setup() .Build(); Mocker.GetMock().Setup(m => m.GetMovie(It.Is(id => id == _movie.Id))).Returns(_movie); + + Mocker.GetMock>() + .Setup(x => x.CurrentValue) + .Returns(new ConfigFileOptions()); } [Test] diff --git a/src/NzbDrone.Core.Test/UpdateTests/UpdateServiceFixture.cs b/src/NzbDrone.Core.Test/UpdateTests/UpdateServiceFixture.cs index 969bd9f950..c2b665f786 100644 --- a/src/NzbDrone.Core.Test/UpdateTests/UpdateServiceFixture.cs +++ b/src/NzbDrone.Core.Test/UpdateTests/UpdateServiceFixture.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.IO; using FluentAssertions; +using Microsoft.Extensions.Options; using Moq; using NUnit.Framework; using NzbDrone.Common; @@ -60,9 +61,9 @@ public void Setup() Mocker.GetMock().Setup(c => c.GetCurrentProcess()).Returns(new ProcessInfo { Id = 12 }); Mocker.GetMock().Setup(c => c.ExecutingApplication).Returns(@"C:\Test\Radarr.exe"); - Mocker.GetMock() - .SetupGet(s => s.UpdateAutomatically) - .Returns(true); + Mocker.GetMock>() + .Setup(s => s.CurrentValue) + .Returns(new ConfigFileOptions { UpdateAutomatically = true }); Mocker.GetMock() .Setup(c => c.FolderWritable(It.IsAny())) @@ -77,13 +78,9 @@ public void Setup() private void GivenInstallScript(string path) { - Mocker.GetMock() - .SetupGet(s => s.UpdateMechanism) - .Returns(UpdateMechanism.Script); - - Mocker.GetMock() - .SetupGet(s => s.UpdateScriptPath) - .Returns(path); + Mocker.GetMock>() + .Setup(s => s.CurrentValue) + .Returns(new ConfigFileOptions { UpdateMechanism = UpdateMechanism.Script, UpdateScriptPath = path }); Mocker.GetMock() .Setup(s => s.FileExists(path, StringComparison.Ordinal)) @@ -334,7 +331,7 @@ public void should_switch_to_branch_specified_in_updatepackage() Subject.Execute(new ApplicationUpdateCommand()); - Mocker.GetMock() + Mocker.GetMock() .Verify(v => v.SaveConfigDictionary(It.Is>(d => d.ContainsKey("Branch") && (string)d["Branch"] == "fake")), Times.Once()); } diff --git a/src/NzbDrone.Core/Analytics/AnalyticsService.cs b/src/NzbDrone.Core/Analytics/AnalyticsService.cs index 2727431bea..99eccf35ac 100644 --- a/src/NzbDrone.Core/Analytics/AnalyticsService.cs +++ b/src/NzbDrone.Core/Analytics/AnalyticsService.cs @@ -1,5 +1,6 @@ -using System; +using System; using System.Linq; +using Microsoft.Extensions.Options; using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Core.Configuration; using NzbDrone.Core.Datastore; @@ -15,16 +16,16 @@ public interface IAnalyticsService public class AnalyticsService : IAnalyticsService { - private readonly IConfigFileProvider _configFileProvider; + private readonly IOptionsMonitor _configFileProvider; private readonly IHistoryService _historyService; - public AnalyticsService(IHistoryService historyService, IConfigFileProvider configFileProvider) + public AnalyticsService(IHistoryService historyService, IOptionsMonitor configFileProvider) { _configFileProvider = configFileProvider; _historyService = historyService; } - public bool IsEnabled => (_configFileProvider.AnalyticsEnabled && RuntimeInfo.IsProduction) || RuntimeInfo.IsDevelopment; + public bool IsEnabled => (_configFileProvider.CurrentValue.AnalyticsEnabled && RuntimeInfo.IsProduction) || RuntimeInfo.IsDevelopment; public bool InstallIsActive { diff --git a/src/NzbDrone.Core/Authentication/UserService.cs b/src/NzbDrone.Core/Authentication/UserService.cs index 00a7018deb..17c984792f 100644 --- a/src/NzbDrone.Core/Authentication/UserService.cs +++ b/src/NzbDrone.Core/Authentication/UserService.cs @@ -1,5 +1,6 @@ using System; using System.Linq; +using Microsoft.Extensions.Options; using NzbDrone.Common.Disk; using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Common.Extensions; @@ -19,17 +20,17 @@ public interface IUserService User FindUser(Guid identifier); } - public class UserService : IUserService, IHandle + public class UserService : IUserService { private readonly IUserRepository _repo; private readonly IAppFolderInfo _appFolderInfo; private readonly IDiskProvider _diskProvider; - private readonly IConfigFileProvider _configFileProvider; + private readonly IOptionsMonitor _configFileProvider; public UserService(IUserRepository repo, IAppFolderInfo appFolderInfo, IDiskProvider diskProvider, - IConfigFileProvider configFileProvider) + IOptionsMonitor configFileProvider) { _repo = repo; _appFolderInfo = appFolderInfo; @@ -102,28 +103,5 @@ public User FindUser(Guid identifier) { return _repo.FindUser(identifier); } - - public void Handle(ApplicationStartedEvent message) - { - if (_repo.All().Any()) - { - return; - } - - var xDoc = _configFileProvider.LoadConfigFile(); - var config = xDoc.Descendants("Config").Single(); - var usernameElement = config.Descendants("Username").FirstOrDefault(); - var passwordElement = config.Descendants("Password").FirstOrDefault(); - - if (usernameElement == null || passwordElement == null) - { - return; - } - - var username = usernameElement.Value; - var password = passwordElement.Value; - - Add(username, password); - } } } diff --git a/src/NzbDrone.Core/Configuration/ConfigFileOptions.cs b/src/NzbDrone.Core/Configuration/ConfigFileOptions.cs new file mode 100644 index 0000000000..cdd4f5c032 --- /dev/null +++ b/src/NzbDrone.Core/Configuration/ConfigFileOptions.cs @@ -0,0 +1,71 @@ +using System; +using Microsoft.Extensions.Configuration; +using NzbDrone.Common.EnvironmentInfo; +using NzbDrone.Core.Authentication; +using NzbDrone.Core.Update; + +namespace NzbDrone.Core.Configuration +{ + public class ConfigFileOptions + { + [Persist] + public string BindAddress { get; set; } = "*"; + [Persist] + public int Port { get; set; } = 7878; + [Persist] + public int SslPort { get; set; } = 9898; + [Persist] + public bool EnableSsl { get; set; } + [Persist] + public bool LaunchBrowser { get; set; } = true; + public AuthenticationType AuthenticationMethod { get; set; } + public bool AnalyticsEnabled { get; set; } = true; + [Persist] + public string Branch { get; set; } = "master"; + [Persist] + public string LogLevel { get; set; } = "info"; + public string ConsoleLogLevel { get; set; } = string.Empty; + public bool LogSql { get; set; } + public int LogRotate { get; set; } = 50; + public bool FilterSentryEvents { get; set; } = true; + [Persist] + public string ApiKey { get; set; } = GenerateApiKey(); + [Persist] + public string SslCertPath { get; set; } + [Persist] + public string SslCertPassword { get; set; } + [Persist] + public string UrlBase { get; set; } = string.Empty; + [Persist] + public string InstanceName { get; set; } = BuildInfo.AppName; + public bool UpdateAutomatically { get; set; } + public UpdateMechanism UpdateMechanism { get; set; } = UpdateMechanism.BuiltIn; + public string UpdateScriptPath { get; set; } = string.Empty; + public string SyslogServer { get; set; } = string.Empty; + public int SyslogPort { get; set; } = 514; + public string SyslogLevel { get; set; } = "info"; + public string PostgresHost { get; set; } + public int PostgresPort { get; set; } + public string PostgresUser { get; set; } + public string PostgresPassword { get; set; } + public string PostgresMainDb { get; set; } = BuildInfo.AppName.ToLower() + "-main"; + public string PostgresLogDb { get; set; } = BuildInfo.AppName.ToLower() + "-log"; + + private static string GenerateApiKey() + { + return Guid.NewGuid().ToString().Replace("-", ""); + } + + public static ConfigFileOptions GetOptions() + { + var config = new ConfigurationBuilder() + .AddEnvironmentVariables($"{BuildInfo.AppName}:") + .Build(); + + var options = new ConfigFileOptions(); + config.Bind(options); + + return options; + } + } +} diff --git a/src/NzbDrone.Core/Configuration/ConfigFileProvider.cs b/src/NzbDrone.Core/Configuration/ConfigFileProvider.cs deleted file mode 100644 index 2d4de359ce..0000000000 --- a/src/NzbDrone.Core/Configuration/ConfigFileProvider.cs +++ /dev/null @@ -1,401 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text.RegularExpressions; -using System.Xml; -using System.Xml.Linq; -using Microsoft.Extensions.Options; -using NzbDrone.Common.Cache; -using NzbDrone.Common.Disk; -using NzbDrone.Common.EnvironmentInfo; -using NzbDrone.Common.Extensions; -using NzbDrone.Core.Authentication; -using NzbDrone.Core.Configuration.Events; -using NzbDrone.Core.Datastore; -using NzbDrone.Core.Lifecycle; -using NzbDrone.Core.Messaging.Commands; -using NzbDrone.Core.Messaging.Events; -using NzbDrone.Core.Update; - -namespace NzbDrone.Core.Configuration -{ - public interface IConfigFileProvider : IHandleAsync, - IExecute - { - XDocument LoadConfigFile(); - Dictionary GetConfigDictionary(); - void SaveConfigDictionary(Dictionary configValues); - - string BindAddress { get; } - int Port { get; } - int SslPort { get; } - bool EnableSsl { get; } - bool LaunchBrowser { get; } - AuthenticationType AuthenticationMethod { get; } - bool AnalyticsEnabled { get; } - string LogLevel { get; } - string ConsoleLogLevel { get; } - bool LogSql { get; } - int LogRotate { get; } - bool FilterSentryEvents { get; } - string Branch { get; } - string ApiKey { get; } - string SslCertPath { get; } - string SslCertPassword { get; } - string UrlBase { get; } - string UiFolder { get; } - string InstanceName { get; } - bool UpdateAutomatically { get; } - UpdateMechanism UpdateMechanism { get; } - string UpdateScriptPath { get; } - string SyslogServer { get; } - int SyslogPort { get; } - string SyslogLevel { get; } - string PostgresHost { get; } - int PostgresPort { get; } - string PostgresUser { get; } - string PostgresPassword { get; } - string PostgresMainDb { get; } - string PostgresLogDb { get; } - } - - public class ConfigFileProvider : IConfigFileProvider - { - public const string CONFIG_ELEMENT_NAME = "Config"; - - private readonly IEventAggregator _eventAggregator; - private readonly IDiskProvider _diskProvider; - private readonly ICached _cache; - private readonly PostgresOptions _postgresOptions; - - private readonly string _configFile; - private static readonly Regex HiddenCharacterRegex = new Regex("[^a-z0-9]", RegexOptions.Compiled | RegexOptions.IgnoreCase); - - private static readonly object Mutex = new object(); - - public ConfigFileProvider(IAppFolderInfo appFolderInfo, - ICacheManager cacheManager, - IEventAggregator eventAggregator, - IDiskProvider diskProvider, - IOptions postgresOptions) - { - _cache = cacheManager.GetCache(GetType()); - _eventAggregator = eventAggregator; - _diskProvider = diskProvider; - _configFile = appFolderInfo.GetConfigPath(); - _postgresOptions = postgresOptions.Value; - } - - public Dictionary GetConfigDictionary() - { - var dict = new Dictionary(StringComparer.InvariantCultureIgnoreCase); - - var type = GetType(); - var properties = type.GetProperties(); - - foreach (var propertyInfo in properties) - { - var value = propertyInfo.GetValue(this, null); - - dict.Add(propertyInfo.Name, value); - } - - return dict; - } - - public void SaveConfigDictionary(Dictionary configValues) - { - _cache.Clear(); - - var allWithDefaults = GetConfigDictionary(); - - foreach (var configValue in configValues) - { - if (configValue.Key.Equals("ApiKey", StringComparison.InvariantCultureIgnoreCase)) - { - continue; - } - - object currentValue; - allWithDefaults.TryGetValue(configValue.Key, out currentValue); - if (currentValue == null) - { - continue; - } - - var equal = configValue.Value.ToString().Equals(currentValue.ToString()); - - if (!equal) - { - SetValue(configValue.Key.FirstCharToUpper(), configValue.Value.ToString()); - } - } - - _eventAggregator.PublishEvent(new ConfigFileSavedEvent()); - } - - public string BindAddress - { - get - { - const string defaultValue = "*"; - - string bindAddress = GetValue("BindAddress", defaultValue); - if (string.IsNullOrWhiteSpace(bindAddress)) - { - return defaultValue; - } - - return bindAddress; - } - } - - public int Port => GetValueInt("Port", 7878); - - public int SslPort => GetValueInt("SslPort", 9898); - - public bool EnableSsl => GetValueBoolean("EnableSsl", false); - - public bool LaunchBrowser => GetValueBoolean("LaunchBrowser", true); - - public string ApiKey - { - get - { - var apiKey = GetValue("ApiKey", GenerateApiKey()); - - if (apiKey.IsNullOrWhiteSpace()) - { - apiKey = GenerateApiKey(); - SetValue("ApiKey", apiKey); - } - - return apiKey; - } - } - - public AuthenticationType AuthenticationMethod - { - get - { - var enabled = GetValueBoolean("AuthenticationEnabled", false, false); - - if (enabled) - { - SetValue("AuthenticationMethod", AuthenticationType.Basic); - return AuthenticationType.Basic; - } - - return GetValueEnum("AuthenticationMethod", AuthenticationType.None); - } - } - - public bool AnalyticsEnabled => GetValueBoolean("AnalyticsEnabled", true, persist: false); - - public string Branch => GetValue("Branch", "master").ToLowerInvariant(); - - 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", "radarr-main", persist: false); - public string PostgresLogDb => _postgresOptions?.LogDb ?? GetValue("PostgresLogDb", "radarr-log", persist: false); - public int PostgresPort => (_postgresOptions?.Port ?? 0) != 0 ? _postgresOptions.Port : GetValueInt("PostgresPort", 5432, 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 string UrlBase - { - get - { - var urlBase = GetValue("UrlBase", "").Trim('/'); - - if (urlBase.IsNullOrWhiteSpace()) - { - return urlBase; - } - - return "/" + urlBase.Trim('/').ToLower(); - } - } - - public string UiFolder => BuildInfo.IsDebug ? Path.Combine("..", "UI") : "UI"; - public string InstanceName => GetValue("InstanceName", BuildInfo.AppName); - - public bool UpdateAutomatically => GetValueBoolean("UpdateAutomatically", false, false); - - public UpdateMechanism UpdateMechanism => GetValueEnum("UpdateMechanism", UpdateMechanism.BuiltIn, false); - - public string UpdateScriptPath => GetValue("UpdateScriptPath", "", false); - - public string SyslogServer => GetValue("SyslogServer", "", persist: false); - - public int SyslogPort => GetValueInt("SyslogPort", 514, persist: false); - - public string SyslogLevel => GetValue("SyslogLevel", LogLevel, false).ToLowerInvariant(); - - public int GetValueInt(string key, int defaultValue, bool persist = true) - { - return Convert.ToInt32(GetValue(key, defaultValue, persist)); - } - - public bool GetValueBoolean(string key, bool defaultValue, bool persist = true) - { - return Convert.ToBoolean(GetValue(key, defaultValue, persist)); - } - - public T GetValueEnum(string key, T defaultValue, bool persist = true) - { - return (T)Enum.Parse(typeof(T), GetValue(key, defaultValue), persist); - } - - public string GetValue(string key, object defaultValue, bool persist = true) - { - return _cache.Get(key, () => - { - var xDoc = LoadConfigFile(); - var config = xDoc.Descendants(CONFIG_ELEMENT_NAME).Single(); - - var parentContainer = config; - - var valueHolder = parentContainer.Descendants(key).ToList(); - - if (valueHolder.Count == 1) - { - return valueHolder.First().Value.Trim(); - } - - //Save the value - if (persist) - { - SetValue(key, defaultValue); - } - - //return the default value - return defaultValue.ToString(); - }); - } - - public void SetValue(string key, object value) - { - var valueString = value.ToString().Trim(); - var xDoc = LoadConfigFile(); - var config = xDoc.Descendants(CONFIG_ELEMENT_NAME).Single(); - - var parentContainer = config; - - var keyHolder = parentContainer.Descendants(key); - - if (keyHolder.Count() != 1) - { - parentContainer.Add(new XElement(key, valueString)); - } - else - { - parentContainer.Descendants(key).Single().Value = valueString; - } - - _cache.Set(key, valueString); - - SaveConfigFile(xDoc); - } - - public void SetValue(string key, Enum value) - { - SetValue(key, value.ToString().ToLower()); - } - - private void EnsureDefaultConfigFile() - { - if (!File.Exists(_configFile)) - { - SaveConfigDictionary(GetConfigDictionary()); - } - } - - private void DeleteOldValues() - { - var xDoc = LoadConfigFile(); - var config = xDoc.Descendants(CONFIG_ELEMENT_NAME).Single(); - - var type = GetType(); - var properties = type.GetProperties(); - - foreach (var configValue in config.Descendants().ToList()) - { - var name = configValue.Name.LocalName; - - if (!properties.Any(p => p.Name == name)) - { - config.Descendants(name).Remove(); - } - } - - SaveConfigFile(xDoc); - } - - public XDocument LoadConfigFile() - { - try - { - lock (Mutex) - { - if (_diskProvider.FileExists(_configFile)) - { - var contents = _diskProvider.ReadAllText(_configFile); - - if (contents.IsNullOrWhiteSpace()) - { - throw new InvalidConfigFileException($"{_configFile} is empty. Please delete the config file and Radarr will recreate it."); - } - - if (contents.All(char.IsControl)) - { - throw new InvalidConfigFileException($"{_configFile} is corrupt. Please delete the config file and Radarr will recreate it."); - } - - return XDocument.Parse(_diskProvider.ReadAllText(_configFile)); - } - - var xDoc = new XDocument(new XDeclaration("1.0", "utf-8", "yes")); - xDoc.Add(new XElement(CONFIG_ELEMENT_NAME)); - - return xDoc; - } - } - catch (XmlException ex) - { - throw new InvalidConfigFileException($"{_configFile} is corrupt is invalid. Please delete the config file and Radarr will recreate it.", ex); - } - } - - private void SaveConfigFile(XDocument xDoc) - { - lock (Mutex) - { - _diskProvider.WriteAllText(_configFile, xDoc.ToString()); - } - } - - private string GenerateApiKey() - { - return Guid.NewGuid().ToString().Replace("-", ""); - } - - public void HandleAsync(ApplicationStartedEvent message) - { - EnsureDefaultConfigFile(); - DeleteOldValues(); - } - - public void Execute(ResetApiKeyCommand message) - { - SetValue("ApiKey", GenerateApiKey()); - } - } -} diff --git a/src/NzbDrone.Core/Configuration/ConfigFileWriter.cs b/src/NzbDrone.Core/Configuration/ConfigFileWriter.cs new file mode 100644 index 0000000000..6dd3249336 --- /dev/null +++ b/src/NzbDrone.Core/Configuration/ConfigFileWriter.cs @@ -0,0 +1,219 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Xml; +using System.Xml.Linq; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Options; +using NLog; +using NzbDrone.Common.Disk; +using NzbDrone.Common.EnvironmentInfo; +using NzbDrone.Common.Extensions; +using NzbDrone.Core.Configuration.Events; +using NzbDrone.Core.Lifecycle; +using NzbDrone.Core.Messaging.Commands; +using NzbDrone.Core.Messaging.Events; + +namespace NzbDrone.Core.Configuration +{ + public interface IConfigFileWriter : IHandleAsync, + IExecute + { + public void EnsureDefaultConfigFile(); + void SaveConfigDictionary(Dictionary configValues); + } + + public class ConfigFileWriter : IConfigFileWriter + { + public static string CONFIG_ELEMENT_NAME = BuildInfo.AppName; + + private readonly IEventAggregator _eventAggregator; + private readonly IDiskProvider _diskProvider; + private readonly IConfigurationRoot _configuration; + private readonly IOptionsMonitor _configFileOptions; + private readonly Logger _logger; + + private readonly string _configFile; + + private static readonly object Mutex = new object(); + + public ConfigFileWriter(IAppFolderInfo appFolderInfo, + IEventAggregator eventAggregator, + IDiskProvider diskProvider, + IConfiguration configuration, + IOptionsMonitor configFileOptions, + Logger logger) + { + _eventAggregator = eventAggregator; + _diskProvider = diskProvider; + _configuration = configuration as IConfigurationRoot; + _configFileOptions = configFileOptions; + _logger = logger; + + _configFile = appFolderInfo.GetConfigPath(); + + _configFileOptions.OnChange(OnChange); + } + + private Dictionary GetConfigDictionary() + { + var dict = new Dictionary(StringComparer.InvariantCultureIgnoreCase); + + var properties = typeof(ConfigFileOptions).GetProperties(); + + foreach (var propertyInfo in properties) + { + var value = propertyInfo.GetValue(_configFileOptions.CurrentValue, null); + + dict.Add(propertyInfo.Name, value); + } + + return dict; + } + + public void SaveConfigDictionary(Dictionary configValues) + { + var allWithDefaults = GetConfigDictionary(); + + var persistKeys = typeof(ConfigFileOptions).GetProperties() + .Where(x => Attribute.IsDefined(x, typeof(PersistAttribute))) + .Select(x => x.Name) + .ToList(); + + foreach (var configValue in configValues) + { + if (configValue.Key.Equals("ApiKey", StringComparison.InvariantCultureIgnoreCase)) + { + continue; + } + + allWithDefaults.TryGetValue(configValue.Key, out var currentValue); + if (currentValue == null) + { + continue; + } + + var equal = configValue.Value.ToString().Equals(currentValue.ToString()); + var persist = persistKeys.Contains(configValue.Key); + + if (persist || !equal) + { + SetValue(configValue.Key.FirstCharToUpper(), configValue.Value.ToString()); + } + } + + _eventAggregator.PublishEvent(new ConfigFileSavedEvent()); + } + + public void SetValue(string key, object value) + { + var valueString = value.ToString().Trim(); + var xDoc = LoadConfigFile(); + var config = xDoc.Descendants(CONFIG_ELEMENT_NAME).Single(); + + var keyHolder = config.Descendants(key); + + if (keyHolder.Count() != 1) + { + config.Add(new XElement(key, valueString)); + } + else + { + config.Descendants(key).Single().Value = valueString; + } + + SaveConfigFile(xDoc); + } + + public void EnsureDefaultConfigFile() + { + if (!File.Exists(_configFile)) + { + SaveConfigDictionary(GetConfigDictionary()); + SetValue(nameof(ConfigFileOptions.ApiKey), _configFileOptions.CurrentValue.ApiKey); + } + } + + private void DeleteOldValues() + { + var xDoc = LoadConfigFile(); + var config = xDoc.Descendants(CONFIG_ELEMENT_NAME).Single(); + + var properties = typeof(ConfigFileOptions).GetProperties(); + + foreach (var configValue in config.Descendants().ToList()) + { + var name = configValue.Name.LocalName; + + if (!properties.Any(p => p.Name == name)) + { + config.Descendants(name).Remove(); + } + } + + SaveConfigFile(xDoc); + } + + public XDocument LoadConfigFile() + { + try + { + lock (Mutex) + { + if (_diskProvider.FileExists(_configFile)) + { + var contents = _diskProvider.ReadAllText(_configFile); + + if (contents.IsNullOrWhiteSpace()) + { + throw new InvalidConfigFileException($"{_configFile} is empty. Please delete the config file and Radarr will recreate it."); + } + + if (contents.All(char.IsControl)) + { + throw new InvalidConfigFileException($"{_configFile} is corrupt. Please delete the config file and Radarr will recreate it."); + } + + return XDocument.Parse(_diskProvider.ReadAllText(_configFile)); + } + + var xDoc = new XDocument(new XDeclaration("1.0", "utf-8", "yes")); + xDoc.Add(new XElement(CONFIG_ELEMENT_NAME)); + + return xDoc; + } + } + catch (XmlException ex) + { + throw new InvalidConfigFileException($"{_configFile} is corrupt is invalid. Please delete the config file and Radarr will recreate it.", ex); + } + } + + private void SaveConfigFile(XDocument xDoc) + { + lock (Mutex) + { + _diskProvider.WriteAllText(_configFile, xDoc.ToString()); + _configuration.Reload(); + } + } + + public void HandleAsync(ApplicationStartedEvent message) + { + DeleteOldValues(); + } + + public void Execute(ResetApiKeyCommand message) + { + SetValue(nameof(ConfigFileOptions.ApiKey), new ConfigFileOptions().ApiKey); + } + + private void OnChange(ConfigFileOptions options) + { + _logger.Info("Config file updated"); + + _eventAggregator.PublishEvent(new ConfigFileSavedEvent()); + } + } +} diff --git a/src/NzbDrone.Core/Configuration/PersistAttribute.cs b/src/NzbDrone.Core/Configuration/PersistAttribute.cs new file mode 100644 index 0000000000..43d659cb1b --- /dev/null +++ b/src/NzbDrone.Core/Configuration/PersistAttribute.cs @@ -0,0 +1,9 @@ +using System; + +namespace NzbDrone.Core.Configuration +{ + [AttributeUsage(AttributeTargets.Property)] + public class PersistAttribute : Attribute + { + } +} diff --git a/src/NzbDrone.Core/Datastore/ConnectionStringFactory.cs b/src/NzbDrone.Core/Datastore/ConnectionStringFactory.cs index 961d060f89..201724ad05 100644 --- a/src/NzbDrone.Core/Datastore/ConnectionStringFactory.cs +++ b/src/NzbDrone.Core/Datastore/ConnectionStringFactory.cs @@ -1,5 +1,6 @@ using System; using System.Data.SQLite; +using Microsoft.Extensions.Options; using Npgsql; using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Common.Extensions; @@ -16,16 +17,16 @@ public interface IConnectionStringFactory public class ConnectionStringFactory : IConnectionStringFactory { - private readonly IConfigFileProvider _configFileProvider; + private readonly IOptionsMonitor _configFileProvider; - public ConnectionStringFactory(IAppFolderInfo appFolderInfo, IConfigFileProvider configFileProvider) + public ConnectionStringFactory(IAppFolderInfo appFolderInfo, IOptionsMonitor configFileProvider) { _configFileProvider = configFileProvider; - MainDbConnectionString = _configFileProvider.PostgresHost.IsNotNullOrWhiteSpace() ? GetPostgresConnectionString(_configFileProvider.PostgresMainDb) : + MainDbConnectionString = _configFileProvider.CurrentValue.PostgresHost.IsNotNullOrWhiteSpace() ? GetPostgresConnectionString(_configFileProvider.CurrentValue.PostgresMainDb) : GetConnectionString(appFolderInfo.GetDatabase()); - LogDbConnectionString = _configFileProvider.PostgresHost.IsNotNullOrWhiteSpace() ? GetPostgresConnectionString(_configFileProvider.PostgresLogDb) : + LogDbConnectionString = _configFileProvider.CurrentValue.PostgresHost.IsNotNullOrWhiteSpace() ? GetPostgresConnectionString(_configFileProvider.CurrentValue.PostgresLogDb) : GetConnectionString(appFolderInfo.GetLogDatabase()); } @@ -63,10 +64,10 @@ private string GetPostgresConnectionString(string dbName) var connectionBuilder = new NpgsqlConnectionStringBuilder(); connectionBuilder.Database = dbName; - connectionBuilder.Host = _configFileProvider.PostgresHost; - connectionBuilder.Username = _configFileProvider.PostgresUser; - connectionBuilder.Password = _configFileProvider.PostgresPassword; - connectionBuilder.Port = _configFileProvider.PostgresPort; + connectionBuilder.Host = _configFileProvider.CurrentValue.PostgresHost; + connectionBuilder.Username = _configFileProvider.CurrentValue.PostgresUser; + connectionBuilder.Password = _configFileProvider.CurrentValue.PostgresPassword; + connectionBuilder.Port = _configFileProvider.CurrentValue.PostgresPort; connectionBuilder.Enlist = false; return connectionBuilder.ConnectionString; diff --git a/src/NzbDrone.Core/Datastore/PostgresOptions.cs b/src/NzbDrone.Core/Datastore/PostgresOptions.cs index 0c65a4b392..f2f79a0de2 100644 --- a/src/NzbDrone.Core/Datastore/PostgresOptions.cs +++ b/src/NzbDrone.Core/Datastore/PostgresOptions.cs @@ -1,4 +1,5 @@ using Microsoft.Extensions.Configuration; +using NzbDrone.Common.EnvironmentInfo; namespace NzbDrone.Core.Datastore { @@ -18,7 +19,7 @@ public static PostgresOptions GetOptions() .Build(); var postgresOptions = new PostgresOptions(); - config.GetSection("Radarr:Postgres").Bind(postgresOptions); + config.GetSection($"{BuildInfo.AppName}:Postgres").Bind(postgresOptions); return postgresOptions; } diff --git a/src/NzbDrone.Core/HealthCheck/Checks/ReleaseBranchCheck.cs b/src/NzbDrone.Core/HealthCheck/Checks/ReleaseBranchCheck.cs index 6126471a55..5deacb5e43 100644 --- a/src/NzbDrone.Core/HealthCheck/Checks/ReleaseBranchCheck.cs +++ b/src/NzbDrone.Core/HealthCheck/Checks/ReleaseBranchCheck.cs @@ -1,5 +1,6 @@ using System; using System.Linq; +using Microsoft.Extensions.Options; using NzbDrone.Core.Configuration; using NzbDrone.Core.Configuration.Events; using NzbDrone.Core.Localization; @@ -9,9 +10,9 @@ namespace NzbDrone.Core.HealthCheck.Checks [CheckOn(typeof(ConfigSavedEvent))] public class ReleaseBranchCheck : HealthCheckBase { - private readonly IConfigFileProvider _configFileService; + private readonly IOptionsMonitor _configFileService; - public ReleaseBranchCheck(IConfigFileProvider configFileService, ILocalizationService localizationService) + public ReleaseBranchCheck(IOptionsMonitor configFileService, ILocalizationService localizationService) : base(localizationService) { _configFileService = configFileService; @@ -19,11 +20,11 @@ public ReleaseBranchCheck(IConfigFileProvider configFileService, ILocalizationSe public override HealthCheck Check() { - var currentBranch = _configFileService.Branch.ToLower(); + var currentBranch = _configFileService.CurrentValue.Branch.ToLower(); if (!Enum.GetNames(typeof(ReleaseBranches)).Any(x => x.ToLower() == currentBranch)) { - return new HealthCheck(GetType(), HealthCheckResult.Warning, string.Format(_localizationService.GetLocalizedString("ReleaseBranchCheckOfficialBranchMessage"), _configFileService.Branch), "#branch-is-not-a-valid-release-branch"); + return new HealthCheck(GetType(), HealthCheckResult.Warning, string.Format(_localizationService.GetLocalizedString("ReleaseBranchCheckOfficialBranchMessage"), _configFileService.CurrentValue.Branch), "#branch-is-not-a-valid-release-branch"); } return new HealthCheck(GetType()); diff --git a/src/NzbDrone.Core/HealthCheck/Checks/UpdateCheck.cs b/src/NzbDrone.Core/HealthCheck/Checks/UpdateCheck.cs index f92e29b803..518f2f6864 100644 --- a/src/NzbDrone.Core/HealthCheck/Checks/UpdateCheck.cs +++ b/src/NzbDrone.Core/HealthCheck/Checks/UpdateCheck.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using Microsoft.Extensions.Options; using NzbDrone.Common.Disk; using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Common.Extensions; @@ -16,13 +17,13 @@ public class UpdateCheck : HealthCheckBase private readonly IDiskProvider _diskProvider; private readonly IAppFolderInfo _appFolderInfo; private readonly ICheckUpdateService _checkUpdateService; - private readonly IConfigFileProvider _configFileProvider; + private readonly IOptionsMonitor _configFileProvider; private readonly IOsInfo _osInfo; public UpdateCheck(IDiskProvider diskProvider, IAppFolderInfo appFolderInfo, ICheckUpdateService checkUpdateService, - IConfigFileProvider configFileProvider, + IOptionsMonitor configFileProvider, IOsInfo osInfo, ILocalizationService localizationService) : base(localizationService) @@ -39,8 +40,8 @@ public override HealthCheck Check() var startupFolder = _appFolderInfo.StartUpFolder; var uiFolder = Path.Combine(startupFolder, "UI"); - if ((OsInfo.IsWindows || _configFileProvider.UpdateAutomatically) && - _configFileProvider.UpdateMechanism == UpdateMechanism.BuiltIn && + if ((OsInfo.IsWindows || _configFileProvider.CurrentValue.UpdateAutomatically) && + _configFileProvider.CurrentValue.UpdateMechanism == UpdateMechanism.BuiltIn && !_osInfo.IsDocker) { if (OsInfo.IsOsx && startupFolder.GetAncestorFolders().Contains("AppTranslocation")) diff --git a/src/NzbDrone.Core/HealthCheck/ServerSideNotificationService.cs b/src/NzbDrone.Core/HealthCheck/ServerSideNotificationService.cs index fe14e4b86e..28ae030d89 100644 --- a/src/NzbDrone.Core/HealthCheck/ServerSideNotificationService.cs +++ b/src/NzbDrone.Core/HealthCheck/ServerSideNotificationService.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; +using Microsoft.Extensions.Options; using NLog; using NzbDrone.Common.Cloud; using NzbDrone.Common.EnvironmentInfo; @@ -19,11 +20,11 @@ public interface IServerSideNotificationService public class ServerSideNotificationService : IServerSideNotificationService { private readonly IHttpClient _client; - private readonly IConfigFileProvider _configFileProvider; + private readonly IOptionsMonitor _configFileProvider; private readonly IHttpRequestBuilderFactory _cloudRequestBuilder; private readonly Logger _logger; - public ServerSideNotificationService(IHttpClient client, IConfigFileProvider configFileProvider, IRadarrCloudRequestBuilder cloudRequestBuilder, Logger logger) + public ServerSideNotificationService(IHttpClient client, IOptionsMonitor configFileProvider, IRadarrCloudRequestBuilder cloudRequestBuilder, Logger logger) { _client = client; _configFileProvider = configFileProvider; @@ -39,7 +40,7 @@ public List GetServerChecks() .AddQueryParam("os", OsInfo.Os.ToString().ToLowerInvariant()) .AddQueryParam("arch", RuntimeInformation.OSArchitecture) .AddQueryParam("runtime", PlatformInfo.Platform.ToString().ToLowerInvariant()) - .AddQueryParam("branch", _configFileProvider.Branch) + .AddQueryParam("branch", _configFileProvider.CurrentValue.Branch) .Build(); try { diff --git a/src/NzbDrone.Core/Instrumentation/ReconfigureLogging.cs b/src/NzbDrone.Core/Instrumentation/ReconfigureLogging.cs index b2f80d8d11..db287d9ba5 100644 --- a/src/NzbDrone.Core/Instrumentation/ReconfigureLogging.cs +++ b/src/NzbDrone.Core/Instrumentation/ReconfigureLogging.cs @@ -1,5 +1,6 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; +using Microsoft.Extensions.Options; using NLog; using NLog.Config; using NLog.Targets.Syslog; @@ -17,21 +18,21 @@ namespace NzbDrone.Core.Instrumentation { public class ReconfigureLogging : IHandleAsync { - private readonly IConfigFileProvider _configFileProvider; + private readonly IOptionsMonitor _configFileProvider; - public ReconfigureLogging(IConfigFileProvider configFileProvider) + public ReconfigureLogging(IOptionsMonitor configFileProvider) { _configFileProvider = configFileProvider; } public void Reconfigure() { - var minimumLogLevel = LogLevel.FromString(_configFileProvider.LogLevel); + var minimumLogLevel = LogLevel.FromString(_configFileProvider.CurrentValue.LogLevel); LogLevel minimumConsoleLogLevel; - if (_configFileProvider.ConsoleLogLevel.IsNotNullOrWhiteSpace()) + if (_configFileProvider.CurrentValue.ConsoleLogLevel.IsNotNullOrWhiteSpace()) { - minimumConsoleLogLevel = LogLevel.FromString(_configFileProvider.ConsoleLogLevel); + minimumConsoleLogLevel = LogLevel.FromString(_configFileProvider.CurrentValue.ConsoleLogLevel); } else if (minimumLogLevel > LogLevel.Info) { @@ -42,10 +43,10 @@ public void Reconfigure() minimumConsoleLogLevel = LogLevel.Info; } - if (_configFileProvider.SyslogServer.IsNotNullOrWhiteSpace()) + if (_configFileProvider.CurrentValue.SyslogServer.IsNotNullOrWhiteSpace()) { - var syslogLevel = LogLevel.FromString(_configFileProvider.SyslogLevel); - SetSyslogParameters(_configFileProvider.SyslogServer, _configFileProvider.SyslogPort, syslogLevel); + var syslogLevel = LogLevel.FromString(_configFileProvider.CurrentValue.SyslogLevel); + SetSyslogParameters(_configFileProvider.CurrentValue.SyslogServer, _configFileProvider.CurrentValue.SyslogPort, syslogLevel); } var rules = LogManager.Configuration.LoggingRules; @@ -60,7 +61,7 @@ public void Reconfigure() SetLogRotation(); //Log Sql - SqlBuilderExtensions.LogSql = _configFileProvider.LogSql; + SqlBuilderExtensions.LogSql = _configFileProvider.CurrentValue.LogSql; //Sentry ReconfigureSentry(); @@ -95,7 +96,7 @@ private void SetLogRotation() { foreach (var target in LogManager.Configuration.AllTargets.OfType()) { - target.MaxArchiveFiles = _configFileProvider.LogRotate; + target.MaxArchiveFiles = _configFileProvider.CurrentValue.LogRotate; } } @@ -104,8 +105,8 @@ private void ReconfigureSentry() var sentryTarget = LogManager.Configuration.AllTargets.OfType().FirstOrDefault(); if (sentryTarget != null) { - sentryTarget.SentryEnabled = (RuntimeInfo.IsProduction && _configFileProvider.AnalyticsEnabled) || RuntimeInfo.IsDevelopment; - sentryTarget.FilterEvents = _configFileProvider.FilterSentryEvents; + sentryTarget.SentryEnabled = (RuntimeInfo.IsProduction && _configFileProvider.CurrentValue.AnalyticsEnabled) || RuntimeInfo.IsDevelopment; + sentryTarget.FilterEvents = _configFileProvider.CurrentValue.FilterSentryEvents; } } @@ -119,7 +120,7 @@ private void SetSyslogParameters(string syslogServer, int syslogPort, LogLevel m syslogTarget.MessageSend.Udp.Server = syslogServer; syslogTarget.MessageSend.Udp.ReconnectInterval = 500; syslogTarget.MessageCreation.Rfc = RfcNumber.Rfc5424; - syslogTarget.MessageCreation.Rfc5424.AppName = _configFileProvider.InstanceName; + syslogTarget.MessageCreation.Rfc5424.AppName = _configFileProvider.CurrentValue.InstanceName; var loggingRule = new LoggingRule("*", minimumLogLevel, syslogTarget); diff --git a/src/NzbDrone.Core/Instrumentation/ReconfigureSentry.cs b/src/NzbDrone.Core/Instrumentation/ReconfigureSentry.cs index 2d8f14b202..6df55c949d 100644 --- a/src/NzbDrone.Core/Instrumentation/ReconfigureSentry.cs +++ b/src/NzbDrone.Core/Instrumentation/ReconfigureSentry.cs @@ -1,4 +1,5 @@ -using System.Linq; +using System.Linq; +using Microsoft.Extensions.Options; using NLog; using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Common.Instrumentation.Sentry; @@ -11,11 +12,11 @@ namespace NzbDrone.Core.Instrumentation { public class ReconfigureSentry : IHandleAsync { - private readonly IConfigFileProvider _configFileProvider; + private readonly IOptionsMonitor _configFileProvider; private readonly IPlatformInfo _platformInfo; private readonly IMainDatabase _database; - public ReconfigureSentry(IConfigFileProvider configFileProvider, + public ReconfigureSentry(IOptionsMonitor configFileProvider, IPlatformInfo platformInfo, IMainDatabase database) { @@ -30,7 +31,7 @@ public void Reconfigure() var sentryTarget = LogManager.Configuration.AllTargets.OfType().FirstOrDefault(); if (sentryTarget != null) { - sentryTarget.UpdateScope(_database.Version, _database.Migration, _configFileProvider.Branch, _platformInfo); + sentryTarget.UpdateScope(_database.Version, _database.Migration, _configFileProvider.CurrentValue.Branch, _platformInfo); } } diff --git a/src/NzbDrone.Core/MediaCover/MediaCoverProxy.cs b/src/NzbDrone.Core/MediaCover/MediaCoverProxy.cs index 8f6ed8c9a3..5e847ac0ef 100644 --- a/src/NzbDrone.Core/MediaCover/MediaCoverProxy.cs +++ b/src/NzbDrone.Core/MediaCover/MediaCoverProxy.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.IO; +using Microsoft.Extensions.Options; using NzbDrone.Common.Cache; using NzbDrone.Common.Http; using NzbDrone.Core.Configuration; @@ -18,10 +19,10 @@ public interface IMediaCoverProxy public class MediaCoverProxy : IMediaCoverProxy { private readonly IHttpClient _httpClient; - private readonly IConfigFileProvider _configFileProvider; + private readonly IOptionsMonitor _configFileProvider; private readonly ICached _cache; - public MediaCoverProxy(IHttpClient httpClient, IConfigFileProvider configFileProvider, ICacheManager cacheManager) + public MediaCoverProxy(IHttpClient httpClient, IOptionsMonitor configFileProvider, ICacheManager cacheManager) { _httpClient = httpClient; _configFileProvider = configFileProvider; @@ -37,7 +38,7 @@ public string RegisterUrl(string url) _cache.ClearExpired(); var fileName = Path.GetFileName(url); - return _configFileProvider.UrlBase + @"/MediaCoverProxy/" + hash + "/" + fileName; + return _configFileProvider.CurrentValue.UrlBase + @"/MediaCoverProxy/" + hash + "/" + fileName; } public string GetUrl(string hash) diff --git a/src/NzbDrone.Core/MediaCover/MediaCoverService.cs b/src/NzbDrone.Core/MediaCover/MediaCoverService.cs index eaccc9ae64..b61adeffe8 100644 --- a/src/NzbDrone.Core/MediaCover/MediaCoverService.cs +++ b/src/NzbDrone.Core/MediaCover/MediaCoverService.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Net; using System.Threading; +using Microsoft.Extensions.Options; using NLog; using NzbDrone.Common; using NzbDrone.Common.Disk; @@ -35,7 +36,7 @@ public class MediaCoverService : private readonly IHttpClient _httpClient; private readonly IDiskProvider _diskProvider; private readonly ICoverExistsSpecification _coverExistsSpecification; - private readonly IConfigFileProvider _configFileProvider; + private readonly IOptionsMonitor _configFileProvider; private readonly IEventAggregator _eventAggregator; private readonly Logger _logger; @@ -51,7 +52,7 @@ public MediaCoverService(IMediaCoverProxy mediaCoverProxy, IDiskProvider diskProvider, IAppFolderInfo appFolderInfo, ICoverExistsSpecification coverExistsSpecification, - IConfigFileProvider configFileProvider, + IOptionsMonitor configFileProvider, IEventAggregator eventAggregator, Logger logger) { @@ -104,7 +105,7 @@ public void ConvertToLocalUrls(int movieId, IEnumerable covers, Dict var filePath = GetCoverPath(movieId, mediaCover.CoverType); mediaCover.RemoteUrl = mediaCover.Url; - mediaCover.Url = _configFileProvider.UrlBase + @"/MediaCover/" + movieId + "/" + mediaCover.CoverType.ToString().ToLower() + ".jpg"; + mediaCover.Url = _configFileProvider.CurrentValue.UrlBase + @"/MediaCover/" + movieId + "/" + mediaCover.CoverType.ToString().ToLower() + ".jpg"; FileInfo file; var fileExists = false; diff --git a/src/NzbDrone.Core/Radarr.Core.csproj b/src/NzbDrone.Core/Radarr.Core.csproj index b4c1e9bdc0..3d8569f431 100644 --- a/src/NzbDrone.Core/Radarr.Core.csproj +++ b/src/NzbDrone.Core/Radarr.Core.csproj @@ -13,7 +13,8 @@ - + + diff --git a/src/NzbDrone.Core/Update/ConfigureUpdateMechanism.cs b/src/NzbDrone.Core/Update/ConfigureUpdateMechanism.cs index 79e91bce13..26c6f676ce 100644 --- a/src/NzbDrone.Core/Update/ConfigureUpdateMechanism.cs +++ b/src/NzbDrone.Core/Update/ConfigureUpdateMechanism.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Microsoft.Extensions.Options; using NLog; using NzbDrone.Common.Extensions; using NzbDrone.Core.Configuration; @@ -16,19 +17,21 @@ public interface IUpdaterConfigProvider public class UpdaterConfigProvider : IUpdaterConfigProvider, IHandle { private readonly Logger _logger; - private readonly IConfigFileProvider _configFileProvider; + private readonly IOptionsMonitor _configFileProvider; + private readonly IConfigFileWriter _configFileWriter; private readonly IDeploymentInfoProvider _deploymentInfoProvider; - public UpdaterConfigProvider(IDeploymentInfoProvider deploymentInfoProvider, IConfigFileProvider configFileProvider, Logger logger) + public UpdaterConfigProvider(IDeploymentInfoProvider deploymentInfoProvider, IOptionsMonitor configFileProvider, IConfigFileWriter configFileWriter, Logger logger) { _deploymentInfoProvider = deploymentInfoProvider; _configFileProvider = configFileProvider; + _configFileWriter = configFileWriter; _logger = logger; } public void Handle(ApplicationStartedEvent message) { - var updateMechanism = _configFileProvider.UpdateMechanism; + var updateMechanism = _configFileProvider.CurrentValue.UpdateMechanism; var packageUpdateMechanism = _deploymentInfoProvider.PackageUpdateMechanism; var externalMechanisms = Enum.GetValues(typeof(UpdateMechanism)) @@ -49,7 +52,7 @@ public void Handle(ApplicationStartedEvent message) if (_deploymentInfoProvider.IsExternalUpdateMechanism) { - var currentBranch = _configFileProvider.Branch; + var currentBranch = _configFileProvider.CurrentValue.Branch; var packageBranch = _deploymentInfoProvider.PackageBranch; if (packageBranch.IsNotNullOrWhiteSpace() && packageBranch != currentBranch) { @@ -63,18 +66,18 @@ private void ChangeUpdateMechanism(UpdateMechanism updateMechanism) { var config = new Dictionary { - [nameof(_configFileProvider.UpdateMechanism)] = updateMechanism + [nameof(_configFileProvider.CurrentValue.UpdateMechanism)] = updateMechanism }; - _configFileProvider.SaveConfigDictionary(config); + _configFileWriter.SaveConfigDictionary(config); } private void ChangeBranch(string branch) { var config = new Dictionary { - [nameof(_configFileProvider.Branch)] = branch + [nameof(_configFileProvider.CurrentValue.Branch)] = branch }; - _configFileProvider.SaveConfigDictionary(config); + _configFileWriter.SaveConfigDictionary(config); } } } diff --git a/src/NzbDrone.Core/Update/InstallUpdateService.cs b/src/NzbDrone.Core/Update/InstallUpdateService.cs index 5717f4d948..b8b94c73d1 100644 --- a/src/NzbDrone.Core/Update/InstallUpdateService.cs +++ b/src/NzbDrone.Core/Update/InstallUpdateService.cs @@ -1,8 +1,10 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Drawing; using System.IO; using System.Threading; +using Microsoft.Extensions.Options; using NLog; using NzbDrone.Common; using NzbDrone.Common.Disk; @@ -34,7 +36,8 @@ public class InstallUpdateService : IExecute, IEx private readonly IVerifyUpdates _updateVerifier; private readonly IStartupContext _startupContext; private readonly IDeploymentInfoProvider _deploymentInfoProvider; - private readonly IConfigFileProvider _configFileProvider; + private readonly IOptionsMonitor _configFileProvider; + private readonly IConfigFileWriter _configFileWriter; private readonly IRuntimeInfo _runtimeInfo; private readonly IBackupService _backupService; private readonly IOsInfo _osInfo; @@ -50,7 +53,8 @@ public InstallUpdateService(ICheckUpdateService checkUpdateService, IVerifyUpdates updateVerifier, IStartupContext startupContext, IDeploymentInfoProvider deploymentInfoProvider, - IConfigFileProvider configFileProvider, + IOptionsMonitor configFileProvider, + IConfigFileWriter configFileWriter, IRuntimeInfo runtimeInfo, IBackupService backupService, IOsInfo osInfo, @@ -73,6 +77,7 @@ public InstallUpdateService(ICheckUpdateService checkUpdateService, _startupContext = startupContext; _deploymentInfoProvider = deploymentInfoProvider; _configFileProvider = configFileProvider; + _configFileWriter = configFileWriter; _runtimeInfo = runtimeInfo; _backupService = backupService; _osInfo = osInfo; @@ -83,7 +88,7 @@ private bool InstallUpdate(UpdatePackage updatePackage) { EnsureAppDataSafety(); - if (OsInfo.IsWindows || _configFileProvider.UpdateMechanism != UpdateMechanism.Script) + if (OsInfo.IsWindows || _configFileProvider.CurrentValue.UpdateMechanism != UpdateMechanism.Script) { var startupFolder = _appFolderInfo.StartUpFolder; var uiFolder = Path.Combine(startupFolder, "UI"); @@ -137,7 +142,7 @@ private bool InstallUpdate(UpdatePackage updatePackage) _backupService.Backup(BackupType.Update); - if (OsInfo.IsNotWindows && _configFileProvider.UpdateMechanism == UpdateMechanism.Script) + if (OsInfo.IsNotWindows && _configFileProvider.CurrentValue.UpdateMechanism == UpdateMechanism.Script) { InstallUpdateWithScript(updateSandboxFolder); return true; @@ -170,7 +175,7 @@ private bool InstallUpdate(UpdatePackage updatePackage) private void EnsureValidBranch(UpdatePackage package) { - var currentBranch = _configFileProvider.Branch; + var currentBranch = _configFileProvider.CurrentValue.Branch; if (package.Branch != currentBranch) { try @@ -178,7 +183,7 @@ private void EnsureValidBranch(UpdatePackage package) _logger.Info("Branch [{0}] is being redirected to [{1}]]", currentBranch, package.Branch); var config = new Dictionary(); config["Branch"] = package.Branch; - _configFileProvider.SaveConfigDictionary(config); + _configFileWriter.SaveConfigDictionary(config); } catch (Exception e) { @@ -189,7 +194,7 @@ private void EnsureValidBranch(UpdatePackage package) private void InstallUpdateWithScript(string updateSandboxFolder) { - var scriptPath = _configFileProvider.UpdateScriptPath; + var scriptPath = _configFileProvider.CurrentValue.UpdateScriptPath; if (scriptPath.IsNullOrWhiteSpace()) { @@ -204,7 +209,7 @@ private void InstallUpdateWithScript(string updateSandboxFolder) _logger.Info("Removing Radarr.Update"); _diskProvider.DeleteFolder(_appFolderInfo.GetUpdateClientFolder(), true); - _logger.ProgressInfo("Starting update script: {0}", _configFileProvider.UpdateScriptPath); + _logger.ProgressInfo("Starting update script: {0}", _configFileProvider.CurrentValue.UpdateScriptPath); _processProvider.Start(scriptPath, GetUpdaterArgs(updateSandboxFolder)); } @@ -243,19 +248,19 @@ private UpdatePackage GetUpdatePackage(CommandTrigger updateTrigger) return null; } - if (OsInfo.IsNotWindows && !_configFileProvider.UpdateAutomatically && updateTrigger != CommandTrigger.Manual) + if (OsInfo.IsNotWindows && !_configFileProvider.CurrentValue.UpdateAutomatically && updateTrigger != CommandTrigger.Manual) { _logger.ProgressDebug("Auto-update not enabled, not installing available update."); return null; } // Safety net, ConfigureUpdateMechanism should take care of invalid settings - if (_configFileProvider.UpdateMechanism == UpdateMechanism.BuiltIn && _deploymentInfoProvider.IsExternalUpdateMechanism) + if (_configFileProvider.CurrentValue.UpdateMechanism == UpdateMechanism.BuiltIn && _deploymentInfoProvider.IsExternalUpdateMechanism) { _logger.ProgressDebug("Built-In updater disabled, please use {0} to install", _deploymentInfoProvider.PackageUpdateMechanism); return null; } - else if (_configFileProvider.UpdateMechanism != UpdateMechanism.Script && _deploymentInfoProvider.IsExternalUpdateMechanism) + else if (_configFileProvider.CurrentValue.UpdateMechanism != UpdateMechanism.Script && _deploymentInfoProvider.IsExternalUpdateMechanism) { _logger.ProgressDebug("Update available, please use {0} to install", _deploymentInfoProvider.PackageUpdateMechanism); return null; @@ -315,8 +320,8 @@ public void Handle(ApplicationStartingEvent message) _logger.Debug("Post-install update check requested"); // Don't do a prestartup update check unless BuiltIn update is enabled - if (!_configFileProvider.UpdateAutomatically || - _configFileProvider.UpdateMechanism != UpdateMechanism.BuiltIn || + if (!_configFileProvider.CurrentValue.UpdateAutomatically || + _configFileProvider.CurrentValue.UpdateMechanism != UpdateMechanism.BuiltIn || _deploymentInfoProvider.IsExternalUpdateMechanism) { _logger.Debug("Built-in updater disabled, skipping post-install update check"); diff --git a/src/NzbDrone.Core/Update/RecentUpdateProvider.cs b/src/NzbDrone.Core/Update/RecentUpdateProvider.cs index 4796a68e23..09843019a7 100644 --- a/src/NzbDrone.Core/Update/RecentUpdateProvider.cs +++ b/src/NzbDrone.Core/Update/RecentUpdateProvider.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System.Collections.Generic; +using Microsoft.Extensions.Options; using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Core.Configuration; using NzbDrone.Core.Update.History; @@ -12,11 +13,11 @@ public interface IRecentUpdateProvider public class RecentUpdateProvider : IRecentUpdateProvider { - private readonly IConfigFileProvider _configFileProvider; + private readonly IOptionsMonitor _configFileProvider; private readonly IUpdatePackageProvider _updatePackageProvider; private readonly IUpdateHistoryService _updateHistoryService; - public RecentUpdateProvider(IConfigFileProvider configFileProvider, + public RecentUpdateProvider(IOptionsMonitor configFileProvider, IUpdatePackageProvider updatePackageProvider, IUpdateHistoryService updateHistoryService) { @@ -27,7 +28,7 @@ public RecentUpdateProvider(IConfigFileProvider configFileProvider, public List GetRecentUpdatePackages() { - var branch = _configFileProvider.Branch; + var branch = _configFileProvider.CurrentValue.Branch; var version = BuildInfo.Version; var prevVersion = _updateHistoryService.PreviouslyInstalled(); return _updatePackageProvider.GetRecentUpdates(branch, version, prevVersion); diff --git a/src/NzbDrone.Core/Update/UpdateCheckService.cs b/src/NzbDrone.Core/Update/UpdateCheckService.cs index 46e4b6b631..a4436be5a0 100644 --- a/src/NzbDrone.Core/Update/UpdateCheckService.cs +++ b/src/NzbDrone.Core/Update/UpdateCheckService.cs @@ -1,3 +1,4 @@ +using Microsoft.Extensions.Options; using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Core.Configuration; @@ -11,10 +12,10 @@ public interface ICheckUpdateService public class CheckUpdateService : ICheckUpdateService { private readonly IUpdatePackageProvider _updatePackageProvider; - private readonly IConfigFileProvider _configFileProvider; + private readonly IOptionsMonitor _configFileProvider; public CheckUpdateService(IUpdatePackageProvider updatePackageProvider, - IConfigFileProvider configFileProvider) + IOptionsMonitor configFileProvider) { _updatePackageProvider = updatePackageProvider; _configFileProvider = configFileProvider; @@ -22,7 +23,7 @@ public CheckUpdateService(IUpdatePackageProvider updatePackageProvider, public UpdatePackage AvailableUpdate() { - return _updatePackageProvider.GetLatestUpdate(_configFileProvider.Branch, BuildInfo.Version); + return _updatePackageProvider.GetLatestUpdate(_configFileProvider.CurrentValue.Branch, BuildInfo.Version); } } } diff --git a/src/NzbDrone.Host.Test/ContainerFixture.cs b/src/NzbDrone.Host.Test/ContainerFixture.cs index e0f6a7a2e1..94db30b2b1 100644 --- a/src/NzbDrone.Host.Test/ContainerFixture.cs +++ b/src/NzbDrone.Host.Test/ContainerFixture.cs @@ -12,6 +12,7 @@ using NzbDrone.Common.Composition.Extensions; using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Common.Instrumentation.Extensions; +using NzbDrone.Core.Configuration; using NzbDrone.Core.Datastore; using NzbDrone.Core.Datastore.Extensions; using NzbDrone.Core.Download; @@ -44,9 +45,9 @@ public void SetUp() .AddStartupContext(args); // dummy lifetime and broadcaster so tests resolve - 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/AccessControl/FirewallAdapter.cs b/src/NzbDrone.Host/AccessControl/FirewallAdapter.cs index 232babb722..b9760ec155 100644 --- a/src/NzbDrone.Host/AccessControl/FirewallAdapter.cs +++ b/src/NzbDrone.Host/AccessControl/FirewallAdapter.cs @@ -1,5 +1,6 @@ using System; using System.Linq; +using Microsoft.Extensions.Options; using NetFwTypeLib; using NLog; using NzbDrone.Common.EnvironmentInfo; @@ -16,10 +17,10 @@ public class FirewallAdapter : IFirewallAdapter { private const NET_FW_PROFILE_TYPE_ FIREWALL_PROFILE = NET_FW_PROFILE_TYPE_.NET_FW_PROFILE_STANDARD; - private readonly IConfigFileProvider _configFileProvider; + private readonly IOptionsMonitor _configFileProvider; private readonly Logger _logger; - public FirewallAdapter(IConfigFileProvider configFileProvider, Logger logger) + public FirewallAdapter(IOptionsMonitor configFileProvider, Logger logger) { _configFileProvider = configFileProvider; _logger = logger; @@ -29,16 +30,16 @@ public void MakeAccessible() { if (IsFirewallEnabled()) { - if (!IsNzbDronePortOpen(_configFileProvider.Port)) + if (!IsNzbDronePortOpen(_configFileProvider.CurrentValue.Port)) { - _logger.Debug("Opening Port for Radarr: {0}", _configFileProvider.Port); - OpenFirewallPort(_configFileProvider.Port); + _logger.Debug("Opening Port for Radarr: {0}", _configFileProvider.CurrentValue.Port); + OpenFirewallPort(_configFileProvider.CurrentValue.Port); } - if (_configFileProvider.EnableSsl && !IsNzbDronePortOpen(_configFileProvider.SslPort)) + if (_configFileProvider.CurrentValue.EnableSsl && !IsNzbDronePortOpen(_configFileProvider.CurrentValue.SslPort)) { - _logger.Debug("Opening SSL Port for Radarr: {0}", _configFileProvider.SslPort); - OpenFirewallPort(_configFileProvider.SslPort); + _logger.Debug("Opening SSL Port for Radarr: {0}", _configFileProvider.CurrentValue.SslPort); + OpenFirewallPort(_configFileProvider.CurrentValue.SslPort); } } } diff --git a/src/NzbDrone.Host/AppLifetime.cs b/src/NzbDrone.Host/AppLifetime.cs index fc15cfc4ab..b0dfe678b7 100644 --- a/src/NzbDrone.Host/AppLifetime.cs +++ b/src/NzbDrone.Host/AppLifetime.cs @@ -1,6 +1,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Options; using NLog; using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Common.Processes; @@ -14,7 +15,7 @@ namespace NzbDrone.Host public class AppLifetime : IHostedService, IHandle { private readonly IHostApplicationLifetime _appLifetime; - private readonly IConfigFileProvider _configFileProvider; + private readonly IOptionsMonitor _configFileProvider; private readonly IRuntimeInfo _runtimeInfo; private readonly IStartupContext _startupContext; private readonly IBrowserService _browserService; @@ -23,7 +24,7 @@ public class AppLifetime : IHostedService, IHandle private readonly Logger _logger; public AppLifetime(IHostApplicationLifetime appLifetime, - IConfigFileProvider configFileProvider, + IOptionsMonitor configFileProvider, IRuntimeInfo runtimeInfo, IStartupContext startupContext, IBrowserService browserService, @@ -59,7 +60,7 @@ private void OnAppStarted() _runtimeInfo.IsExiting = false; if (!_startupContext.Flags.Contains(StartupContext.NO_BROWSER) - && _configFileProvider.LaunchBrowser) + && _configFileProvider.CurrentValue.LaunchBrowser) { _browserService.LaunchWebUI(); } diff --git a/src/NzbDrone.Host/Bootstrap.cs b/src/NzbDrone.Host/Bootstrap.cs index bc63e27378..209d465932 100644 --- a/src/NzbDrone.Host/Bootstrap.cs +++ b/src/NzbDrone.Host/Bootstrap.cs @@ -3,8 +3,6 @@ using System.Diagnostics; using System.IO; using System.Reflection; -using System.Security.Cryptography; -using System.Security.Cryptography.X509Certificates; using System.Text; using DryIoc; using DryIoc.Microsoft.DependencyInjection; @@ -23,7 +21,6 @@ using NzbDrone.Core.Configuration; using NzbDrone.Core.Datastore.Extensions; using NzbDrone.Host; -using PostgresOptions = NzbDrone.Core.Datastore.PostgresOptions; namespace Radarr.Host { @@ -53,7 +50,6 @@ public static void Start(string[] args, Action trayCallback = null Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); var appMode = GetApplicationMode(startupContext); - var config = GetConfiguration(startupContext); switch (appMode) { @@ -86,18 +82,13 @@ public static void Start(string[] args, Action trayCallback = null .UseServiceProviderFactory(new DryIocServiceProviderFactory(new Container(rules => rules.WithNzbDroneRules()))) .ConfigureContainer(c => { - c.AutoAddServices(Bootstrap.ASSEMBLIES) + c.AutoAddServices(ASSEMBLIES) .AddNzbDroneLogger() .AddDatabase() .AddStartupContext(startupContext) .Resolve() .Route(appMode); - }) - .ConfigureServices(services => - { - services.Configure(config.GetSection("Radarr:Postgres")); }).Build(); - break; } } @@ -115,55 +106,20 @@ public static void Start(string[] args, Action trayCallback = null public static IHostBuilder CreateConsoleHostBuilder(string[] args, StartupContext context) { - var config = GetConfiguration(context); - - var bindAddress = config.GetValue(nameof(ConfigFileProvider.BindAddress), "*"); - var port = config.GetValue(nameof(ConfigFileProvider.Port), 7878); - var sslPort = config.GetValue(nameof(ConfigFileProvider.SslPort), 8787); - var enableSsl = config.GetValue(nameof(ConfigFileProvider.EnableSsl), false); - var sslCertPath = config.GetValue(nameof(ConfigFileProvider.SslCertPath)); - var sslCertPassword = config.GetValue(nameof(ConfigFileProvider.SslCertPassword)); - - var urls = new List { BuildUrl("http", bindAddress, port) }; - - if (enableSsl && sslCertPath.IsNotNullOrWhiteSpace()) - { - urls.Add(BuildUrl("https", bindAddress, sslPort)); - } - return new HostBuilder() .UseContentRoot(Directory.GetCurrentDirectory()) + .AddConfig(context) .UseServiceProviderFactory(new DryIocServiceProviderFactory(new Container(rules => rules.WithNzbDroneRules()))) .ConfigureContainer(c => { - c.AutoAddServices(Bootstrap.ASSEMBLIES) + c.AutoAddServices(ASSEMBLIES) .AddNzbDroneLogger() .AddDatabase() .AddStartupContext(context); }) - .ConfigureServices(services => - { - services.Configure(config.GetSection("Radarr:Postgres")); - }) .ConfigureWebHost(builder => { - builder.UseConfiguration(config); - builder.UseUrls(urls.ToArray()); - builder.UseKestrel(options => - { - if (enableSsl && sslCertPath.IsNotNullOrWhiteSpace()) - { - options.ConfigureHttpsDefaults(configureOptions => - { - configureOptions.ServerCertificate = ValidateSslCertificate(sslCertPath, sslCertPassword); - }); - } - }); - builder.ConfigureKestrel(serverOptions => - { - serverOptions.AllowSynchronousIO = true; - serverOptions.Limits.MaxRequestBodySize = null; - }); + builder.UseKestrel(); builder.UseStartup(); }); } @@ -209,41 +165,15 @@ public static ApplicationModes GetApplicationMode(IStartupContext startupContext return ApplicationModes.Interactive; } - private static IConfiguration GetConfiguration(StartupContext context) + private static IHostBuilder AddConfig(this IHostBuilder builder, StartupContext context) { var appFolder = new AppFolderInfo(context); - return new ConfigurationBuilder() - .AddXmlFile(appFolder.GetConfigPath(), optional: true, reloadOnChange: false) + return builder.ConfigureAppConfiguration((_, config) => + { + config.AddXmlFile(appFolder.GetConfigPath(), optional: true, reloadOnChange: true) .AddInMemoryCollection(new List> { new ("dataProtectionFolder", appFolder.GetDataProtectionPath()) }) - .AddEnvironmentVariables() - .Build(); - } - - private static string BuildUrl(string scheme, string bindAddress, int port) - { - return $"{scheme}://{bindAddress}:{port}"; - } - - private static X509Certificate2 ValidateSslCertificate(string cert, string password) - { - X509Certificate2 certificate; - - try - { - certificate = new X509Certificate2(cert, password, X509KeyStorageFlags.DefaultKeySet); - } - catch (CryptographicException ex) - { - if (ex.HResult == 0x2 || ex.HResult == 0x2006D080) - { - throw new RadarrStartupException(ex, - $"The SSL certificate file {cert} does not exist"); - } - - throw new RadarrStartupException(ex); - } - - return certificate; + .AddEnvironmentVariables($"{BuildInfo.AppName}:"); + }); } } } diff --git a/src/NzbDrone.Host/BrowserService.cs b/src/NzbDrone.Host/BrowserService.cs index 81ca0ed9a6..a7b93e2c83 100644 --- a/src/NzbDrone.Host/BrowserService.cs +++ b/src/NzbDrone.Host/BrowserService.cs @@ -1,4 +1,5 @@ -using System; +using System; +using Microsoft.Extensions.Options; using NLog; using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Common.Processes; @@ -14,11 +15,11 @@ public interface IBrowserService public class BrowserService : IBrowserService { private readonly IProcessProvider _processProvider; - private readonly IConfigFileProvider _configFileProvider; + private readonly IOptionsMonitor _configFileProvider; private readonly IRuntimeInfo _runtimeInfo; private readonly Logger _logger; - public BrowserService(IProcessProvider processProvider, IConfigFileProvider configFileProvider, IRuntimeInfo runtimeInfo, Logger logger) + public BrowserService(IProcessProvider processProvider, IOptionsMonitor configFileProvider, IRuntimeInfo runtimeInfo, Logger logger) { _processProvider = processProvider; _configFileProvider = configFileProvider; @@ -28,7 +29,7 @@ public BrowserService(IProcessProvider processProvider, IConfigFileProvider conf public void LaunchWebUI() { - var url = string.Format("http://localhost:{0}", _configFileProvider.Port); + var url = string.Format("http://localhost:{0}", _configFileProvider.CurrentValue.Port); try { if (_runtimeInfo.IsUserInteractive) diff --git a/src/NzbDrone.Host/ConfigureKestrel.cs b/src/NzbDrone.Host/ConfigureKestrel.cs new file mode 100644 index 0000000000..b743b9cc7d --- /dev/null +++ b/src/NzbDrone.Host/ConfigureKestrel.cs @@ -0,0 +1,81 @@ +using System; +using System.Net; +using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Server.Kestrel.Core; +using Microsoft.Extensions.Options; +using NzbDrone.Common.Exceptions; +using NzbDrone.Common.Extensions; +using NzbDrone.Core.Configuration; + +namespace NzbDrone.Host +{ + public class ConfigureKestrel : IConfigureOptions + { + private readonly IOptionsMonitor _config; + + public ConfigureKestrel(IOptionsMonitor config) + { + _config = config; + } + + public void Configure(KestrelServerOptions options) + { + options.AllowSynchronousIO = true; + options.Limits.MaxRequestBodySize = null; + + Listen(options, _config.CurrentValue.BindAddress, _config.CurrentValue.Port); + + if (_config.CurrentValue.EnableSsl && _config.CurrentValue.SslCertPath.IsNotNullOrWhiteSpace()) + { + options.ConfigureHttpsDefaults(opts => opts.ServerCertificate = ValidateSslCertificate(_config.CurrentValue.SslCertPath, _config.CurrentValue.SslCertPassword)); + Listen(options, _config.CurrentValue.BindAddress, _config.CurrentValue.SslPort, opts => opts.UseHttps()); + } + } + + private static void Listen(KestrelServerOptions options, string address, int port) + { + Listen(options, address, port, _ => { }); + } + + private static void Listen(KestrelServerOptions options, string address, int port, Action configureListenOptions) + { + // following https://github.com/dotnet/aspnetcore/blob/d96a100bddc72606f7417b665428411388b8ac54/src/Servers/Kestrel/Core/src/Internal/AddressBinder.cs#L123 + if (string.Equals(address, "localhost", StringComparison.OrdinalIgnoreCase)) + { + options.ListenLocalhost(port, configureListenOptions); + } + else if (IPAddress.TryParse(address, out var endpoint)) + { + options.Listen(endpoint, port, configureListenOptions); + } + else + { + options.ListenAnyIP(port, configureListenOptions); + } + } + + private static X509Certificate2 ValidateSslCertificate(string cert, string password) + { + X509Certificate2 certificate; + + try + { + certificate = new X509Certificate2(cert, password, X509KeyStorageFlags.DefaultKeySet); + } + catch (CryptographicException ex) + { + if (ex.HResult == 0x2 || ex.HResult == 0x2006D080) + { + throw new RadarrStartupException(ex, + $"The SSL certificate file {cert} does not exist"); + } + + throw new RadarrStartupException(ex); + } + + return certificate; + } + } +} diff --git a/src/NzbDrone.Host/Startup.cs b/src/NzbDrone.Host/Startup.cs index 4a7e848cfb..14a981d542 100644 --- a/src/NzbDrone.Host/Startup.cs +++ b/src/NzbDrone.Host/Startup.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Drawing; using System.IO; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Builder; @@ -10,6 +11,7 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; using Microsoft.OpenApi.Models; using NLog.Extensions.Logging; using NzbDrone.Common.EnvironmentInfo; @@ -44,13 +46,14 @@ public Startup(IConfiguration configuration) public void ConfigureServices(IServiceCollection services) { + services.Configure(Configuration); + services.AddLogging(b => { b.ClearProviders(); - b.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Trace); - b.AddFilter("Microsoft.AspNetCore", Microsoft.Extensions.Logging.LogLevel.Warning); + b.SetMinimumLevel(LogLevel.Trace); + b.AddFilter("Microsoft.AspNetCore", LogLevel.Warning); b.AddFilter("Radarr.Http.Authentication", LogLevel.Information); - b.AddFilter("Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager", LogLevel.Error); b.AddNLog(); }); @@ -213,7 +216,8 @@ public void Configure(IApplicationBuilder app, ReconfigureLogging reconfigureLogging, IAppFolderFactory appFolderFactory, IProvidePidFile pidFileProvider, - IConfigFileProvider configFileProvider, + IConfigFileWriter configFileWriter, + IOptions configFileProvider, IRuntimeInfo runtimeInfo, IFirewallAdapter firewallAdapter, RadarrErrorPipeline errorHandler) @@ -221,6 +225,7 @@ public void Configure(IApplicationBuilder app, initializeLogger.Initialize(); appFolderFactory.Register(); pidFileProvider.Write(); + configFileWriter.EnsureDefaultConfigFile(); reconfigureLogging.Reconfigure(); @@ -244,7 +249,7 @@ public void Configure(IApplicationBuilder app, app.UseForwardedHeaders(); app.UseMiddleware(); - app.UsePathBase(new PathString(configFileProvider.UrlBase)); + app.UsePathBase(new PathString(configFileProvider.Value.UrlBase)); app.UseExceptionHandler(new ExceptionHandlerOptions { AllowStatusCode404Response = true, @@ -259,7 +264,7 @@ public void Configure(IApplicationBuilder app, app.Properties["host.AppName"] = BuildInfo.AppName; app.UseMiddleware(); - app.UseMiddleware(configFileProvider.UrlBase); + app.UseMiddleware(configFileProvider.Value.UrlBase); app.UseMiddleware(); app.UseMiddleware(); app.UseMiddleware(new List { "/api/v3/command" }); diff --git a/src/NzbDrone.Integration.Test/IntegrationTest.cs b/src/NzbDrone.Integration.Test/IntegrationTest.cs index 6dc3671e50..3ee59f31d1 100644 --- a/src/NzbDrone.Integration.Test/IntegrationTest.cs +++ b/src/NzbDrone.Integration.Test/IntegrationTest.cs @@ -5,6 +5,7 @@ using Npgsql; using NUnit.Framework; using NzbDrone.Common.Extensions; +using NzbDrone.Core.Configuration; using NzbDrone.Core.Datastore; using NzbDrone.Core.Datastore.Migration.Framework; using NzbDrone.Core.Indexers.Newznab; @@ -25,7 +26,7 @@ public abstract class IntegrationTest : IntegrationTestBase protected int Port { get; private set; } - protected PostgresOptions PostgresOptions { get; set; } = new (); + protected ConfigFileOptions Options { get; set; } = new (); protected override string RootUrl => $"http://localhost:{Port}/"; @@ -35,14 +36,14 @@ protected override void StartTestTarget() { Port = Interlocked.Increment(ref StaticPort); - PostgresOptions = PostgresDatabase.GetTestOptions(); + Options = ConfigFileOptions.GetOptions(); - if (PostgresOptions?.Host != null) + if (Options?.PostgresHost != null) { - CreatePostgresDb(PostgresOptions); + CreatePostgresDb(Options); } - _runner = new NzbDroneRunner(LogManager.GetCurrentClassLogger(), PostgresOptions, Port); + _runner = new NzbDroneRunner(LogManager.GetCurrentClassLogger(), Options, Port); _runner.Kill(); _runner.Start(); @@ -74,19 +75,19 @@ protected override void InitializeTestTarget() protected override void StopTestTarget() { _runner.Kill(); - if (PostgresOptions?.Host != null) + if (Options?.PostgresHost != null) { - DropPostgresDb(PostgresOptions); + DropPostgresDb(Options); } } - private static void CreatePostgresDb(PostgresOptions options) + private static void CreatePostgresDb(ConfigFileOptions options) { PostgresDatabase.Create(options, MigrationType.Main); PostgresDatabase.Create(options, MigrationType.Log); } - private static void DropPostgresDb(PostgresOptions options) + private static void DropPostgresDb(ConfigFileOptions options) { PostgresDatabase.Drop(options, MigrationType.Main); PostgresDatabase.Drop(options, MigrationType.Log); diff --git a/src/NzbDrone.Test.Common/Datastore/PostgresDatabase.cs b/src/NzbDrone.Test.Common/Datastore/PostgresDatabase.cs index 940334ca29..a384678180 100644 --- a/src/NzbDrone.Test.Common/Datastore/PostgresDatabase.cs +++ b/src/NzbDrone.Test.Common/Datastore/PostgresDatabase.cs @@ -1,5 +1,6 @@ using System; using Npgsql; +using NzbDrone.Core.Configuration; using NzbDrone.Core.Datastore; using NzbDrone.Core.Datastore.Migration.Framework; @@ -7,18 +8,18 @@ namespace NzbDrone.Test.Common.Datastore { public static class PostgresDatabase { - public static PostgresOptions GetTestOptions() + public static ConfigFileOptions GetTestOptions() { - var options = PostgresOptions.GetOptions(); + var options = ConfigFileOptions.GetOptions(); var uid = TestBase.GetUID(); - options.MainDb = uid + "_main"; - options.LogDb = uid + "_log"; + options.PostgresMainDb = uid + "_main"; + options.PostgresLogDb = uid + "_log"; return options; } - public static void Create(PostgresOptions options, MigrationType migrationType) + public static void Create(ConfigFileOptions options, MigrationType migrationType) { var db = GetDatabaseName(options, migrationType); var connectionString = GetConnectionString(options); @@ -26,11 +27,11 @@ public static void Create(PostgresOptions options, MigrationType migrationType) conn.Open(); using var cmd = conn.CreateCommand(); - cmd.CommandText = $"CREATE DATABASE \"{db}\" WITH OWNER = {options.User} ENCODING = 'UTF8' CONNECTION LIMIT = -1;"; + cmd.CommandText = $"CREATE DATABASE \"{db}\" WITH OWNER = {options.PostgresUser} ENCODING = 'UTF8' CONNECTION LIMIT = -1;"; cmd.ExecuteNonQuery(); } - public static void Drop(PostgresOptions options, MigrationType migrationType) + public static void Drop(ConfigFileOptions options, MigrationType migrationType) { var db = GetDatabaseName(options, migrationType); var connectionString = GetConnectionString(options); @@ -42,26 +43,26 @@ public static void Drop(PostgresOptions options, MigrationType migrationType) cmd.ExecuteNonQuery(); } - private static string GetConnectionString(PostgresOptions options) + private static string GetConnectionString(ConfigFileOptions options) { var builder = new NpgsqlConnectionStringBuilder() { - Host = options.Host, - Port = options.Port, - Username = options.User, - Password = options.Password, + Host = options.PostgresHost, + Port = options.PostgresPort, + Username = options.PostgresUser, + Password = options.PostgresPassword, Enlist = false }; return builder.ConnectionString; } - private static string GetDatabaseName(PostgresOptions options, MigrationType migrationType) + private static string GetDatabaseName(ConfigFileOptions options, MigrationType migrationType) { return migrationType switch { - MigrationType.Main => options.MainDb, - MigrationType.Log => options.LogDb, + MigrationType.Main => options.PostgresMainDb, + MigrationType.Log => options.PostgresLogDb, _ => throw new NotImplementedException("Unknown migration type") }; } diff --git a/src/NzbDrone.Test.Common/NzbDroneRunner.cs b/src/NzbDrone.Test.Common/NzbDroneRunner.cs index 4b4531f52f..375cd64ab5 100644 --- a/src/NzbDrone.Test.Common/NzbDroneRunner.cs +++ b/src/NzbDrone.Test.Common/NzbDroneRunner.cs @@ -25,15 +25,15 @@ public class NzbDroneRunner public string AppData { get; private set; } public string ApiKey { get; private set; } - public PostgresOptions PostgresOptions { get; private set; } + public ConfigFileOptions Options { get; private set; } public int Port { get; private set; } - public NzbDroneRunner(Logger logger, PostgresOptions postgresOptions, int port = 7878) + public NzbDroneRunner(Logger logger, ConfigFileOptions options, int port = 7878) { _processProvider = new ProcessProvider(logger); _restClient = new RestClient($"http://localhost:{port}/api/v3"); - PostgresOptions = postgresOptions; + Options = options; Port = port; } @@ -138,14 +138,14 @@ public void KillAll() private void Start(string outputRadarrConsoleExe) { StringDictionary envVars = new (); - if (PostgresOptions?.Host != null) + if (Options?.PostgresHost != null) { - envVars.Add("Radarr__Postgres__Host", PostgresOptions.Host); - envVars.Add("Radarr__Postgres__Port", PostgresOptions.Port.ToString()); - envVars.Add("Radarr__Postgres__User", PostgresOptions.User); - envVars.Add("Radarr__Postgres__Password", PostgresOptions.Password); - envVars.Add("Radarr__Postgres__MainDb", PostgresOptions.MainDb); - envVars.Add("Radarr__Postgres__LogDb", PostgresOptions.LogDb); + envVars.Add("Radarr__PostgresHost", Options.PostgresHost); + envVars.Add("Radarr__PostgresPort", Options.PostgresPort.ToString()); + envVars.Add("Radarr__PostgresUser", Options.PostgresUser); + envVars.Add("Radarr__PostgresPassword", Options.PostgresPassword); + envVars.Add("Radarr__PostgresMainDb", Options.PostgresMainDb); + envVars.Add("Radarr__PostgresLogDb", Options.PostgresLogDb); TestContext.Progress.WriteLine("Using env vars:\n{0}", envVars.ToJson()); } @@ -175,11 +175,11 @@ private void GenerateConfigFile() var xDoc = new XDocument( new XDeclaration("1.0", "utf-8", "yes"), - new XElement(ConfigFileProvider.CONFIG_ELEMENT_NAME, - new XElement(nameof(ConfigFileProvider.ApiKey), apiKey), - new XElement(nameof(ConfigFileProvider.LogLevel), "trace"), - new XElement(nameof(ConfigFileProvider.AnalyticsEnabled), false), - new XElement(nameof(ConfigFileProvider.Port), Port))); + new XElement(ConfigFileWriter.CONFIG_ELEMENT_NAME, + new XElement(nameof(ConfigFileOptions.ApiKey), apiKey), + new XElement(nameof(ConfigFileOptions.LogLevel), "trace"), + new XElement(nameof(ConfigFileOptions.AnalyticsEnabled), false), + new XElement(nameof(ConfigFileOptions.Port), Port))); var data = xDoc.ToString(); diff --git a/src/Radarr.Api.V3/Config/HostConfigController.cs b/src/Radarr.Api.V3/Config/HostConfigController.cs index 7d172d18a8..d3b00ae751 100644 --- a/src/Radarr.Api.V3/Config/HostConfigController.cs +++ b/src/Radarr.Api.V3/Config/HostConfigController.cs @@ -4,6 +4,7 @@ using System.Security.Cryptography.X509Certificates; using FluentValidation; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Options; using NzbDrone.Common.Extensions; using NzbDrone.Core.Authentication; using NzbDrone.Core.Configuration; @@ -19,16 +20,19 @@ namespace Radarr.Api.V3.Config [V3ApiController("config/host")] public class HostConfigController : RestController { - private readonly IConfigFileProvider _configFileProvider; + private readonly IOptionsMonitor _configFileProvider; + private readonly IConfigFileWriter _configFileWriter; private readonly IConfigService _configService; private readonly IUserService _userService; - public HostConfigController(IConfigFileProvider configFileProvider, + public HostConfigController(IOptionsMonitor configFileProvider, + IConfigFileWriter configFileWriter, IConfigService configService, IUserService userService, FileExistsValidator fileExistsValidator) { _configFileProvider = configFileProvider; + _configFileWriter = configFileWriter; _configService = configService; _userService = userService; @@ -87,7 +91,7 @@ protected override HostConfigResource GetResourceById(int id) [HttpGet] public HostConfigResource GetHostConfig() { - var resource = _configFileProvider.ToResource(_configService); + var resource = _configFileProvider.CurrentValue.ToResource(_configService); resource.Id = 1; var user = _userService.FindUser(); @@ -107,7 +111,7 @@ public ActionResult SaveHostConfig(HostConfigResource resour .GetProperties(BindingFlags.Instance | BindingFlags.Public) .ToDictionary(prop => prop.Name, prop => prop.GetValue(resource, null)); - _configFileProvider.SaveConfigDictionary(dictionary); + _configFileWriter.SaveConfigDictionary(dictionary); _configService.SaveConfigDictionary(dictionary); if (resource.Username.IsNotNullOrWhiteSpace() && resource.Password.IsNotNullOrWhiteSpace()) diff --git a/src/Radarr.Api.V3/Config/HostConfigResource.cs b/src/Radarr.Api.V3/Config/HostConfigResource.cs index 836eaf25d5..9810f378e0 100644 --- a/src/Radarr.Api.V3/Config/HostConfigResource.cs +++ b/src/Radarr.Api.V3/Config/HostConfigResource.cs @@ -45,7 +45,7 @@ public class HostConfigResource : RestResource public static class HostConfigResourceMapper { - public static HostConfigResource ToResource(this IConfigFileProvider model, IConfigService configService) + public static HostConfigResource ToResource(this ConfigFileOptions model, IConfigService configService) { // TODO: Clean this mess up. don't mix data from multiple classes, use sub-resources instead? return new HostConfigResource diff --git a/src/Radarr.Api.V3/Logs/LogFileController.cs b/src/Radarr.Api.V3/Logs/LogFileController.cs index f478a63f1f..f1e06c0823 100644 --- a/src/Radarr.Api.V3/Logs/LogFileController.cs +++ b/src/Radarr.Api.V3/Logs/LogFileController.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.IO; +using Microsoft.Extensions.Options; using NzbDrone.Common.Disk; using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Common.Extensions; @@ -16,7 +17,7 @@ public class LogFileController : LogFileControllerBase public LogFileController(IAppFolderInfo appFolderInfo, IDiskProvider diskProvider, - IConfigFileProvider configFileProvider) + IOptionsMonitor configFileProvider) : base(diskProvider, configFileProvider, "") { _appFolderInfo = appFolderInfo; diff --git a/src/Radarr.Api.V3/Logs/LogFileControllerBase.cs b/src/Radarr.Api.V3/Logs/LogFileControllerBase.cs index cd75a33fa3..42322e6526 100644 --- a/src/Radarr.Api.V3/Logs/LogFileControllerBase.cs +++ b/src/Radarr.Api.V3/Logs/LogFileControllerBase.cs @@ -2,6 +2,7 @@ using System.IO; using System.Linq; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Options; using NLog; using NzbDrone.Common.Disk; using NzbDrone.Core.Configuration; @@ -14,10 +15,10 @@ public abstract class LogFileControllerBase : Controller protected string _resource; private readonly IDiskProvider _diskProvider; - private readonly IConfigFileProvider _configFileProvider; + private readonly IOptionsMonitor _configFileProvider; public LogFileControllerBase(IDiskProvider diskProvider, - IConfigFileProvider configFileProvider, + IOptionsMonitor configFileProvider, string resource) { _diskProvider = diskProvider; @@ -42,8 +43,8 @@ public List GetLogFilesResponse() Id = i + 1, Filename = filename, LastWriteTime = _diskProvider.FileGetLastWrite(file), - ContentsUrl = string.Format("{0}/api/v1/{1}/{2}", _configFileProvider.UrlBase, _resource, filename), - DownloadUrl = string.Format("{0}/{1}/{2}", _configFileProvider.UrlBase, DownloadUrlRoot, filename) + ContentsUrl = string.Format("{0}/api/v1/{1}/{2}", _configFileProvider.CurrentValue.UrlBase, _resource, filename), + DownloadUrl = string.Format("{0}/{1}/{2}", _configFileProvider.CurrentValue.UrlBase, DownloadUrlRoot, filename) }); } diff --git a/src/Radarr.Api.V3/Logs/UpdateLogFileController.cs b/src/Radarr.Api.V3/Logs/UpdateLogFileController.cs index fcdc173549..bbb0c2716a 100644 --- a/src/Radarr.Api.V3/Logs/UpdateLogFileController.cs +++ b/src/Radarr.Api.V3/Logs/UpdateLogFileController.cs @@ -2,6 +2,7 @@ using System.IO; using System.Linq; using System.Text.RegularExpressions; +using Microsoft.Extensions.Options; using NzbDrone.Common.Disk; using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Common.Extensions; @@ -18,7 +19,7 @@ public class UpdateLogFileController : LogFileControllerBase public UpdateLogFileController(IAppFolderInfo appFolderInfo, IDiskProvider diskProvider, - IConfigFileProvider configFileProvider) + IOptionsMonitor configFileProvider) : base(diskProvider, configFileProvider, "update") { _appFolderInfo = appFolderInfo; diff --git a/src/Radarr.Api.V3/System/SystemController.cs b/src/Radarr.Api.V3/System/SystemController.cs index c759f7b9e6..4a13222f5e 100644 --- a/src/Radarr.Api.V3/System/SystemController.cs +++ b/src/Radarr.Api.V3/System/SystemController.cs @@ -3,6 +3,7 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Routing; using Microsoft.AspNetCore.Routing.Internal; +using Microsoft.Extensions.Options; using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Common.Extensions; using NzbDrone.Core.Configuration; @@ -20,7 +21,7 @@ public class SystemController : Controller private readonly IRuntimeInfo _runtimeInfo; private readonly IPlatformInfo _platformInfo; private readonly IOsInfo _osInfo; - private readonly IConfigFileProvider _configFileProvider; + private readonly IOptionsMonitor _configFileProvider; private readonly IMainDatabase _database; private readonly ILifecycleService _lifecycleService; private readonly IDeploymentInfoProvider _deploymentInfoProvider; @@ -32,7 +33,7 @@ public SystemController(IAppFolderInfo appFolderInfo, IRuntimeInfo runtimeInfo, IPlatformInfo platformInfo, IOsInfo osInfo, - IConfigFileProvider configFileProvider, + IOptionsMonitor configFileProvider, IMainDatabase database, ILifecycleService lifecycleService, IDeploymentInfoProvider deploymentInfoProvider, @@ -59,7 +60,7 @@ public object GetStatus() return new { AppName = BuildInfo.AppName, - InstanceName = _configFileProvider.InstanceName, + InstanceName = _configFileProvider.CurrentValue.InstanceName, Version = BuildInfo.Version.ToString(), BuildTime = BuildInfo.BuildDateTime, IsDebug = BuildInfo.IsDebug, @@ -76,12 +77,12 @@ public object GetStatus() IsWindows = OsInfo.IsWindows, IsDocker = _osInfo.IsDocker, Mode = _runtimeInfo.Mode, - Branch = _configFileProvider.Branch, - Authentication = _configFileProvider.AuthenticationMethod, + Branch = _configFileProvider.CurrentValue.Branch, + Authentication = _configFileProvider.CurrentValue.AuthenticationMethod, DatabaseType = _database.DatabaseType, DatabaseVersion = _database.Version, MigrationVersion = _database.Migration, - UrlBase = _configFileProvider.UrlBase, + UrlBase = _configFileProvider.CurrentValue.UrlBase, RuntimeVersion = _platformInfo.Version, RuntimeName = PlatformInfo.Platform, StartTime = _runtimeInfo.StartTime, diff --git a/src/Radarr.Http/Authentication/ApiKeyAuthenticationHandler.cs b/src/Radarr.Http/Authentication/ApiKeyAuthenticationHandler.cs index 8de970ffef..11d6e8b161 100644 --- a/src/Radarr.Http/Authentication/ApiKeyAuthenticationHandler.cs +++ b/src/Radarr.Http/Authentication/ApiKeyAuthenticationHandler.cs @@ -28,10 +28,10 @@ public ApiKeyAuthenticationHandler(IOptionsMonitor ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock, - IConfigFileProvider config) + IOptionsMonitor config) : base(options, logger, encoder, clock) { - _apiKey = config.ApiKey; + _apiKey = config.CurrentValue.ApiKey; } private string ParseApiKey() diff --git a/src/Radarr.Http/Authentication/AuthenticationController.cs b/src/Radarr.Http/Authentication/AuthenticationController.cs index e796f92f4e..749ff86b85 100644 --- a/src/Radarr.Http/Authentication/AuthenticationController.cs +++ b/src/Radarr.Http/Authentication/AuthenticationController.cs @@ -4,6 +4,7 @@ using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Options; using NzbDrone.Core.Authentication; using NzbDrone.Core.Configuration; @@ -14,9 +15,9 @@ namespace Radarr.Http.Authentication public class AuthenticationController : Controller { private readonly IAuthenticationService _authService; - private readonly IConfigFileProvider _configFileProvider; + private readonly IOptionsMonitor _configFileProvider; - public AuthenticationController(IAuthenticationService authService, IConfigFileProvider configFileProvider) + public AuthenticationController(IAuthenticationService authService, IOptionsMonitor configFileProvider) { _authService = authService; _configFileProvider = configFileProvider; @@ -46,7 +47,7 @@ public async Task Login([FromForm] LoginResource resource, [FromQ await HttpContext.SignInAsync(AuthenticationType.Forms.ToString(), new ClaimsPrincipal(new ClaimsIdentity(claims, "Cookies", "user", "identifier")), authProperties); - return Redirect(_configFileProvider.UrlBase + "/"); + return Redirect(_configFileProvider.CurrentValue.UrlBase + "/"); } [HttpGet("logout")] @@ -54,7 +55,7 @@ public async Task Logout() { _authService.Logout(HttpContext); await HttpContext.SignOutAsync(AuthenticationType.Forms.ToString()); - return Redirect(_configFileProvider.UrlBase + "/"); + return Redirect(_configFileProvider.CurrentValue.UrlBase + "/"); } } } diff --git a/src/Radarr.Http/Authentication/AuthenticationService.cs b/src/Radarr.Http/Authentication/AuthenticationService.cs index 46cc61b48a..1663ba7ad4 100644 --- a/src/Radarr.Http/Authentication/AuthenticationService.cs +++ b/src/Radarr.Http/Authentication/AuthenticationService.cs @@ -1,4 +1,5 @@ using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Options; using NLog; using NzbDrone.Core.Authentication; using NzbDrone.Core.Configuration; @@ -22,11 +23,11 @@ public class AuthenticationService : IAuthenticationService private static string API_KEY; private static AuthenticationType AUTH_METHOD; - public AuthenticationService(IConfigFileProvider configFileProvider, IUserService userService) + public AuthenticationService(IOptionsMonitor configFileProvider, IUserService userService) { _userService = userService; - API_KEY = configFileProvider.ApiKey; - AUTH_METHOD = configFileProvider.AuthenticationMethod; + API_KEY = configFileProvider.CurrentValue.ApiKey; + AUTH_METHOD = configFileProvider.CurrentValue.AuthenticationMethod; } public User Login(HttpRequest request, string username, string password) diff --git a/src/Radarr.Http/Authentication/UiAuthorizationPolicyProvider.cs b/src/Radarr.Http/Authentication/UiAuthorizationPolicyProvider.cs index a5295a99f7..78d316edc6 100644 --- a/src/Radarr.Http/Authentication/UiAuthorizationPolicyProvider.cs +++ b/src/Radarr.Http/Authentication/UiAuthorizationPolicyProvider.cs @@ -9,12 +9,12 @@ namespace NzbDrone.Http.Authentication public class UiAuthorizationPolicyProvider : IAuthorizationPolicyProvider { private const string POLICY_NAME = "UI"; - private readonly IConfigFileProvider _config; + private readonly IOptionsMonitor _config; public DefaultAuthorizationPolicyProvider FallbackPolicyProvider { get; } public UiAuthorizationPolicyProvider(IOptions options, - IConfigFileProvider config) + IOptionsMonitor config) { FallbackPolicyProvider = new DefaultAuthorizationPolicyProvider(options); _config = config; @@ -28,7 +28,7 @@ public Task GetPolicyAsync(string policyName) { if (policyName.Equals(POLICY_NAME, StringComparison.OrdinalIgnoreCase)) { - var policy = new AuthorizationPolicyBuilder(_config.AuthenticationMethod.ToString()) + var policy = new AuthorizationPolicyBuilder(_config.CurrentValue.AuthenticationMethod.ToString()) .RequireAuthenticatedUser(); return Task.FromResult(policy.Build()); } diff --git a/src/Radarr.Http/Frontend/InitializeJsController.cs b/src/Radarr.Http/Frontend/InitializeJsController.cs index daacaab6d9..0b66a25986 100644 --- a/src/Radarr.Http/Frontend/InitializeJsController.cs +++ b/src/Radarr.Http/Frontend/InitializeJsController.cs @@ -1,6 +1,7 @@ using System.Text; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Options; using NzbDrone.Common; using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Core.Analytics; @@ -12,21 +13,17 @@ namespace Radarr.Http.Frontend [ApiController] public class InitializeJsController : Controller { - private readonly IConfigFileProvider _configFileProvider; + private readonly IOptionsMonitor _configFileProvider; private readonly IAnalyticsService _analyticsService; - private static string _apiKey; - private static string _urlBase; - private string _generatedContent; + private string ApiKey => _configFileProvider.CurrentValue.ApiKey; + private string UrlBase => _configFileProvider.CurrentValue.UrlBase; - public InitializeJsController(IConfigFileProvider configFileProvider, + public InitializeJsController(IOptionsMonitor configFileProvider, IAnalyticsService analyticsService) { _configFileProvider = configFileProvider; _analyticsService = analyticsService; - - _apiKey = configFileProvider.ApiKey; - _urlBase = configFileProvider.UrlBase; } [HttpGet("/initialize.js")] @@ -37,28 +34,21 @@ public IActionResult Index() private string GetContent() { - if (RuntimeInfo.IsProduction && _generatedContent != null) - { - return _generatedContent; - } - var builder = new StringBuilder(); builder.AppendLine("window.Radarr = {"); - builder.AppendLine($" apiRoot: '{_urlBase}/api/v3',"); - builder.AppendLine($" apiKey: '{_apiKey}',"); + builder.AppendLine($" apiRoot: '{UrlBase}/api/v3',"); + builder.AppendLine($" apiKey: '{ApiKey}',"); builder.AppendLine($" release: '{BuildInfo.Release}',"); builder.AppendLine($" version: '{BuildInfo.Version.ToString()}',"); - builder.AppendLine($" instanceName: '{_configFileProvider.InstanceName.ToString()}',"); - builder.AppendLine($" branch: '{_configFileProvider.Branch.ToLower()}',"); + builder.AppendLine($" instanceName: '{_configFileProvider.CurrentValue.InstanceName}',"); + builder.AppendLine($" branch: '{_configFileProvider.CurrentValue.Branch.ToLower()}',"); builder.AppendLine($" analytics: {_analyticsService.IsEnabled.ToString().ToLowerInvariant()},"); builder.AppendLine($" userHash: '{HashUtil.AnonymousToken()}',"); - builder.AppendLine($" urlBase: '{_urlBase}',"); + builder.AppendLine($" urlBase: '{UrlBase}',"); builder.AppendLine($" isProduction: {RuntimeInfo.IsProduction.ToString().ToLowerInvariant()}"); builder.AppendLine("};"); - _generatedContent = builder.ToString(); - - return _generatedContent; + return builder.ToString(); } } } diff --git a/src/Radarr.Http/Frontend/Mappers/BrowserConfig.cs b/src/Radarr.Http/Frontend/Mappers/BrowserConfig.cs index c0c592cbc0..0926e1f8a5 100644 --- a/src/Radarr.Http/Frontend/Mappers/BrowserConfig.cs +++ b/src/Radarr.Http/Frontend/Mappers/BrowserConfig.cs @@ -1,4 +1,5 @@ -using System.IO; +using System.IO; +using Microsoft.Extensions.Options; using NLog; using NzbDrone.Common.Disk; using NzbDrone.Common.EnvironmentInfo; @@ -9,9 +10,9 @@ namespace Radarr.Http.Frontend.Mappers public class BrowserConfig : StaticResourceMapperBase { private readonly IAppFolderInfo _appFolderInfo; - private readonly IConfigFileProvider _configFileProvider; + private readonly IOptionsMonitor _configFileProvider; - public BrowserConfig(IAppFolderInfo appFolderInfo, IDiskProvider diskProvider, IConfigFileProvider configFileProvider, Logger logger) + public BrowserConfig(IAppFolderInfo appFolderInfo, IDiskProvider diskProvider, IOptionsMonitor configFileProvider, Logger logger) : base(diskProvider, logger) { _appFolderInfo = appFolderInfo; @@ -23,7 +24,7 @@ public override string Map(string resourceUrl) var path = resourceUrl.Replace('/', Path.DirectorySeparatorChar); path = path.Trim(Path.DirectorySeparatorChar); - return Path.ChangeExtension(Path.Combine(_appFolderInfo.StartUpFolder, _configFileProvider.UiFolder, path), "xml"); + return Path.ChangeExtension(Path.Combine(_appFolderInfo.StartUpFolder, _uiFolder, path), "xml"); } public override bool CanHandle(string resourceUrl) diff --git a/src/Radarr.Http/Frontend/Mappers/FaviconMapper.cs b/src/Radarr.Http/Frontend/Mappers/FaviconMapper.cs index 65efb29035..1991c75223 100644 --- a/src/Radarr.Http/Frontend/Mappers/FaviconMapper.cs +++ b/src/Radarr.Http/Frontend/Mappers/FaviconMapper.cs @@ -1,4 +1,5 @@ -using System.IO; +using System.IO; +using Microsoft.Extensions.Options; using NLog; using NzbDrone.Common.Disk; using NzbDrone.Common.EnvironmentInfo; @@ -9,9 +10,9 @@ namespace Radarr.Http.Frontend.Mappers public class FaviconMapper : StaticResourceMapperBase { private readonly IAppFolderInfo _appFolderInfo; - private readonly IConfigFileProvider _configFileProvider; + private readonly IOptionsMonitor _configFileProvider; - public FaviconMapper(IAppFolderInfo appFolderInfo, IDiskProvider diskProvider, IConfigFileProvider configFileProvider, Logger logger) + public FaviconMapper(IAppFolderInfo appFolderInfo, IDiskProvider diskProvider, IOptionsMonitor configFileProvider, Logger logger) : base(diskProvider, logger) { _appFolderInfo = appFolderInfo; @@ -29,7 +30,7 @@ public override string Map(string resourceUrl) var path = Path.Combine("Content", "Images", "Icons", fileName); - return Path.Combine(_appFolderInfo.StartUpFolder, _configFileProvider.UiFolder, path); + return Path.Combine(_appFolderInfo.StartUpFolder, _uiFolder, path); } public override bool CanHandle(string resourceUrl) diff --git a/src/Radarr.Http/Frontend/Mappers/IndexHtmlMapper.cs b/src/Radarr.Http/Frontend/Mappers/IndexHtmlMapper.cs index 2d1a2d1029..1f3e2779f8 100644 --- a/src/Radarr.Http/Frontend/Mappers/IndexHtmlMapper.cs +++ b/src/Radarr.Http/Frontend/Mappers/IndexHtmlMapper.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using Microsoft.Extensions.Options; using NLog; using NzbDrone.Common.Disk; using NzbDrone.Common.EnvironmentInfo; @@ -9,19 +10,19 @@ namespace Radarr.Http.Frontend.Mappers { public class IndexHtmlMapper : HtmlMapperBase { - private readonly IConfigFileProvider _configFileProvider; + private readonly IOptionsMonitor _configFileProvider; public IndexHtmlMapper(IAppFolderInfo appFolderInfo, IDiskProvider diskProvider, - IConfigFileProvider configFileProvider, + IOptionsMonitor configFileProvider, Lazy cacheBreakProviderFactory, Logger logger) : base(diskProvider, cacheBreakProviderFactory, logger) { _configFileProvider = configFileProvider; - HtmlPath = Path.Combine(appFolderInfo.StartUpFolder, _configFileProvider.UiFolder, "index.html"); - UrlBase = configFileProvider.UrlBase; + HtmlPath = Path.Combine(appFolderInfo.StartUpFolder, _uiFolder, "index.html"); + UrlBase = configFileProvider.CurrentValue.UrlBase; } public override string Map(string resourceUrl) diff --git a/src/Radarr.Http/Frontend/Mappers/LoginHtmlMapper.cs b/src/Radarr.Http/Frontend/Mappers/LoginHtmlMapper.cs index 184c826cf2..0d44f630ed 100644 --- a/src/Radarr.Http/Frontend/Mappers/LoginHtmlMapper.cs +++ b/src/Radarr.Http/Frontend/Mappers/LoginHtmlMapper.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using Microsoft.Extensions.Options; using NLog; using NzbDrone.Common.Disk; using NzbDrone.Common.EnvironmentInfo; @@ -12,12 +13,12 @@ public class LoginHtmlMapper : HtmlMapperBase public LoginHtmlMapper(IAppFolderInfo appFolderInfo, IDiskProvider diskProvider, Lazy cacheBreakProviderFactory, - IConfigFileProvider configFileProvider, + IOptionsMonitor configFileProvider, Logger logger) : base(diskProvider, cacheBreakProviderFactory, logger) { - HtmlPath = Path.Combine(appFolderInfo.StartUpFolder, configFileProvider.UiFolder, "login.html"); - UrlBase = configFileProvider.UrlBase; + HtmlPath = Path.Combine(appFolderInfo.StartUpFolder, _uiFolder, "login.html"); + UrlBase = configFileProvider.CurrentValue.UrlBase; } public override string Map(string resourceUrl) diff --git a/src/Radarr.Http/Frontend/Mappers/ManifestMapper.cs b/src/Radarr.Http/Frontend/Mappers/ManifestMapper.cs index c0ff69f7cc..333a629095 100644 --- a/src/Radarr.Http/Frontend/Mappers/ManifestMapper.cs +++ b/src/Radarr.Http/Frontend/Mappers/ManifestMapper.cs @@ -1,4 +1,5 @@ -using System.IO; +using System.IO; +using Microsoft.Extensions.Options; using NLog; using NzbDrone.Common.Disk; using NzbDrone.Common.EnvironmentInfo; @@ -9,9 +10,9 @@ namespace Radarr.Http.Frontend.Mappers public class ManifestMapper : StaticResourceMapperBase { private readonly IAppFolderInfo _appFolderInfo; - private readonly IConfigFileProvider _configFileProvider; + private readonly IOptionsMonitor _configFileProvider; - public ManifestMapper(IAppFolderInfo appFolderInfo, IDiskProvider diskProvider, IConfigFileProvider configFileProvider, Logger logger) + public ManifestMapper(IAppFolderInfo appFolderInfo, IDiskProvider diskProvider, IOptionsMonitor configFileProvider, Logger logger) : base(diskProvider, logger) { _appFolderInfo = appFolderInfo; @@ -23,7 +24,7 @@ public override string Map(string resourceUrl) var path = resourceUrl.Replace('/', Path.DirectorySeparatorChar); path = path.Trim(Path.DirectorySeparatorChar); - return Path.ChangeExtension(Path.Combine(_appFolderInfo.StartUpFolder, _configFileProvider.UiFolder, path), "json"); + return Path.ChangeExtension(Path.Combine(_appFolderInfo.StartUpFolder, _uiFolder, path), "json"); } public override bool CanHandle(string resourceUrl) diff --git a/src/Radarr.Http/Frontend/Mappers/RobotsTxtMapper.cs b/src/Radarr.Http/Frontend/Mappers/RobotsTxtMapper.cs index 06fa10407b..697fe2027f 100644 --- a/src/Radarr.Http/Frontend/Mappers/RobotsTxtMapper.cs +++ b/src/Radarr.Http/Frontend/Mappers/RobotsTxtMapper.cs @@ -1,4 +1,5 @@ -using System.IO; +using System.IO; +using Microsoft.Extensions.Options; using NLog; using NzbDrone.Common.Disk; using NzbDrone.Common.EnvironmentInfo; @@ -9,9 +10,9 @@ namespace Radarr.Http.Frontend.Mappers public class RobotsTxtMapper : StaticResourceMapperBase { private readonly IAppFolderInfo _appFolderInfo; - private readonly IConfigFileProvider _configFileProvider; + private readonly IOptionsMonitor _configFileProvider; - public RobotsTxtMapper(IAppFolderInfo appFolderInfo, IDiskProvider diskProvider, IConfigFileProvider configFileProvider, Logger logger) + public RobotsTxtMapper(IAppFolderInfo appFolderInfo, IDiskProvider diskProvider, IOptionsMonitor configFileProvider, Logger logger) : base(diskProvider, logger) { _appFolderInfo = appFolderInfo; @@ -22,7 +23,7 @@ public override string Map(string resourceUrl) { var path = Path.Combine("Content", "robots.txt"); - return Path.Combine(_appFolderInfo.StartUpFolder, _configFileProvider.UiFolder, path); + return Path.Combine(_appFolderInfo.StartUpFolder, _uiFolder, path); } public override bool CanHandle(string resourceUrl) diff --git a/src/Radarr.Http/Frontend/Mappers/StaticResourceMapper.cs b/src/Radarr.Http/Frontend/Mappers/StaticResourceMapper.cs index 785543301b..4a17204d84 100644 --- a/src/Radarr.Http/Frontend/Mappers/StaticResourceMapper.cs +++ b/src/Radarr.Http/Frontend/Mappers/StaticResourceMapper.cs @@ -1,4 +1,5 @@ using System.IO; +using Microsoft.Extensions.Options; using NLog; using NzbDrone.Common.Disk; using NzbDrone.Common.EnvironmentInfo; @@ -9,9 +10,9 @@ namespace Radarr.Http.Frontend.Mappers public class StaticResourceMapper : StaticResourceMapperBase { private readonly IAppFolderInfo _appFolderInfo; - private readonly IConfigFileProvider _configFileProvider; + private readonly IOptionsMonitor _configFileProvider; - public StaticResourceMapper(IAppFolderInfo appFolderInfo, IDiskProvider diskProvider, IConfigFileProvider configFileProvider, Logger logger) + public StaticResourceMapper(IAppFolderInfo appFolderInfo, IDiskProvider diskProvider, IOptionsMonitor configFileProvider, Logger logger) : base(diskProvider, logger) { _appFolderInfo = appFolderInfo; @@ -23,7 +24,7 @@ public override string Map(string resourceUrl) var path = resourceUrl.Replace('/', Path.DirectorySeparatorChar); path = path.Trim(Path.DirectorySeparatorChar); - return Path.Combine(_appFolderInfo.StartUpFolder, _configFileProvider.UiFolder, path); + return Path.Combine(_appFolderInfo.StartUpFolder, _uiFolder, path); } public override bool CanHandle(string resourceUrl) diff --git a/src/Radarr.Http/Frontend/Mappers/StaticResourceMapperBase.cs b/src/Radarr.Http/Frontend/Mappers/StaticResourceMapperBase.cs index 402bce1672..476ed1db70 100644 --- a/src/Radarr.Http/Frontend/Mappers/StaticResourceMapperBase.cs +++ b/src/Radarr.Http/Frontend/Mappers/StaticResourceMapperBase.cs @@ -10,6 +10,8 @@ namespace Radarr.Http.Frontend.Mappers { public abstract class StaticResourceMapperBase : IMapHttpRequestsToDisk { + protected readonly string _uiFolder; + private readonly IDiskProvider _diskProvider; private readonly Logger _logger; private readonly StringComparison _caseSensitive; @@ -22,6 +24,7 @@ protected StaticResourceMapperBase(IDiskProvider diskProvider, Logger logger) _mimeTypeProvider = new FileExtensionContentTypeProvider(); _caseSensitive = RuntimeInfo.IsProduction ? DiskProviderBase.PathStringComparison : StringComparison.OrdinalIgnoreCase; + _uiFolder = BuildInfo.IsDebug ? Path.Combine("..", "UI") : "UI"; } public abstract string Map(string resourceUrl); diff --git a/src/postgres.runsettings b/src/postgres.runsettings index fceb79c36b..a021070761 100644 --- a/src/postgres.runsettings +++ b/src/postgres.runsettings @@ -2,10 +2,10 @@ - 192.168.100.5 - 5432 - abc - abc + 192.168.100.5 + 5432 + abc + abc \ No newline at end of file