Modernize startup and ConfigFileProvider

This commit is contained in:
ta264 2022-06-20 21:35:11 +01:00
parent e3468daba0
commit 41e8f7aa45
58 changed files with 704 additions and 853 deletions

View file

@ -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<ConfigFileProvider>
public class ConfigFileWriterTest : TestBase<ConfigFileWriter>
{
private string _configFileContents;
private string _configFilePath;
@ -45,56 +45,7 @@ protected void WithMockConfigFile(string configFile)
.Callback<string, string>((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<IDiskProvider>()
.Setup(v => v.FileExists(_configFilePath))
.Returns(true);
Assert.Throws<InvalidConfigFileException>(() => Subject.GetValue("key", "value"));
}
[Test]
public void should_throw_if_config_file_contains_only_null_character()
{
_configFileContents = "\0";
Assert.Throws<InvalidConfigFileException>(() => Subject.GetValue("key", "value"));
}
[Test]
public void should_throw_if_config_file_contains_invalid_xml()
{
_configFileContents = "{ \"key\": \"value\" }";
Assert.Throws<InvalidConfigFileException>(() => Subject.GetValue("key", "value"));
}
}*/
}
}

View file

@ -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<IHostLifetime>().Object);
container.RegisterInstance(new Mock<IOptions<PostgresOptions>>().Object);
container.RegisterInstance(new Mock<IOptions<ConfigFileOptions>>().Object);
var serviceProvider = container.GetServiceProvider();

View file

@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using FluentAssertions;

View file

@ -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<IOptions<PostgresOptions>>().Value;
var options = Mocker.Resolve<IOptionsMonitor<ConfigFileOptions>>().CurrentValue;
PostgresDatabase.Create(options, MigrationType);
}
private void DropPostgresDb()
{
var options = Mocker.Resolve<IOptions<PostgresOptions>>().Value;
var options = Mocker.Resolve<IOptionsMonitor<ConfigFileOptions>>().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<IConfigFileProvider>(Mocker.Resolve<ConfigFileProvider>());
Mocker.GetMock<IOptionsMonitor<ConfigFileOptions>>().Setup(x => x.CurrentValue).Returns(options);
Mocker.SetConstant<IConnectionStringFactory>(Mocker.Resolve<ConnectionStringFactory>());
Mocker.SetConstant<IMigrationController>(Mocker.Resolve<MigrationController>());

View file

@ -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<IConfigFileProvider>()
.SetupGet(s => s.Branch)
.Returns(branch);
Mocker.GetMock<IOptionsMonitor<ConfigFileOptions>>()
.Setup(s => s.CurrentValue)
.Returns(new ConfigFileOptions { Branch = branch });
}
[TestCase("aphrodite")]

View file

@ -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<ILocalizationService>()
.Setup(s => s.GetLocalizedString(It.IsAny<string>()))
.Returns("Some Warning Message");
Mocker.GetMock<IOptionsMonitor<ConfigFileOptions>>()
.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<IConfigFileProvider>()
.Setup(s => s.UpdateAutomatically)
.Returns(true);
Mocker.GetMock<IOptionsMonitor<ConfigFileOptions>>()
.Setup(s => s.CurrentValue)
.Returns(new ConfigFileOptions { UpdateAutomatically = true });
Mocker.GetMock<IAppFolderInfo>()
.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<IConfigFileProvider>()
.Setup(s => s.UpdateAutomatically)
.Returns(true);
Mocker.GetMock<IOptionsMonitor<ConfigFileOptions>>()
.Setup(s => s.CurrentValue)
.Returns(new ConfigFileOptions { UpdateAutomatically = true });
Mocker.GetMock<IAppFolderInfo>()
.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<IConfigFileProvider>()
.Setup(s => s.UpdateAutomatically)
.Returns(true);
Mocker.GetMock<IConfigFileProvider>()
.Setup(s => s.UpdateMechanism)
.Returns(UpdateMechanism.Script);
Mocker.GetMock<IOptionsMonitor<ConfigFileOptions>>()
.Setup(s => s.CurrentValue)
.Returns(new ConfigFileOptions { UpdateAutomatically = true, UpdateMechanism = UpdateMechanism.Script });
Mocker.GetMock<IAppFolderInfo>()
.Setup(s => s.StartUpFolder)

View file

@ -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<IMovieService>().Setup(m => m.GetMovie(It.Is<int>(id => id == _movie.Id))).Returns(_movie);
Mocker.GetMock<IOptionsMonitor<ConfigFileOptions>>()
.Setup(x => x.CurrentValue)
.Returns(new ConfigFileOptions());
}
[Test]

View file

@ -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<IProcessProvider>().Setup(c => c.GetCurrentProcess()).Returns(new ProcessInfo { Id = 12 });
Mocker.GetMock<IRuntimeInfo>().Setup(c => c.ExecutingApplication).Returns(@"C:\Test\Radarr.exe");
Mocker.GetMock<IConfigFileProvider>()
.SetupGet(s => s.UpdateAutomatically)
.Returns(true);
Mocker.GetMock<IOptionsMonitor<ConfigFileOptions>>()
.Setup(s => s.CurrentValue)
.Returns(new ConfigFileOptions { UpdateAutomatically = true });
Mocker.GetMock<IDiskProvider>()
.Setup(c => c.FolderWritable(It.IsAny<string>()))
@ -77,13 +78,9 @@ public void Setup()
private void GivenInstallScript(string path)
{
Mocker.GetMock<IConfigFileProvider>()
.SetupGet(s => s.UpdateMechanism)
.Returns(UpdateMechanism.Script);
Mocker.GetMock<IConfigFileProvider>()
.SetupGet(s => s.UpdateScriptPath)
.Returns(path);
Mocker.GetMock<IOptionsMonitor<ConfigFileOptions>>()
.Setup(s => s.CurrentValue)
.Returns(new ConfigFileOptions { UpdateMechanism = UpdateMechanism.Script, UpdateScriptPath = path });
Mocker.GetMock<IDiskProvider>()
.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<IConfigFileProvider>()
Mocker.GetMock<IConfigFileWriter>()
.Verify(v => v.SaveConfigDictionary(It.Is<Dictionary<string, object>>(d => d.ContainsKey("Branch") && (string)d["Branch"] == "fake")), Times.Once());
}

View file

@ -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<ConfigFileOptions> _configFileProvider;
private readonly IHistoryService _historyService;
public AnalyticsService(IHistoryService historyService, IConfigFileProvider configFileProvider)
public AnalyticsService(IHistoryService historyService, IOptionsMonitor<ConfigFileOptions> 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
{

View file

@ -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<ApplicationStartedEvent>
public class UserService : IUserService
{
private readonly IUserRepository _repo;
private readonly IAppFolderInfo _appFolderInfo;
private readonly IDiskProvider _diskProvider;
private readonly IConfigFileProvider _configFileProvider;
private readonly IOptionsMonitor<ConfigFileOptions> _configFileProvider;
public UserService(IUserRepository repo,
IAppFolderInfo appFolderInfo,
IDiskProvider diskProvider,
IConfigFileProvider configFileProvider)
IOptionsMonitor<ConfigFileOptions> 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);
}
}
}

View file

@ -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;
}
}
}

View file

@ -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<ApplicationStartedEvent>,
IExecute<ResetApiKeyCommand>
{
XDocument LoadConfigFile();
Dictionary<string, object> GetConfigDictionary();
void SaveConfigDictionary(Dictionary<string, object> 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<string> _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> postgresOptions)
{
_cache = cacheManager.GetCache<string>(GetType());
_eventAggregator = eventAggregator;
_diskProvider = diskProvider;
_configFile = appFolderInfo.GetConfigPath();
_postgresOptions = postgresOptions.Value;
}
public Dictionary<string, object> GetConfigDictionary()
{
var dict = new Dictionary<string, object>(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<string, object> 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<T>(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());
}
}
}

View file

@ -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<ApplicationStartedEvent>,
IExecute<ResetApiKeyCommand>
{
public void EnsureDefaultConfigFile();
void SaveConfigDictionary(Dictionary<string, object> 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> _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> configFileOptions,
Logger logger)
{
_eventAggregator = eventAggregator;
_diskProvider = diskProvider;
_configuration = configuration as IConfigurationRoot;
_configFileOptions = configFileOptions;
_logger = logger;
_configFile = appFolderInfo.GetConfigPath();
_configFileOptions.OnChange(OnChange);
}
private Dictionary<string, object> GetConfigDictionary()
{
var dict = new Dictionary<string, object>(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<string, object> 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());
}
}
}

View file

@ -0,0 +1,9 @@
using System;
namespace NzbDrone.Core.Configuration
{
[AttributeUsage(AttributeTargets.Property)]
public class PersistAttribute : Attribute
{
}
}

View file

@ -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<ConfigFileOptions> _configFileProvider;
public ConnectionStringFactory(IAppFolderInfo appFolderInfo, IConfigFileProvider configFileProvider)
public ConnectionStringFactory(IAppFolderInfo appFolderInfo, IOptionsMonitor<ConfigFileOptions> 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;

View file

@ -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;
}

View file

@ -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<ConfigFileOptions> _configFileService;
public ReleaseBranchCheck(IConfigFileProvider configFileService, ILocalizationService localizationService)
public ReleaseBranchCheck(IOptionsMonitor<ConfigFileOptions> 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());

View file

@ -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<ConfigFileOptions> _configFileProvider;
private readonly IOsInfo _osInfo;
public UpdateCheck(IDiskProvider diskProvider,
IAppFolderInfo appFolderInfo,
ICheckUpdateService checkUpdateService,
IConfigFileProvider configFileProvider,
IOptionsMonitor<ConfigFileOptions> 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"))

View file

@ -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<ConfigFileOptions> _configFileProvider;
private readonly IHttpRequestBuilderFactory _cloudRequestBuilder;
private readonly Logger _logger;
public ServerSideNotificationService(IHttpClient client, IConfigFileProvider configFileProvider, IRadarrCloudRequestBuilder cloudRequestBuilder, Logger logger)
public ServerSideNotificationService(IHttpClient client, IOptionsMonitor<ConfigFileOptions> configFileProvider, IRadarrCloudRequestBuilder cloudRequestBuilder, Logger logger)
{
_client = client;
_configFileProvider = configFileProvider;
@ -39,7 +40,7 @@ public List<HealthCheck> 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
{

View file

@ -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<ConfigFileSavedEvent>
{
private readonly IConfigFileProvider _configFileProvider;
private readonly IOptionsMonitor<ConfigFileOptions> _configFileProvider;
public ReconfigureLogging(IConfigFileProvider configFileProvider)
public ReconfigureLogging(IOptionsMonitor<ConfigFileOptions> 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<NzbDroneFileTarget>())
{
target.MaxArchiveFiles = _configFileProvider.LogRotate;
target.MaxArchiveFiles = _configFileProvider.CurrentValue.LogRotate;
}
}
@ -104,8 +105,8 @@ private void ReconfigureSentry()
var sentryTarget = LogManager.Configuration.AllTargets.OfType<SentryTarget>().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);

View file

@ -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<ApplicationStartedEvent>
{
private readonly IConfigFileProvider _configFileProvider;
private readonly IOptionsMonitor<ConfigFileOptions> _configFileProvider;
private readonly IPlatformInfo _platformInfo;
private readonly IMainDatabase _database;
public ReconfigureSentry(IConfigFileProvider configFileProvider,
public ReconfigureSentry(IOptionsMonitor<ConfigFileOptions> configFileProvider,
IPlatformInfo platformInfo,
IMainDatabase database)
{
@ -30,7 +31,7 @@ public void Reconfigure()
var sentryTarget = LogManager.Configuration.AllTargets.OfType<SentryTarget>().FirstOrDefault();
if (sentryTarget != null)
{
sentryTarget.UpdateScope(_database.Version, _database.Migration, _configFileProvider.Branch, _platformInfo);
sentryTarget.UpdateScope(_database.Version, _database.Migration, _configFileProvider.CurrentValue.Branch, _platformInfo);
}
}

View file

@ -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<ConfigFileOptions> _configFileProvider;
private readonly ICached<string> _cache;
public MediaCoverProxy(IHttpClient httpClient, IConfigFileProvider configFileProvider, ICacheManager cacheManager)
public MediaCoverProxy(IHttpClient httpClient, IOptionsMonitor<ConfigFileOptions> 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)

View file

@ -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<ConfigFileOptions> _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<ConfigFileOptions> configFileProvider,
IEventAggregator eventAggregator,
Logger logger)
{
@ -104,7 +105,7 @@ public void ConvertToLocalUrls(int movieId, IEnumerable<MediaCover> 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;

View file

@ -13,7 +13,8 @@
<PackageReference Include="System.Memory" Version="4.5.4" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="6.0.1" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="7.0.0-preview.5.22301.12" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="7.0.0-preview.5.22301.12" />
<PackageReference Include="FluentMigrator.Runner" Version="3.3.2" />
<PackageReference Include="FluentMigrator.Runner.SQLite" Version="3.3.2" />
<PackageReference Include="FluentValidation" Version="8.6.2" />

View file

@ -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<ApplicationStartedEvent>
{
private readonly Logger _logger;
private readonly IConfigFileProvider _configFileProvider;
private readonly IOptionsMonitor<ConfigFileOptions> _configFileProvider;
private readonly IConfigFileWriter _configFileWriter;
private readonly IDeploymentInfoProvider _deploymentInfoProvider;
public UpdaterConfigProvider(IDeploymentInfoProvider deploymentInfoProvider, IConfigFileProvider configFileProvider, Logger logger)
public UpdaterConfigProvider(IDeploymentInfoProvider deploymentInfoProvider, IOptionsMonitor<ConfigFileOptions> 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<string, object>
{
[nameof(_configFileProvider.UpdateMechanism)] = updateMechanism
[nameof(_configFileProvider.CurrentValue.UpdateMechanism)] = updateMechanism
};
_configFileProvider.SaveConfigDictionary(config);
_configFileWriter.SaveConfigDictionary(config);
}
private void ChangeBranch(string branch)
{
var config = new Dictionary<string, object>
{
[nameof(_configFileProvider.Branch)] = branch
[nameof(_configFileProvider.CurrentValue.Branch)] = branch
};
_configFileProvider.SaveConfigDictionary(config);
_configFileWriter.SaveConfigDictionary(config);
}
}
}

View file

@ -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<ApplicationCheckUpdateCommand>, IEx
private readonly IVerifyUpdates _updateVerifier;
private readonly IStartupContext _startupContext;
private readonly IDeploymentInfoProvider _deploymentInfoProvider;
private readonly IConfigFileProvider _configFileProvider;
private readonly IOptionsMonitor<ConfigFileOptions> _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<ConfigFileOptions> 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<string, object>();
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");

View file

@ -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<ConfigFileOptions> _configFileProvider;
private readonly IUpdatePackageProvider _updatePackageProvider;
private readonly IUpdateHistoryService _updateHistoryService;
public RecentUpdateProvider(IConfigFileProvider configFileProvider,
public RecentUpdateProvider(IOptionsMonitor<ConfigFileOptions> configFileProvider,
IUpdatePackageProvider updatePackageProvider,
IUpdateHistoryService updateHistoryService)
{
@ -27,7 +28,7 @@ public RecentUpdateProvider(IConfigFileProvider configFileProvider,
public List<UpdatePackage> GetRecentUpdatePackages()
{
var branch = _configFileProvider.Branch;
var branch = _configFileProvider.CurrentValue.Branch;
var version = BuildInfo.Version;
var prevVersion = _updateHistoryService.PreviouslyInstalled();
return _updatePackageProvider.GetRecentUpdates(branch, version, prevVersion);

View file

@ -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<ConfigFileOptions> _configFileProvider;
public CheckUpdateService(IUpdatePackageProvider updatePackageProvider,
IConfigFileProvider configFileProvider)
IOptionsMonitor<ConfigFileOptions> 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);
}
}
}

View file

@ -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<IHostLifetime>(new Mock<IHostLifetime>().Object);
container.RegisterInstance<IBroadcastSignalRMessage>(new Mock<IBroadcastSignalRMessage>().Object);
container.RegisterInstance<IOptions<PostgresOptions>>(new Mock<IOptions<PostgresOptions>>().Object);
container.RegisterInstance(new Mock<IHostLifetime>().Object);
container.RegisterInstance(new Mock<IBroadcastSignalRMessage>().Object);
container.RegisterInstance(new Mock<IOptionsMonitor<ConfigFileOptions>>().Object);
_container = container.GetServiceProvider();
}

View file

@ -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<ConfigFileOptions> _configFileProvider;
private readonly Logger _logger;
public FirewallAdapter(IConfigFileProvider configFileProvider, Logger logger)
public FirewallAdapter(IOptionsMonitor<ConfigFileOptions> 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);
}
}
}

View file

@ -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<ApplicationShutdownRequested>
{
private readonly IHostApplicationLifetime _appLifetime;
private readonly IConfigFileProvider _configFileProvider;
private readonly IOptionsMonitor<ConfigFileOptions> _configFileProvider;
private readonly IRuntimeInfo _runtimeInfo;
private readonly IStartupContext _startupContext;
private readonly IBrowserService _browserService;
@ -23,7 +24,7 @@ public class AppLifetime : IHostedService, IHandle<ApplicationShutdownRequested>
private readonly Logger _logger;
public AppLifetime(IHostApplicationLifetime appLifetime,
IConfigFileProvider configFileProvider,
IOptionsMonitor<ConfigFileOptions> 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();
}

View file

@ -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<IHostBuilder> 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<IHostBuilder> trayCallback = null
.UseServiceProviderFactory(new DryIocServiceProviderFactory(new Container(rules => rules.WithNzbDroneRules())))
.ConfigureContainer<IContainer>(c =>
{
c.AutoAddServices(Bootstrap.ASSEMBLIES)
c.AutoAddServices(ASSEMBLIES)
.AddNzbDroneLogger()
.AddDatabase()
.AddStartupContext(startupContext)
.Resolve<UtilityModeRouter>()
.Route(appMode);
})
.ConfigureServices(services =>
{
services.Configure<PostgresOptions>(config.GetSection("Radarr:Postgres"));
}).Build();
break;
}
}
@ -115,55 +106,20 @@ public static void Start(string[] args, Action<IHostBuilder> 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<string>(nameof(ConfigFileProvider.SslCertPath));
var sslCertPassword = config.GetValue<string>(nameof(ConfigFileProvider.SslCertPassword));
var urls = new List<string> { 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<IContainer>(c =>
{
c.AutoAddServices(Bootstrap.ASSEMBLIES)
c.AutoAddServices(ASSEMBLIES)
.AddNzbDroneLogger()
.AddDatabase()
.AddStartupContext(context);
})
.ConfigureServices(services =>
{
services.Configure<PostgresOptions>(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<Startup>();
});
}
@ -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<KeyValuePair<string, string>> { 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}:");
});
}
}
}

View file

@ -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<ConfigFileOptions> _configFileProvider;
private readonly IRuntimeInfo _runtimeInfo;
private readonly Logger _logger;
public BrowserService(IProcessProvider processProvider, IConfigFileProvider configFileProvider, IRuntimeInfo runtimeInfo, Logger logger)
public BrowserService(IProcessProvider processProvider, IOptionsMonitor<ConfigFileOptions> 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)

View file

@ -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<KestrelServerOptions>
{
private readonly IOptionsMonitor<ConfigFileOptions> _config;
public ConfigureKestrel(IOptionsMonitor<ConfigFileOptions> 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<ListenOptions> 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;
}
}
}

View file

@ -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<ConfigFileOptions>(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<ConfigFileOptions> 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<LoggingMiddleware>();
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<VersionMiddleware>();
app.UseMiddleware<UrlBaseMiddleware>(configFileProvider.UrlBase);
app.UseMiddleware<UrlBaseMiddleware>(configFileProvider.Value.UrlBase);
app.UseMiddleware<CacheHeaderMiddleware>();
app.UseMiddleware<IfModifiedMiddleware>();
app.UseMiddleware<BufferingMiddleware>(new List<string> { "/api/v3/command" });

View file

@ -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);

View file

@ -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")
};
}

View file

@ -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();

View file

@ -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<HostConfigResource>
{
private readonly IConfigFileProvider _configFileProvider;
private readonly IOptionsMonitor<ConfigFileOptions> _configFileProvider;
private readonly IConfigFileWriter _configFileWriter;
private readonly IConfigService _configService;
private readonly IUserService _userService;
public HostConfigController(IConfigFileProvider configFileProvider,
public HostConfigController(IOptionsMonitor<ConfigFileOptions> 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<HostConfigResource> 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())

View file

@ -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

View file

@ -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<ConfigFileOptions> configFileProvider)
: base(diskProvider, configFileProvider, "")
{
_appFolderInfo = appFolderInfo;

View file

@ -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<ConfigFileOptions> _configFileProvider;
public LogFileControllerBase(IDiskProvider diskProvider,
IConfigFileProvider configFileProvider,
IOptionsMonitor<ConfigFileOptions> configFileProvider,
string resource)
{
_diskProvider = diskProvider;
@ -42,8 +43,8 @@ public List<LogFileResource> 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)
});
}

View file

@ -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<ConfigFileOptions> configFileProvider)
: base(diskProvider, configFileProvider, "update")
{
_appFolderInfo = appFolderInfo;

View file

@ -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<ConfigFileOptions> _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<ConfigFileOptions> 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,

View file

@ -28,10 +28,10 @@ public ApiKeyAuthenticationHandler(IOptionsMonitor<ApiKeyAuthenticationOptions>
ILoggerFactory logger,
UrlEncoder encoder,
ISystemClock clock,
IConfigFileProvider config)
IOptionsMonitor<ConfigFileOptions> config)
: base(options, logger, encoder, clock)
{
_apiKey = config.ApiKey;
_apiKey = config.CurrentValue.ApiKey;
}
private string ParseApiKey()

View file

@ -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<ConfigFileOptions> _configFileProvider;
public AuthenticationController(IAuthenticationService authService, IConfigFileProvider configFileProvider)
public AuthenticationController(IAuthenticationService authService, IOptionsMonitor<ConfigFileOptions> configFileProvider)
{
_authService = authService;
_configFileProvider = configFileProvider;
@ -46,7 +47,7 @@ public async Task<IActionResult> 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<IActionResult> Logout()
{
_authService.Logout(HttpContext);
await HttpContext.SignOutAsync(AuthenticationType.Forms.ToString());
return Redirect(_configFileProvider.UrlBase + "/");
return Redirect(_configFileProvider.CurrentValue.UrlBase + "/");
}
}
}

View file

@ -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<ConfigFileOptions> 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)

View file

@ -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<ConfigFileOptions> _config;
public DefaultAuthorizationPolicyProvider FallbackPolicyProvider { get; }
public UiAuthorizationPolicyProvider(IOptions<AuthorizationOptions> options,
IConfigFileProvider config)
IOptionsMonitor<ConfigFileOptions> config)
{
FallbackPolicyProvider = new DefaultAuthorizationPolicyProvider(options);
_config = config;
@ -28,7 +28,7 @@ public Task<AuthorizationPolicy> 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());
}

View file

@ -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<ConfigFileOptions> _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<ConfigFileOptions> 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();
}
}
}

View file

@ -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<ConfigFileOptions> _configFileProvider;
public BrowserConfig(IAppFolderInfo appFolderInfo, IDiskProvider diskProvider, IConfigFileProvider configFileProvider, Logger logger)
public BrowserConfig(IAppFolderInfo appFolderInfo, IDiskProvider diskProvider, IOptionsMonitor<ConfigFileOptions> 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)

View file

@ -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<ConfigFileOptions> _configFileProvider;
public FaviconMapper(IAppFolderInfo appFolderInfo, IDiskProvider diskProvider, IConfigFileProvider configFileProvider, Logger logger)
public FaviconMapper(IAppFolderInfo appFolderInfo, IDiskProvider diskProvider, IOptionsMonitor<ConfigFileOptions> 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)

View file

@ -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<ConfigFileOptions> _configFileProvider;
public IndexHtmlMapper(IAppFolderInfo appFolderInfo,
IDiskProvider diskProvider,
IConfigFileProvider configFileProvider,
IOptionsMonitor<ConfigFileOptions> configFileProvider,
Lazy<ICacheBreakerProvider> 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)

View file

@ -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<ICacheBreakerProvider> cacheBreakProviderFactory,
IConfigFileProvider configFileProvider,
IOptionsMonitor<ConfigFileOptions> 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)

View file

@ -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<ConfigFileOptions> _configFileProvider;
public ManifestMapper(IAppFolderInfo appFolderInfo, IDiskProvider diskProvider, IConfigFileProvider configFileProvider, Logger logger)
public ManifestMapper(IAppFolderInfo appFolderInfo, IDiskProvider diskProvider, IOptionsMonitor<ConfigFileOptions> 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)

View file

@ -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<ConfigFileOptions> _configFileProvider;
public RobotsTxtMapper(IAppFolderInfo appFolderInfo, IDiskProvider diskProvider, IConfigFileProvider configFileProvider, Logger logger)
public RobotsTxtMapper(IAppFolderInfo appFolderInfo, IDiskProvider diskProvider, IOptionsMonitor<ConfigFileOptions> 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)

View file

@ -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<ConfigFileOptions> _configFileProvider;
public StaticResourceMapper(IAppFolderInfo appFolderInfo, IDiskProvider diskProvider, IConfigFileProvider configFileProvider, Logger logger)
public StaticResourceMapper(IAppFolderInfo appFolderInfo, IDiskProvider diskProvider, IOptionsMonitor<ConfigFileOptions> 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)

View file

@ -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);

View file

@ -2,10 +2,10 @@
<RunSettings>
<RunConfiguration>
<EnvironmentVariables>
<Radarr__Postgres__Host>192.168.100.5</Radarr__Postgres__Host>
<Radarr__Postgres__Port>5432</Radarr__Postgres__Port>
<Radarr__Postgres__User>abc</Radarr__Postgres__User>
<Radarr__Postgres__Password>abc</Radarr__Postgres__Password>
<Radarr__PostgresHost>192.168.100.5</Radarr__PostgresHost>
<Radarr__PostgresPort>5432</Radarr__PostgresPort>
<Radarr__PostgresUser>abc</Radarr__PostgresUser>
<Radarr__PostgresPassword>abc</Radarr__PostgresPassword>
</EnvironmentVariables>
</RunConfiguration>
</RunSettings>