mirror of
https://github.com/Radarr/Radarr.git
synced 2024-10-05 15:47:20 +02:00
New: Use native dotnet host and DryIoc
(cherry picked from commit d6170dbfedf27a6218afe242a0fae2eb8b368aec) (cherry picked from commit 7fe36a7e9222e830f4920e09a85115df0bdbf190)
This commit is contained in:
parent
2d53ec24f8
commit
06e2d5b3c3
@ -24,10 +24,7 @@ const requiresRestartKeys = [
|
|||||||
'enableSsl',
|
'enableSsl',
|
||||||
'sslPort',
|
'sslPort',
|
||||||
'sslCertPath',
|
'sslCertPath',
|
||||||
'sslCertPassword',
|
'sslCertPassword'
|
||||||
'authenticationMethod',
|
|
||||||
'username',
|
|
||||||
'password'
|
|
||||||
];
|
];
|
||||||
|
|
||||||
class GeneralSettings extends Component {
|
class GeneralSettings extends Component {
|
||||||
|
@ -86,7 +86,6 @@ class SecuritySettings extends Component {
|
|||||||
name="authenticationMethod"
|
name="authenticationMethod"
|
||||||
values={authenticationMethodOptions}
|
values={authenticationMethodOptions}
|
||||||
helpText={translate('AuthenticationMethodHelpText')}
|
helpText={translate('AuthenticationMethodHelpText')}
|
||||||
helpTextWarning={translate('RestartRequiredHelpTextWarning')}
|
|
||||||
onChange={onInputChange}
|
onChange={onInputChange}
|
||||||
{...authenticationMethod}
|
{...authenticationMethod}
|
||||||
/>
|
/>
|
||||||
@ -100,7 +99,6 @@ class SecuritySettings extends Component {
|
|||||||
<FormInputGroup
|
<FormInputGroup
|
||||||
type={inputTypes.TEXT}
|
type={inputTypes.TEXT}
|
||||||
name="username"
|
name="username"
|
||||||
helpTextWarning={translate('RestartRequiredHelpTextWarning')}
|
|
||||||
onChange={onInputChange}
|
onChange={onInputChange}
|
||||||
{...username}
|
{...username}
|
||||||
/>
|
/>
|
||||||
@ -115,7 +113,6 @@ class SecuritySettings extends Component {
|
|||||||
<FormInputGroup
|
<FormInputGroup
|
||||||
type={inputTypes.PASSWORD}
|
type={inputTypes.PASSWORD}
|
||||||
name="password"
|
name="password"
|
||||||
helpTextWarning={translate('RestartRequiredHelpTextWarning')}
|
|
||||||
onChange={onInputChange}
|
onChange={onInputChange}
|
||||||
{...password}
|
{...password}
|
||||||
/>
|
/>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
@ -20,8 +20,7 @@ public class SentryTargetFixture : TestBase
|
|||||||
|
|
||||||
private static Exception[] FilteredExceptions = new Exception[]
|
private static Exception[] FilteredExceptions = new Exception[]
|
||||||
{
|
{
|
||||||
new UnauthorizedAccessException(),
|
new UnauthorizedAccessException()
|
||||||
new TinyIoC.TinyIoCResolutionException(typeof(string))
|
|
||||||
};
|
};
|
||||||
|
|
||||||
[SetUp]
|
[SetUp]
|
||||||
|
@ -1,8 +1,13 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using DryIoc;
|
||||||
|
using DryIoc.Microsoft.DependencyInjection;
|
||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
using NzbDrone.Common.Composition.Extensions;
|
||||||
using NzbDrone.Common.EnvironmentInfo;
|
using NzbDrone.Common.EnvironmentInfo;
|
||||||
using NzbDrone.Core.Datastore;
|
using NzbDrone.Common.Instrumentation.Extensions;
|
||||||
|
using NzbDrone.Core.Datastore.Extensions;
|
||||||
using NzbDrone.Core.Lifecycle;
|
using NzbDrone.Core.Lifecycle;
|
||||||
using NzbDrone.Core.Messaging.Events;
|
using NzbDrone.Core.Messaging.Events;
|
||||||
using NzbDrone.Test.Common;
|
using NzbDrone.Test.Common;
|
||||||
@ -16,12 +21,16 @@ public class ServiceFactoryFixture : TestBase<ServiceFactory>
|
|||||||
[Test]
|
[Test]
|
||||||
public void event_handlers_should_be_unique()
|
public void event_handlers_should_be_unique()
|
||||||
{
|
{
|
||||||
var container = MainAppContainerBuilder.BuildContainer(new StartupContext());
|
var container = new Container(rules => rules.WithNzbDroneRules())
|
||||||
container.Register<IMainDatabase>(new MainDatabase(null));
|
.AddNzbDroneLogger()
|
||||||
container.Register<ILogDatabase>(new LogDatabase(null));
|
.AutoAddServices(Bootstrap.ASSEMBLIES)
|
||||||
container.Resolve<IAppFolderFactory>().Register();
|
.AddDummyDatabase()
|
||||||
|
.AddStartupContext(new StartupContext("first", "second"))
|
||||||
|
.GetServiceProvider();
|
||||||
|
|
||||||
Mocker.SetConstant(container);
|
container.GetRequiredService<IAppFolderFactory>().Register();
|
||||||
|
|
||||||
|
Mocker.SetConstant<System.IServiceProvider>(container);
|
||||||
|
|
||||||
var handlers = Subject.BuildAll<IHandle<ApplicationStartedEvent>>()
|
var handlers = Subject.BuildAll<IHandle<ApplicationStartedEvent>>()
|
||||||
.Select(c => c.GetType().FullName);
|
.Select(c => c.GetType().FullName);
|
||||||
|
87
src/NzbDrone.Common/Composition/AssemblyLoader.cs
Normal file
87
src/NzbDrone.Common/Composition/AssemblyLoader.cs
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Runtime.Loader;
|
||||||
|
using NzbDrone.Common.EnvironmentInfo;
|
||||||
|
|
||||||
|
namespace NzbDrone.Common.Composition
|
||||||
|
{
|
||||||
|
public class AssemblyLoader
|
||||||
|
{
|
||||||
|
static AssemblyLoader()
|
||||||
|
{
|
||||||
|
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(ContainerResolveEventHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<Assembly> Load(IEnumerable<string> assemblyNames)
|
||||||
|
{
|
||||||
|
var toLoad = assemblyNames.ToList();
|
||||||
|
toLoad.Add("Radarr.Common");
|
||||||
|
toLoad.Add(OsInfo.IsWindows ? "Radarr.Windows" : "Radarr.Mono");
|
||||||
|
|
||||||
|
var toRegisterResolver = new List<string> { "System.Data.SQLite" };
|
||||||
|
toRegisterResolver.AddRange(assemblyNames.Intersect(new[] { "Radarr.Core" }));
|
||||||
|
RegisterNativeResolver(toRegisterResolver);
|
||||||
|
|
||||||
|
var startupPath = AppDomain.CurrentDomain.BaseDirectory;
|
||||||
|
|
||||||
|
return toLoad.Select(x =>
|
||||||
|
AssemblyLoadContext.Default.LoadFromAssemblyPath(Path.Combine(startupPath, $"{x}.dll")));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Assembly ContainerResolveEventHandler(object sender, ResolveEventArgs args)
|
||||||
|
{
|
||||||
|
var resolver = new AssemblyDependencyResolver(args.RequestingAssembly.Location);
|
||||||
|
var assemblyPath = resolver.ResolveAssemblyToPath(new AssemblyName(args.Name));
|
||||||
|
|
||||||
|
if (assemblyPath == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return AssemblyLoadContext.Default.LoadFromAssemblyPath(assemblyPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void RegisterNativeResolver(IEnumerable<string> assemblyNames)
|
||||||
|
{
|
||||||
|
foreach (var name in assemblyNames)
|
||||||
|
{
|
||||||
|
// This ensures we look for sqlite3 using libsqlite3.so.0 on Linux and not libsqlite3.so which
|
||||||
|
// is less likely to exist.
|
||||||
|
var assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(
|
||||||
|
Path.Combine(AppDomain.CurrentDomain.BaseDirectory, $"{name}.dll"));
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
NativeLibrary.SetDllImportResolver(assembly, LoadNativeLib);
|
||||||
|
}
|
||||||
|
catch (InvalidOperationException)
|
||||||
|
{
|
||||||
|
// This can only be set once per assembly
|
||||||
|
// Catch required for NzbDrone.Host tests
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IntPtr LoadNativeLib(string libraryName, Assembly assembly, DllImportSearchPath? dllImportSearchPath)
|
||||||
|
{
|
||||||
|
var mappedName = libraryName;
|
||||||
|
if (OsInfo.IsLinux)
|
||||||
|
{
|
||||||
|
if (libraryName == "sqlite3")
|
||||||
|
{
|
||||||
|
mappedName = "libsqlite3.so.0";
|
||||||
|
}
|
||||||
|
else if (libraryName == "mediainfo")
|
||||||
|
{
|
||||||
|
mappedName = "libmediainfo.so.0";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NativeLibrary.Load(mappedName, assembly, dllImportSearchPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,102 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using TinyIoC;
|
|
||||||
|
|
||||||
namespace NzbDrone.Common.Composition
|
|
||||||
{
|
|
||||||
public class Container : IContainer
|
|
||||||
{
|
|
||||||
private readonly TinyIoCContainer _container;
|
|
||||||
private readonly List<Type> _loadedTypes;
|
|
||||||
|
|
||||||
public Container(TinyIoCContainer container, List<Type> loadedTypes)
|
|
||||||
{
|
|
||||||
_container = container;
|
|
||||||
_loadedTypes = loadedTypes;
|
|
||||||
_container.Register<IContainer>(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Register<TService, TImplementation>()
|
|
||||||
where TImplementation : class, TService
|
|
||||||
where TService : class
|
|
||||||
{
|
|
||||||
_container.Register<TService, TImplementation>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Register<T>(T instance)
|
|
||||||
where T : class
|
|
||||||
{
|
|
||||||
_container.Register<T>(instance);
|
|
||||||
}
|
|
||||||
|
|
||||||
public T Resolve<T>()
|
|
||||||
where T : class
|
|
||||||
{
|
|
||||||
return _container.Resolve<T>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public object Resolve(Type type)
|
|
||||||
{
|
|
||||||
return _container.Resolve(type);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Register(Type serviceType, Type implementationType)
|
|
||||||
{
|
|
||||||
_container.Register(serviceType, implementationType);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Register<TService>(Func<IContainer, TService> factory)
|
|
||||||
where TService : class
|
|
||||||
{
|
|
||||||
_container.Register((c, n) => factory(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RegisterSingleton(Type service, Type implementation)
|
|
||||||
{
|
|
||||||
var factory = CreateSingletonImplementationFactory(implementation);
|
|
||||||
|
|
||||||
_container.Register(service, factory);
|
|
||||||
_container.Register(service, factory, implementation.FullName);
|
|
||||||
}
|
|
||||||
|
|
||||||
public IEnumerable<T> ResolveAll<T>()
|
|
||||||
where T : class
|
|
||||||
{
|
|
||||||
return _container.ResolveAll<T>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RegisterAllAsSingleton(Type service, IEnumerable<Type> implementationList)
|
|
||||||
{
|
|
||||||
foreach (var implementation in implementationList)
|
|
||||||
{
|
|
||||||
var factory = CreateSingletonImplementationFactory(implementation);
|
|
||||||
|
|
||||||
_container.Register(service, factory, implementation.FullName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Func<TinyIoCContainer, NamedParameterOverloads, object> CreateSingletonImplementationFactory(Type implementation)
|
|
||||||
{
|
|
||||||
const string singleImplPrefix = "singleImpl_";
|
|
||||||
|
|
||||||
_container.Register(implementation, implementation, singleImplPrefix + implementation.FullName).AsSingleton();
|
|
||||||
|
|
||||||
return (c, p) => _container.Resolve(implementation, singleImplPrefix + implementation.FullName);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsTypeRegistered(Type type)
|
|
||||||
{
|
|
||||||
return _container.CanResolve(type);
|
|
||||||
}
|
|
||||||
|
|
||||||
public IEnumerable<Type> GetImplementations(Type contractType)
|
|
||||||
{
|
|
||||||
return _loadedTypes
|
|
||||||
.Where(implementation =>
|
|
||||||
contractType.IsAssignableFrom(implementation) &&
|
|
||||||
!implementation.IsInterface &&
|
|
||||||
!implementation.IsAbstract);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,136 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Reflection;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.Runtime.Loader;
|
|
||||||
using NzbDrone.Common.EnvironmentInfo;
|
|
||||||
using NzbDrone.Common.Messaging;
|
|
||||||
using TinyIoC;
|
|
||||||
|
|
||||||
namespace NzbDrone.Common.Composition
|
|
||||||
{
|
|
||||||
public abstract class ContainerBuilderBase
|
|
||||||
{
|
|
||||||
private readonly List<Type> _loadedTypes;
|
|
||||||
|
|
||||||
protected IContainer Container { get; }
|
|
||||||
|
|
||||||
protected ContainerBuilderBase(IStartupContext args, List<string> assemblies)
|
|
||||||
{
|
|
||||||
_loadedTypes = new List<Type>();
|
|
||||||
|
|
||||||
assemblies.Add(OsInfo.IsWindows ? "Radarr.Windows" : "Radarr.Mono");
|
|
||||||
assemblies.Add("Radarr.Common");
|
|
||||||
|
|
||||||
var startupPath = AppDomain.CurrentDomain.BaseDirectory;
|
|
||||||
|
|
||||||
foreach (var assemblyName in assemblies)
|
|
||||||
{
|
|
||||||
_loadedTypes.AddRange(AssemblyLoadContext.Default.LoadFromAssemblyPath(Path.Combine(startupPath, $"{assemblyName}.dll")).GetExportedTypes());
|
|
||||||
}
|
|
||||||
|
|
||||||
var toRegisterResolver = new List<string> { "System.Data.SQLite" };
|
|
||||||
toRegisterResolver.AddRange(assemblies.Intersect(new[] { "Radarr.Core" }));
|
|
||||||
RegisterNativeResolver(toRegisterResolver);
|
|
||||||
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(ContainerResolveEventHandler);
|
|
||||||
|
|
||||||
Container = new Container(new TinyIoCContainer(), _loadedTypes);
|
|
||||||
AutoRegisterInterfaces();
|
|
||||||
Container.Register(args);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Assembly ContainerResolveEventHandler(object sender, ResolveEventArgs args)
|
|
||||||
{
|
|
||||||
var resolver = new AssemblyDependencyResolver(args.RequestingAssembly.Location);
|
|
||||||
var assemblyPath = resolver.ResolveAssemblyToPath(new AssemblyName(args.Name));
|
|
||||||
|
|
||||||
if (assemblyPath == null)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return AssemblyLoadContext.Default.LoadFromAssemblyPath(assemblyPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void RegisterNativeResolver(IEnumerable<string> assemblyNames)
|
|
||||||
{
|
|
||||||
// This ensures we look for sqlite3 using libsqlite3.so.0 on Linux and not libsqlite3.so which
|
|
||||||
// is less likely to exist.
|
|
||||||
foreach (var name in assemblyNames)
|
|
||||||
{
|
|
||||||
var assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(
|
|
||||||
Path.Combine(AppDomain.CurrentDomain.BaseDirectory, $"{name}.dll"));
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
NativeLibrary.SetDllImportResolver(assembly, LoadNativeLib);
|
|
||||||
}
|
|
||||||
catch (InvalidOperationException)
|
|
||||||
{
|
|
||||||
// This can only be set once per assembly
|
|
||||||
// Catch required for NzbDrone.Host tests
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static IntPtr LoadNativeLib(string libraryName, Assembly assembly, DllImportSearchPath? dllImportSearchPath)
|
|
||||||
{
|
|
||||||
var mappedName = libraryName;
|
|
||||||
if (OsInfo.IsLinux)
|
|
||||||
{
|
|
||||||
if (libraryName == "sqlite3")
|
|
||||||
{
|
|
||||||
mappedName = "libsqlite3.so.0";
|
|
||||||
}
|
|
||||||
else if (libraryName == "mediainfo")
|
|
||||||
{
|
|
||||||
mappedName = "libmediainfo.so.0";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return NativeLibrary.Load(mappedName, assembly, dllImportSearchPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void AutoRegisterInterfaces()
|
|
||||||
{
|
|
||||||
var loadedInterfaces = _loadedTypes.Where(t => t.IsInterface).ToList();
|
|
||||||
var implementedInterfaces = _loadedTypes.SelectMany(t => t.GetInterfaces());
|
|
||||||
|
|
||||||
var contracts = loadedInterfaces.Union(implementedInterfaces).Where(c => !c.IsGenericTypeDefinition && !string.IsNullOrWhiteSpace(c.FullName))
|
|
||||||
.Where(c => !c.FullName.StartsWith("System"))
|
|
||||||
.Except(new List<Type> { typeof(IMessage), typeof(IEvent), typeof(IContainer) }).Distinct().OrderBy(c => c.FullName);
|
|
||||||
|
|
||||||
foreach (var contract in contracts)
|
|
||||||
{
|
|
||||||
AutoRegisterImplementations(contract);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void AutoRegisterImplementations<TContract>()
|
|
||||||
{
|
|
||||||
AutoRegisterImplementations(typeof(TContract));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void AutoRegisterImplementations(Type contractType)
|
|
||||||
{
|
|
||||||
var implementations = Container.GetImplementations(contractType).Where(c => !c.IsGenericTypeDefinition).ToList();
|
|
||||||
|
|
||||||
if (implementations.Count == 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (implementations.Count == 1)
|
|
||||||
{
|
|
||||||
var impl = implementations.Single();
|
|
||||||
Container.RegisterSingleton(contractType, impl);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Container.RegisterAllAsSingleton(contractType, implementations);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
37
src/NzbDrone.Common/Composition/Extensions.cs
Normal file
37
src/NzbDrone.Common/Composition/Extensions.cs
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using DryIoc;
|
||||||
|
using NzbDrone.Common.EnvironmentInfo;
|
||||||
|
|
||||||
|
namespace NzbDrone.Common.Composition.Extensions
|
||||||
|
{
|
||||||
|
public static class ServiceCollectionExtensions
|
||||||
|
{
|
||||||
|
public static Rules WithNzbDroneRules(this Rules rules)
|
||||||
|
{
|
||||||
|
return rules.WithMicrosoftDependencyInjectionRules()
|
||||||
|
.WithAutoConcreteTypeResolution()
|
||||||
|
.WithDefaultReuse(Reuse.Singleton);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IContainer AddStartupContext(this IContainer container, StartupContext context)
|
||||||
|
{
|
||||||
|
container.RegisterInstance<IStartupContext>(context, ifAlreadyRegistered: IfAlreadyRegistered.Replace);
|
||||||
|
return container;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IContainer AutoAddServices(this IContainer container, List<string> assemblyNames)
|
||||||
|
{
|
||||||
|
var assemblies = AssemblyLoader.Load(assemblyNames);
|
||||||
|
|
||||||
|
container.RegisterMany(assemblies,
|
||||||
|
serviceTypeCondition: type => type.IsInterface && !string.IsNullOrWhiteSpace(type.FullName) && !type.FullName.StartsWith("System"),
|
||||||
|
reuse: Reuse.Singleton);
|
||||||
|
|
||||||
|
var knownTypes = new KnownTypes(assemblies.SelectMany(x => x.GetTypes()).ToList());
|
||||||
|
container.RegisterInstance(knownTypes);
|
||||||
|
|
||||||
|
return container;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,28 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace NzbDrone.Common.Composition
|
|
||||||
{
|
|
||||||
public interface IContainer
|
|
||||||
{
|
|
||||||
void Register<T>(T instance)
|
|
||||||
where T : class;
|
|
||||||
|
|
||||||
void Register<TService, TImplementation>()
|
|
||||||
where TImplementation : class, TService
|
|
||||||
where TService : class;
|
|
||||||
T Resolve<T>()
|
|
||||||
where T : class;
|
|
||||||
object Resolve(Type type);
|
|
||||||
void Register(Type serviceType, Type implementationType);
|
|
||||||
void Register<TService>(Func<IContainer, TService> factory)
|
|
||||||
where TService : class;
|
|
||||||
void RegisterSingleton(Type service, Type implementation);
|
|
||||||
IEnumerable<T> ResolveAll<T>()
|
|
||||||
where T : class;
|
|
||||||
void RegisterAllAsSingleton(Type registrationType, IEnumerable<Type> implementationList);
|
|
||||||
bool IsTypeRegistered(Type type);
|
|
||||||
|
|
||||||
IEnumerable<Type> GetImplementations(Type contractType);
|
|
||||||
}
|
|
||||||
}
|
|
31
src/NzbDrone.Common/Composition/KnownTypes.cs
Normal file
31
src/NzbDrone.Common/Composition/KnownTypes.cs
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace NzbDrone.Common.Composition
|
||||||
|
{
|
||||||
|
public class KnownTypes
|
||||||
|
{
|
||||||
|
private List<Type> _knownTypes;
|
||||||
|
|
||||||
|
// So unity can resolve for tests
|
||||||
|
public KnownTypes()
|
||||||
|
: this(new List<Type>())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public KnownTypes(List<Type> loadedTypes)
|
||||||
|
{
|
||||||
|
_knownTypes = loadedTypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<Type> GetImplementations(Type contractType)
|
||||||
|
{
|
||||||
|
return _knownTypes
|
||||||
|
.Where(implementation =>
|
||||||
|
contractType.IsAssignableFrom(implementation) &&
|
||||||
|
!implementation.IsInterface &&
|
||||||
|
!implementation.IsAbstract);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
using DryIoc;
|
||||||
|
using NLog;
|
||||||
|
|
||||||
|
namespace NzbDrone.Common.Instrumentation.Extensions
|
||||||
|
{
|
||||||
|
public static class CompositionExtensions
|
||||||
|
{
|
||||||
|
public static IContainer AddNzbDroneLogger(this IContainer container)
|
||||||
|
{
|
||||||
|
container.Register(Made.Of<Logger>(() => LogManager.GetLogger(Arg.Index<string>(0)), r => r.Parent.ImplementationType.Name.ToString()), reuse: Reuse.Transient);
|
||||||
|
return container;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -42,10 +42,7 @@ public class SentryTarget : TargetWithLayout
|
|||||||
"UnauthorizedAccessException",
|
"UnauthorizedAccessException",
|
||||||
|
|
||||||
// Filter out people stuck in boot loops
|
// Filter out people stuck in boot loops
|
||||||
"CorruptDatabaseException",
|
"CorruptDatabaseException"
|
||||||
|
|
||||||
// This also filters some people in boot loops
|
|
||||||
"TinyIoCResolutionException"
|
|
||||||
};
|
};
|
||||||
|
|
||||||
public static readonly List<string> FilteredExceptionMessages = new List<string>
|
public static readonly List<string> FilteredExceptionMessages = new List<string>
|
||||||
|
@ -4,9 +4,12 @@
|
|||||||
<DefineConstants Condition="'$(RuntimeIdentifier)' == 'linux-musl-x64' or '$(RuntimeIdentifier)' == 'linux-musl-arm64'">ISMUSL</DefineConstants>
|
<DefineConstants Condition="'$(RuntimeIdentifier)' == 'linux-musl-x64' or '$(RuntimeIdentifier)' == 'linux-musl-arm64'">ISMUSL</DefineConstants>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="DryIoc.dll" Version="4.7.4" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="5.0.1" />
|
||||||
<PackageReference Include="DotNet4.SocksProxy" Version="1.4.0.1" />
|
<PackageReference Include="DotNet4.SocksProxy" Version="1.4.0.1" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||||
<PackageReference Include="NLog" Version="4.7.0" />
|
<PackageReference Include="NLog" Version="4.7.11" />
|
||||||
|
<PackageReference Include="NLog.Extensions.Logging" Version="1.7.4" />
|
||||||
<PackageReference Include="Sentry" Version="3.9.3" />
|
<PackageReference Include="Sentry" Version="3.9.3" />
|
||||||
<PackageReference Include="SharpZipLib" Version="1.2.0" />
|
<PackageReference Include="SharpZipLib" Version="1.2.0" />
|
||||||
<PackageReference Include="System.Text.Json" Version="5.0.2" />
|
<PackageReference Include="System.Text.Json" Version="5.0.2" />
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NzbDrone.Common.Composition;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
namespace NzbDrone.Common
|
namespace NzbDrone.Common
|
||||||
{
|
{
|
||||||
@ -17,9 +17,9 @@ IEnumerable<T> BuildAll<T>()
|
|||||||
|
|
||||||
public class ServiceFactory : IServiceFactory
|
public class ServiceFactory : IServiceFactory
|
||||||
{
|
{
|
||||||
private readonly IContainer _container;
|
private readonly System.IServiceProvider _container;
|
||||||
|
|
||||||
public ServiceFactory(IContainer container)
|
public ServiceFactory(System.IServiceProvider container)
|
||||||
{
|
{
|
||||||
_container = container;
|
_container = container;
|
||||||
}
|
}
|
||||||
@ -27,23 +27,23 @@ public ServiceFactory(IContainer container)
|
|||||||
public T Build<T>()
|
public T Build<T>()
|
||||||
where T : class
|
where T : class
|
||||||
{
|
{
|
||||||
return _container.Resolve<T>();
|
return _container.GetRequiredService<T>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<T> BuildAll<T>()
|
public IEnumerable<T> BuildAll<T>()
|
||||||
where T : class
|
where T : class
|
||||||
{
|
{
|
||||||
return _container.ResolveAll<T>().GroupBy(c => c.GetType().FullName).Select(g => g.First());
|
return _container.GetServices<T>().GroupBy(c => c.GetType().FullName).Select(g => g.First());
|
||||||
}
|
}
|
||||||
|
|
||||||
public object Build(Type contract)
|
public object Build(Type contract)
|
||||||
{
|
{
|
||||||
return _container.Resolve(contract);
|
return _container.GetRequiredService(contract);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<Type> GetImplementations(Type contract)
|
public IEnumerable<Type> GetImplementations(Type contract)
|
||||||
{
|
{
|
||||||
return _container.GetImplementations(contract);
|
return _container.GetServices(contract).Select(x => x.GetType());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,15 +0,0 @@
|
|||||||
using Radarr.Host;
|
|
||||||
|
|
||||||
namespace NzbDrone.Console
|
|
||||||
{
|
|
||||||
public class ConsoleAlerts : IUserAlert
|
|
||||||
{
|
|
||||||
public void Alert(string message)
|
|
||||||
{
|
|
||||||
System.Console.WriteLine();
|
|
||||||
System.Console.WriteLine(message);
|
|
||||||
System.Console.WriteLine("Press enter to continue");
|
|
||||||
System.Console.ReadLine();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -2,6 +2,8 @@
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
using Microsoft.AspNetCore.Connections;
|
using Microsoft.AspNetCore.Connections;
|
||||||
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
using NLog;
|
using NLog;
|
||||||
using NzbDrone.Common.EnvironmentInfo;
|
using NzbDrone.Common.EnvironmentInfo;
|
||||||
using NzbDrone.Common.Exceptions;
|
using NzbDrone.Common.Exceptions;
|
||||||
@ -15,7 +17,7 @@ public static class ConsoleApp
|
|||||||
{
|
{
|
||||||
private static readonly Logger Logger = NzbDroneLogger.GetLogger(typeof(ConsoleApp));
|
private static readonly Logger Logger = NzbDroneLogger.GetLogger(typeof(ConsoleApp));
|
||||||
|
|
||||||
private enum ExitCodes : int
|
private enum ExitCodes
|
||||||
{
|
{
|
||||||
Normal = 0,
|
Normal = 0,
|
||||||
UnknownFailure = 1,
|
UnknownFailure = 1,
|
||||||
@ -40,7 +42,7 @@ public static void Main(string[] args)
|
|||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
|
||||||
Bootstrap.Start(startupArgs, new ConsoleAlerts());
|
Bootstrap.Start(args);
|
||||||
}
|
}
|
||||||
catch (RadarrStartupException ex)
|
catch (RadarrStartupException ex)
|
||||||
{
|
{
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
using System.Data;
|
using System.Data;
|
||||||
using FluentMigrator;
|
using FluentMigrator;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
using NLog.Extensions.Logging;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using NzbDrone.Core.Datastore.Migration.Framework;
|
using NzbDrone.Core.Datastore.Migration.Framework;
|
||||||
|
|
||||||
@ -32,7 +33,7 @@ protected virtual IDbConnection WithDapperMigrationTestDb(Action<TMigration> bef
|
|||||||
|
|
||||||
protected override void SetupLogging()
|
protected override void SetupLogging()
|
||||||
{
|
{
|
||||||
Mocker.SetConstant<ILoggerProvider>(Mocker.Resolve<MigrationLoggerProvider>());
|
Mocker.SetConstant<ILoggerProvider>(Mocker.Resolve<NLogLoggerProvider>());
|
||||||
}
|
}
|
||||||
|
|
||||||
private ITestDatabase WithMigrationAction(Action<TMigration> beforeMigration = null)
|
private ITestDatabase WithMigrationAction(Action<TMigration> beforeMigration = null)
|
||||||
|
@ -39,7 +39,7 @@ public override void Setup()
|
|||||||
{
|
{
|
||||||
if (_customSubject == null)
|
if (_customSubject == null)
|
||||||
{
|
{
|
||||||
_customSubject = new AugmentWithHistory(Mocker.GetMock<IIndexerFactory>().Object, new List<IAugmentParsedMovieInfo> { Mocker.Resolve<AugmentWithReleaseInfo>() });
|
_customSubject = new AugmentWithHistory(new List<Lazy<IAugmentParsedMovieInfo>> { new (Mocker.Resolve<AugmentWithReleaseInfo>()) });
|
||||||
}
|
}
|
||||||
|
|
||||||
return _customSubject;
|
return _customSubject;
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Data.SQLite;
|
using System.Data.SQLite;
|
||||||
using NLog;
|
using NLog;
|
||||||
using NzbDrone.Common.Composition;
|
|
||||||
using NzbDrone.Common.Disk;
|
using NzbDrone.Common.Disk;
|
||||||
using NzbDrone.Common.EnvironmentInfo;
|
using NzbDrone.Common.EnvironmentInfo;
|
||||||
using NzbDrone.Common.Exceptions;
|
using NzbDrone.Common.Exceptions;
|
||||||
@ -40,17 +39,6 @@ private static void InitializeEnvironment()
|
|||||||
Environment.SetEnvironmentVariable("No_SQLiteFunctions", "true");
|
Environment.SetEnvironmentVariable("No_SQLiteFunctions", "true");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void RegisterDatabase(IContainer container)
|
|
||||||
{
|
|
||||||
var mainDb = new MainDatabase(container.Resolve<IDbFactory>().Create());
|
|
||||||
|
|
||||||
container.Register<IMainDatabase>(mainDb);
|
|
||||||
|
|
||||||
var logDb = new LogDatabase(container.Resolve<IDbFactory>().Create(MigrationType.Log));
|
|
||||||
|
|
||||||
container.Register<ILogDatabase>(logDb);
|
|
||||||
}
|
|
||||||
|
|
||||||
public DbFactory(IMigrationController migrationController,
|
public DbFactory(IMigrationController migrationController,
|
||||||
IConnectionStringFactory connectionStringFactory,
|
IConnectionStringFactory connectionStringFactory,
|
||||||
IDiskProvider diskProvider,
|
IDiskProvider diskProvider,
|
||||||
|
@ -0,0 +1,24 @@
|
|||||||
|
using DryIoc;
|
||||||
|
using NzbDrone.Core.Datastore.Migration.Framework;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Datastore.Extensions
|
||||||
|
{
|
||||||
|
public static class CompositionExtensions
|
||||||
|
{
|
||||||
|
public static IContainer AddDatabase(this IContainer container)
|
||||||
|
{
|
||||||
|
container.RegisterDelegate<IDbFactory, IMainDatabase>(f => new MainDatabase(f.Create()), Reuse.Singleton);
|
||||||
|
container.RegisterDelegate<IDbFactory, ILogDatabase>(f => new LogDatabase(f.Create(MigrationType.Log)), Reuse.Singleton);
|
||||||
|
|
||||||
|
return container;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IContainer AddDummyDatabase(this IContainer container)
|
||||||
|
{
|
||||||
|
container.RegisterInstance<IMainDatabase>(new MainDatabase(null));
|
||||||
|
container.RegisterInstance<ILogDatabase>(new LogDatabase(null));
|
||||||
|
|
||||||
|
return container;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using FluentMigrator.Runner;
|
using FluentMigrator.Runner;
|
||||||
@ -7,6 +7,7 @@
|
|||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using NLog;
|
using NLog;
|
||||||
|
using NLog.Extensions.Logging;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Datastore.Migration.Framework
|
namespace NzbDrone.Core.Datastore.Migration.Framework
|
||||||
{
|
{
|
||||||
@ -34,7 +35,7 @@ public void Migrate(string connectionString, MigrationContext migrationContext)
|
|||||||
_logger.Info("*** Migrating {0} ***", connectionString);
|
_logger.Info("*** Migrating {0} ***", connectionString);
|
||||||
|
|
||||||
var serviceProvider = new ServiceCollection()
|
var serviceProvider = new ServiceCollection()
|
||||||
.AddLogging(lb => lb.AddProvider(_migrationLoggerProvider))
|
.AddLogging(b => b.AddNLog())
|
||||||
.AddFluentMigratorCore()
|
.AddFluentMigratorCore()
|
||||||
.ConfigureRunner(
|
.ConfigureRunner(
|
||||||
builder => builder
|
builder => builder
|
||||||
|
@ -1,59 +0,0 @@
|
|||||||
using System;
|
|
||||||
using FluentMigrator.Runner;
|
|
||||||
using FluentMigrator.Runner.Logging;
|
|
||||||
using NLog;
|
|
||||||
|
|
||||||
namespace NzbDrone.Core.Datastore.Migration.Framework
|
|
||||||
{
|
|
||||||
public class MigrationLogger : FluentMigratorLogger
|
|
||||||
{
|
|
||||||
private readonly Logger _logger;
|
|
||||||
|
|
||||||
public MigrationLogger(Logger logger,
|
|
||||||
FluentMigratorLoggerOptions options)
|
|
||||||
: base(options)
|
|
||||||
{
|
|
||||||
_logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void WriteHeading(string message)
|
|
||||||
{
|
|
||||||
_logger.Info("*** {0} ***", message);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void WriteSay(string message)
|
|
||||||
{
|
|
||||||
_logger.Debug(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void WriteEmphasize(string message)
|
|
||||||
{
|
|
||||||
_logger.Warn(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void WriteSql(string sql)
|
|
||||||
{
|
|
||||||
_logger.Debug(sql);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void WriteEmptySql()
|
|
||||||
{
|
|
||||||
_logger.Debug(@"No SQL statement executed.");
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void WriteElapsedTime(TimeSpan timeSpan)
|
|
||||||
{
|
|
||||||
_logger.Debug("Took: {0}", timeSpan);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void WriteError(string message)
|
|
||||||
{
|
|
||||||
_logger.Error(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void WriteError(Exception exception)
|
|
||||||
{
|
|
||||||
_logger.Error(exception);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,26 +0,0 @@
|
|||||||
using FluentMigrator.Runner;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using NLog;
|
|
||||||
using ILogger = Microsoft.Extensions.Logging.ILogger;
|
|
||||||
|
|
||||||
namespace NzbDrone.Core.Datastore.Migration.Framework
|
|
||||||
{
|
|
||||||
public class MigrationLoggerProvider : ILoggerProvider
|
|
||||||
{
|
|
||||||
private readonly Logger _logger;
|
|
||||||
|
|
||||||
public MigrationLoggerProvider(Logger logger)
|
|
||||||
{
|
|
||||||
_logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ILogger CreateLogger(string categoryName)
|
|
||||||
{
|
|
||||||
return new MigrationLogger(_logger, new FluentMigratorLoggerOptions() { ShowElapsedTime = true, ShowSql = true });
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,8 +1,8 @@
|
|||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using FluentValidation.Results;
|
using FluentValidation.Results;
|
||||||
using NLog;
|
using NLog;
|
||||||
using NzbDrone.Common.Composition;
|
|
||||||
using NzbDrone.Core.Messaging.Events;
|
using NzbDrone.Core.Messaging.Events;
|
||||||
using NzbDrone.Core.ThingiProvider;
|
using NzbDrone.Core.ThingiProvider;
|
||||||
|
|
||||||
@ -21,7 +21,7 @@ public class DownloadClientFactory : ProviderFactory<IDownloadClient, DownloadCl
|
|||||||
public DownloadClientFactory(IDownloadClientStatusService downloadClientStatusService,
|
public DownloadClientFactory(IDownloadClientStatusService downloadClientStatusService,
|
||||||
IDownloadClientRepository providerRepository,
|
IDownloadClientRepository providerRepository,
|
||||||
IEnumerable<IDownloadClient> providers,
|
IEnumerable<IDownloadClient> providers,
|
||||||
IContainer container,
|
IServiceProvider container,
|
||||||
IEventAggregator eventAggregator,
|
IEventAggregator eventAggregator,
|
||||||
Logger logger)
|
Logger logger)
|
||||||
: base(providerRepository, providers, container, eventAggregator, logger)
|
: base(providerRepository, providers, container, eventAggregator, logger)
|
||||||
|
@ -19,7 +19,7 @@ public class ExistingExtraFileService : IHandle<MovieScannedEvent>
|
|||||||
|
|
||||||
public ExistingExtraFileService(IDiskProvider diskProvider,
|
public ExistingExtraFileService(IDiskProvider diskProvider,
|
||||||
IDiskScanService diskScanService,
|
IDiskScanService diskScanService,
|
||||||
List<IImportExistingExtraFiles> existingExtraFileImporters,
|
IEnumerable<IImportExistingExtraFiles> existingExtraFileImporters,
|
||||||
Logger logger)
|
Logger logger)
|
||||||
{
|
{
|
||||||
_diskProvider = diskProvider;
|
_diskProvider = diskProvider;
|
||||||
|
@ -37,7 +37,7 @@ public ExtraService(IMediaFileService mediaFileService,
|
|||||||
IMovieService movieService,
|
IMovieService movieService,
|
||||||
IDiskProvider diskProvider,
|
IDiskProvider diskProvider,
|
||||||
IConfigService configService,
|
IConfigService configService,
|
||||||
List<IManageExtraFiles> extraFileManagers,
|
IEnumerable<IManageExtraFiles> extraFileManagers,
|
||||||
Logger logger)
|
Logger logger)
|
||||||
{
|
{
|
||||||
_mediaFileService = mediaFileService;
|
_mediaFileService = mediaFileService;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NLog;
|
using NLog;
|
||||||
@ -17,7 +17,7 @@ public class MetadataFactory : ProviderFactory<IMetadata, MetadataDefinition>, I
|
|||||||
{
|
{
|
||||||
private readonly IMetadataRepository _providerRepository;
|
private readonly IMetadataRepository _providerRepository;
|
||||||
|
|
||||||
public MetadataFactory(IMetadataRepository providerRepository, IEnumerable<IMetadata> providers, IContainer container, IEventAggregator eventAggregator, Logger logger)
|
public MetadataFactory(IMetadataRepository providerRepository, IEnumerable<IMetadata> providers, IServiceProvider container, IEventAggregator eventAggregator, Logger logger)
|
||||||
: base(providerRepository, providers, container, eventAggregator, logger)
|
: base(providerRepository, providers, container, eventAggregator, logger)
|
||||||
{
|
{
|
||||||
_providerRepository = providerRepository;
|
_providerRepository = providerRepository;
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NLog;
|
using NLog;
|
||||||
@ -20,7 +21,7 @@ public class ImportListFactory : ProviderFactory<IImportList, ImportListDefiniti
|
|||||||
|
|
||||||
public ImportListFactory(IImportListRepository providerRepository,
|
public ImportListFactory(IImportListRepository providerRepository,
|
||||||
IEnumerable<IImportList> providers,
|
IEnumerable<IImportList> providers,
|
||||||
IContainer container,
|
IServiceProvider container,
|
||||||
IEventAggregator eventAggregator,
|
IEventAggregator eventAggregator,
|
||||||
Logger logger)
|
Logger logger)
|
||||||
: base(providerRepository, providers, container, eventAggregator, logger)
|
: base(providerRepository, providers, container, eventAggregator, logger)
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using FluentValidation.Results;
|
using FluentValidation.Results;
|
||||||
@ -23,7 +24,7 @@ public class IndexerFactory : ProviderFactory<IIndexer, IndexerDefinition>, IInd
|
|||||||
public IndexerFactory(IIndexerStatusService indexerStatusService,
|
public IndexerFactory(IIndexerStatusService indexerStatusService,
|
||||||
IIndexerRepository providerRepository,
|
IIndexerRepository providerRepository,
|
||||||
IEnumerable<IIndexer> providers,
|
IEnumerable<IIndexer> providers,
|
||||||
IContainer container,
|
IServiceProvider container,
|
||||||
IEventAggregator eventAggregator,
|
IEventAggregator eventAggregator,
|
||||||
Logger logger)
|
Logger logger)
|
||||||
: base(providerRepository, providers, container, eventAggregator, logger)
|
: base(providerRepository, providers, container, eventAggregator, logger)
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using NLog;
|
using NLog;
|
||||||
using NzbDrone.Common;
|
using NzbDrone.Common;
|
||||||
|
using NzbDrone.Common.Composition;
|
||||||
using NzbDrone.Common.EnsureThat;
|
using NzbDrone.Common.EnsureThat;
|
||||||
using NzbDrone.Common.Serializer;
|
using NzbDrone.Common.Serializer;
|
||||||
using NzbDrone.Core.Exceptions;
|
using NzbDrone.Core.Exceptions;
|
||||||
@ -36,17 +37,18 @@ CommandModel Push<TCommand>(TCommand command, CommandPriority priority = Command
|
|||||||
public class CommandQueueManager : IManageCommandQueue, IHandle<ApplicationStartedEvent>
|
public class CommandQueueManager : IManageCommandQueue, IHandle<ApplicationStartedEvent>
|
||||||
{
|
{
|
||||||
private readonly ICommandRepository _repo;
|
private readonly ICommandRepository _repo;
|
||||||
private readonly IServiceFactory _serviceFactory;
|
private readonly KnownTypes _knownTypes;
|
||||||
private readonly Logger _logger;
|
private readonly Logger _logger;
|
||||||
|
|
||||||
private readonly CommandQueue _commandQueue;
|
private readonly CommandQueue _commandQueue;
|
||||||
|
|
||||||
public CommandQueueManager(ICommandRepository repo,
|
public CommandQueueManager(ICommandRepository repo,
|
||||||
IServiceFactory serviceFactory,
|
IServiceFactory serviceFactory,
|
||||||
|
KnownTypes knownTypes,
|
||||||
Logger logger)
|
Logger logger)
|
||||||
{
|
{
|
||||||
_repo = repo;
|
_repo = repo;
|
||||||
_serviceFactory = serviceFactory;
|
_knownTypes = knownTypes;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
|
|
||||||
_commandQueue = new CommandQueue();
|
_commandQueue = new CommandQueue();
|
||||||
@ -233,9 +235,8 @@ public void CleanCommands()
|
|||||||
private dynamic GetCommand(string commandName)
|
private dynamic GetCommand(string commandName)
|
||||||
{
|
{
|
||||||
commandName = commandName.Split('.').Last();
|
commandName = commandName.Split('.').Last();
|
||||||
|
var commands = _knownTypes.GetImplementations(typeof(Command));
|
||||||
var commandType = _serviceFactory.GetImplementations(typeof(Command))
|
var commandType = commands.Single(c => c.Name.Equals(commandName, StringComparison.InvariantCultureIgnoreCase));
|
||||||
.Single(c => c.Name.Equals(commandName, StringComparison.InvariantCultureIgnoreCase));
|
|
||||||
|
|
||||||
return Json.Deserialize("{}", commandType);
|
return Json.Deserialize("{}", commandType);
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NLog;
|
using NLog;
|
||||||
@ -20,7 +21,7 @@ public interface INotificationFactory : IProviderFactory<INotification, Notifica
|
|||||||
|
|
||||||
public class NotificationFactory : ProviderFactory<INotification, NotificationDefinition>, INotificationFactory
|
public class NotificationFactory : ProviderFactory<INotification, NotificationDefinition>, INotificationFactory
|
||||||
{
|
{
|
||||||
public NotificationFactory(INotificationRepository providerRepository, IEnumerable<INotification> providers, IContainer container, IEventAggregator eventAggregator, Logger logger)
|
public NotificationFactory(INotificationRepository providerRepository, IEnumerable<INotification> providers, IServiceProvider container, IEventAggregator eventAggregator, Logger logger)
|
||||||
: base(providerRepository, providers, container, eventAggregator, logger)
|
: base(providerRepository, providers, container, eventAggregator, logger)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -1,21 +1,17 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NzbDrone.Common.Extensions;
|
|
||||||
using NzbDrone.Core.History;
|
using NzbDrone.Core.History;
|
||||||
using NzbDrone.Core.Indexers;
|
|
||||||
using NzbDrone.Core.Parser.Model;
|
using NzbDrone.Core.Parser.Model;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Parser.Augmenters
|
namespace NzbDrone.Core.Parser.Augmenters
|
||||||
{
|
{
|
||||||
public class AugmentWithHistory : IAugmentParsedMovieInfo
|
public class AugmentWithHistory : IAugmentParsedMovieInfo
|
||||||
{
|
{
|
||||||
private readonly IIndexerFactory _indexerFactory;
|
private readonly IEnumerable<Lazy<IAugmentParsedMovieInfo>> _augmenters;
|
||||||
private readonly IEnumerable<IAugmentParsedMovieInfo> _augmenters;
|
|
||||||
|
|
||||||
public AugmentWithHistory(IIndexerFactory indexerFactory, IEnumerable<IAugmentParsedMovieInfo> augmenters)
|
public AugmentWithHistory(IEnumerable<Lazy<IAugmentParsedMovieInfo>> augmenters)
|
||||||
{
|
{
|
||||||
_indexerFactory = indexerFactory;
|
|
||||||
_augmenters = augmenters;
|
_augmenters = augmenters;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,7 +32,6 @@ public ParsedMovieInfo AugmentMovieInfo(ParsedMovieInfo movieInfo, object helper
|
|||||||
|
|
||||||
if (int.TryParse(history.Data.GetValueOrDefault("indexerId"), out var indexerId))
|
if (int.TryParse(history.Data.GetValueOrDefault("indexerId"), out var indexerId))
|
||||||
{
|
{
|
||||||
//var indexerSettings = _indexerFactory.Get(indexerId).Settings as IIndexerSettings;
|
|
||||||
releaseInfo.IndexerId = indexerId;
|
releaseInfo.IndexerId = indexerId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,10 +46,10 @@ public ParsedMovieInfo AugmentMovieInfo(ParsedMovieInfo movieInfo, object helper
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Now we run the release info augmenters from the history release info. TODO: Add setting to only do that if you trust your indexer!
|
//Now we run the release info augmenters from the history release info. TODO: Add setting to only do that if you trust your indexer!
|
||||||
var releaseInfoAugmenters = _augmenters.Where(a => a.HelperType.IsInstanceOfType(releaseInfo));
|
var releaseInfoAugmenters = _augmenters.Where(a => a.Value.HelperType.IsInstanceOfType(releaseInfo));
|
||||||
foreach (var augmenter in releaseInfoAugmenters)
|
foreach (var augmenter in releaseInfoAugmenters)
|
||||||
{
|
{
|
||||||
movieInfo = augmenter.AugmentMovieInfo(movieInfo, releaseInfo);
|
movieInfo = augmenter.Value.AugmentMovieInfo(movieInfo, releaseInfo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,9 +9,9 @@ namespace NzbDrone.Core.Parser.Augmenters
|
|||||||
{
|
{
|
||||||
public class AugmentWithReleaseInfo : IAugmentParsedMovieInfo
|
public class AugmentWithReleaseInfo : IAugmentParsedMovieInfo
|
||||||
{
|
{
|
||||||
private readonly IIndexerFactory _indexerFactory;
|
private readonly Lazy<IIndexerFactory> _indexerFactory;
|
||||||
|
|
||||||
public AugmentWithReleaseInfo(IIndexerFactory indexerFactory)
|
public AugmentWithReleaseInfo(Lazy<IIndexerFactory> indexerFactory)
|
||||||
{
|
{
|
||||||
_indexerFactory = indexerFactory;
|
_indexerFactory = indexerFactory;
|
||||||
}
|
}
|
||||||
@ -31,7 +31,7 @@ public ParsedMovieInfo AugmentMovieInfo(ParsedMovieInfo movieInfo, object helper
|
|||||||
IIndexerSettings indexerSettings = null;
|
IIndexerSettings indexerSettings = null;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
indexerSettings = _indexerFactory.Get(releaseInfo.IndexerId)?.Settings as IIndexerSettings;
|
indexerSettings = _indexerFactory.Value.Get(releaseInfo.IndexerId)?.Settings as IIndexerSettings;
|
||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception)
|
||||||
{
|
{
|
||||||
|
@ -6,14 +6,15 @@
|
|||||||
<PackageReference Include="Dapper" Version="2.0.90" />
|
<PackageReference Include="Dapper" Version="2.0.90" />
|
||||||
<PackageReference Include="MailKit" Version="2.15.0" />
|
<PackageReference Include="MailKit" Version="2.15.0" />
|
||||||
<PackageReference Include="System.Memory" Version="4.5.4" />
|
<PackageReference Include="System.Memory" Version="4.5.4" />
|
||||||
<PackageReference Include="System.ServiceModel.Syndication" Version="5.0.0" />
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="5.0.1" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Logging" Version="5.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Configuration" Version="5.0.0" />
|
||||||
<PackageReference Include="FluentMigrator.Runner" Version="4.0.0-alpha.268" />
|
<PackageReference Include="FluentMigrator.Runner" Version="4.0.0-alpha.268" />
|
||||||
<PackageReference Include="FluentMigrator.Runner.SQLite" Version="4.0.0-alpha.268" />
|
<PackageReference Include="FluentMigrator.Runner.SQLite" Version="4.0.0-alpha.268" />
|
||||||
<PackageReference Include="FluentValidation" Version="8.6.2" />
|
<PackageReference Include="FluentValidation" Version="8.6.2" />
|
||||||
<PackageReference Include="SixLabors.ImageSharp" Version="1.0.1" />
|
<PackageReference Include="SixLabors.ImageSharp" Version="1.0.1" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||||
<PackageReference Include="NLog" Version="4.7.0" />
|
<PackageReference Include="NLog" Version="4.7.11" />
|
||||||
<PackageReference Include="TinyTwitter" Version="1.1.2" />
|
|
||||||
<PackageReference Include="Kveer.XmlRPC" Version="1.1.1" />
|
<PackageReference Include="Kveer.XmlRPC" Version="1.1.1" />
|
||||||
<PackageReference Include="System.Data.SQLite.Core.Servarr" Version="1.0.113.0-0" />
|
<PackageReference Include="System.Data.SQLite.Core.Servarr" Version="1.0.113.0-0" />
|
||||||
<PackageReference Include="System.Text.Json" Version="5.0.2" />
|
<PackageReference Include="System.Text.Json" Version="5.0.2" />
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using FluentValidation.Results;
|
using FluentValidation.Results;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using NLog;
|
using NLog;
|
||||||
using NzbDrone.Common.Composition;
|
using NzbDrone.Common.Composition;
|
||||||
using NzbDrone.Core.Lifecycle;
|
using NzbDrone.Core.Lifecycle;
|
||||||
@ -15,7 +16,7 @@ public abstract class ProviderFactory<TProvider, TProviderDefinition> : IProvide
|
|||||||
where TProvider : IProvider
|
where TProvider : IProvider
|
||||||
{
|
{
|
||||||
private readonly IProviderRepository<TProviderDefinition> _providerRepository;
|
private readonly IProviderRepository<TProviderDefinition> _providerRepository;
|
||||||
private readonly IContainer _container;
|
private readonly IServiceProvider _container;
|
||||||
private readonly IEventAggregator _eventAggregator;
|
private readonly IEventAggregator _eventAggregator;
|
||||||
private readonly Logger _logger;
|
private readonly Logger _logger;
|
||||||
|
|
||||||
@ -23,7 +24,7 @@ public abstract class ProviderFactory<TProvider, TProviderDefinition> : IProvide
|
|||||||
|
|
||||||
protected ProviderFactory(IProviderRepository<TProviderDefinition> providerRepository,
|
protected ProviderFactory(IProviderRepository<TProviderDefinition> providerRepository,
|
||||||
IEnumerable<TProvider> providers,
|
IEnumerable<TProvider> providers,
|
||||||
IContainer container,
|
IServiceProvider container,
|
||||||
IEventAggregator eventAggregator,
|
IEventAggregator eventAggregator,
|
||||||
Logger logger)
|
Logger logger)
|
||||||
{
|
{
|
||||||
@ -118,7 +119,7 @@ public void Delete(int id)
|
|||||||
public TProvider GetInstance(TProviderDefinition definition)
|
public TProvider GetInstance(TProviderDefinition definition)
|
||||||
{
|
{
|
||||||
var type = GetImplementation(definition);
|
var type = GetImplementation(definition);
|
||||||
var instance = (TProvider)_container.Resolve(type);
|
var instance = (TProvider)_container.GetRequiredService(type);
|
||||||
instance.Definition = definition;
|
instance.Definition = definition;
|
||||||
SetProviderCharacteristics(instance, definition);
|
SetProviderCharacteristics(instance, definition);
|
||||||
return instance;
|
return instance;
|
||||||
|
@ -1,14 +1,19 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using DryIoc;
|
||||||
|
using DryIoc.Microsoft.DependencyInjection;
|
||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Moq;
|
using Moq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using NzbDrone.Common;
|
using NzbDrone.Common;
|
||||||
using NzbDrone.Common.Composition;
|
using NzbDrone.Common.Composition.Extensions;
|
||||||
using NzbDrone.Common.EnvironmentInfo;
|
using NzbDrone.Common.EnvironmentInfo;
|
||||||
using NzbDrone.Core.Datastore;
|
using NzbDrone.Common.Instrumentation.Extensions;
|
||||||
|
using NzbDrone.Core.Datastore.Extensions;
|
||||||
using NzbDrone.Core.Download;
|
using NzbDrone.Core.Download;
|
||||||
using NzbDrone.Core.Download.TrackedDownloads;
|
using NzbDrone.Core.Download.TrackedDownloads;
|
||||||
|
using NzbDrone.Core.ImportLists.CouchPotato;
|
||||||
using NzbDrone.Core.Indexers;
|
using NzbDrone.Core.Indexers;
|
||||||
using NzbDrone.Core.Jobs;
|
using NzbDrone.Core.Jobs;
|
||||||
using NzbDrone.Core.Lifecycle;
|
using NzbDrone.Core.Lifecycle;
|
||||||
@ -17,44 +22,50 @@
|
|||||||
using NzbDrone.SignalR;
|
using NzbDrone.SignalR;
|
||||||
using NzbDrone.Test.Common;
|
using NzbDrone.Test.Common;
|
||||||
using Radarr.Host;
|
using Radarr.Host;
|
||||||
|
using IServiceProvider = System.IServiceProvider;
|
||||||
|
|
||||||
namespace NzbDrone.App.Test
|
namespace NzbDrone.App.Test
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class ContainerFixture : TestBase
|
public class ContainerFixture : TestBase
|
||||||
{
|
{
|
||||||
private IContainer _container;
|
private IServiceProvider _container;
|
||||||
|
|
||||||
[SetUp]
|
[SetUp]
|
||||||
public void SetUp()
|
public void SetUp()
|
||||||
{
|
{
|
||||||
var args = new StartupContext("first", "second");
|
var args = new StartupContext("first", "second");
|
||||||
|
|
||||||
_container = MainAppContainerBuilder.BuildContainer(args);
|
|
||||||
|
|
||||||
_container.Register<IMainDatabase>(new MainDatabase(null));
|
|
||||||
|
|
||||||
// set up a dummy broadcaster to allow tests to resolve
|
// set up a dummy broadcaster to allow tests to resolve
|
||||||
var mockBroadcaster = new Mock<IBroadcastSignalRMessage>();
|
var mockBroadcaster = new Mock<IBroadcastSignalRMessage>();
|
||||||
_container.Register<IBroadcastSignalRMessage>(mockBroadcaster.Object);
|
|
||||||
|
var container = new Container(rules => rules.WithNzbDroneRules())
|
||||||
|
.AutoAddServices(Bootstrap.ASSEMBLIES)
|
||||||
|
.AddNzbDroneLogger()
|
||||||
|
.AddDummyDatabase()
|
||||||
|
.AddStartupContext(args);
|
||||||
|
|
||||||
|
container.RegisterInstance<IBroadcastSignalRMessage>(mockBroadcaster.Object);
|
||||||
|
|
||||||
|
_container = container.GetServiceProvider();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void should_be_able_to_resolve_indexers()
|
public void should_be_able_to_resolve_indexers()
|
||||||
{
|
{
|
||||||
_container.Resolve<IEnumerable<IIndexer>>().Should().NotBeEmpty();
|
_container.GetRequiredService<IEnumerable<IIndexer>>().Should().NotBeEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void should_be_able_to_resolve_downloadclients()
|
public void should_be_able_to_resolve_downloadclients()
|
||||||
{
|
{
|
||||||
_container.Resolve<IEnumerable<IDownloadClient>>().Should().NotBeEmpty();
|
_container.GetRequiredService<IEnumerable<IDownloadClient>>().Should().NotBeEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void container_should_inject_itself()
|
public void container_should_inject_itself()
|
||||||
{
|
{
|
||||||
var factory = _container.Resolve<IServiceFactory>();
|
var factory = _container.GetRequiredService<IServiceFactory>();
|
||||||
|
|
||||||
factory.Build<IIndexerFactory>().Should().NotBeNull();
|
factory.Build<IIndexerFactory>().Should().NotBeNull();
|
||||||
}
|
}
|
||||||
@ -64,7 +75,7 @@ public void should_resolve_command_executor_by_name()
|
|||||||
{
|
{
|
||||||
var genericExecutor = typeof(IExecute<>).MakeGenericType(typeof(RssSyncCommand));
|
var genericExecutor = typeof(IExecute<>).MakeGenericType(typeof(RssSyncCommand));
|
||||||
|
|
||||||
var executor = _container.Resolve(genericExecutor);
|
var executor = _container.GetRequiredService(genericExecutor);
|
||||||
|
|
||||||
executor.Should().NotBeNull();
|
executor.Should().NotBeNull();
|
||||||
executor.Should().BeAssignableTo<IExecute<RssSyncCommand>>();
|
executor.Should().BeAssignableTo<IExecute<RssSyncCommand>>();
|
||||||
@ -74,8 +85,8 @@ public void should_resolve_command_executor_by_name()
|
|||||||
[Ignore("Shit appveyor")]
|
[Ignore("Shit appveyor")]
|
||||||
public void should_return_same_instance_of_singletons()
|
public void should_return_same_instance_of_singletons()
|
||||||
{
|
{
|
||||||
var first = _container.ResolveAll<IHandle<ApplicationShutdownRequested>>().OfType<Scheduler>().Single();
|
var first = (DownloadMonitoringService)_container.GetRequiredService<IHandle<ApplicationShutdownRequested>>();
|
||||||
var second = _container.ResolveAll<IHandle<ApplicationShutdownRequested>>().OfType<Scheduler>().Single();
|
var second = _container.GetServices<IHandle<TrackedDownloadsRemovedEvent>>().OfType<ApplicationShutdownRequested>().Single();
|
||||||
|
|
||||||
first.Should().BeSameAs(second);
|
first.Should().BeSameAs(second);
|
||||||
}
|
}
|
||||||
@ -83,8 +94,8 @@ public void should_return_same_instance_of_singletons()
|
|||||||
[Test]
|
[Test]
|
||||||
public void should_return_same_instance_of_singletons_by_different_same_interface()
|
public void should_return_same_instance_of_singletons_by_different_same_interface()
|
||||||
{
|
{
|
||||||
var first = _container.ResolveAll<IHandle<MovieGrabbedEvent>>().OfType<DownloadMonitoringService>().Single();
|
var first = _container.GetServices<IHandle<MovieGrabbedEvent>>().OfType<DownloadMonitoringService>().Single();
|
||||||
var second = _container.ResolveAll<IHandle<MovieGrabbedEvent>>().OfType<DownloadMonitoringService>().Single();
|
var second = _container.GetServices<IHandle<MovieGrabbedEvent>>().OfType<DownloadMonitoringService>().Single();
|
||||||
|
|
||||||
first.Should().BeSameAs(second);
|
first.Should().BeSameAs(second);
|
||||||
}
|
}
|
||||||
@ -92,8 +103,8 @@ public void should_return_same_instance_of_singletons_by_different_same_interfac
|
|||||||
[Test]
|
[Test]
|
||||||
public void should_return_same_instance_of_singletons_by_different_interfaces()
|
public void should_return_same_instance_of_singletons_by_different_interfaces()
|
||||||
{
|
{
|
||||||
var first = _container.ResolveAll<IHandle<MovieGrabbedEvent>>().OfType<DownloadMonitoringService>().Single();
|
var first = _container.GetServices<IHandle<MovieGrabbedEvent>>().OfType<DownloadMonitoringService>().Single();
|
||||||
var second = (DownloadMonitoringService)_container.Resolve<IExecute<RefreshMonitoredDownloadsCommand>>();
|
var second = (DownloadMonitoringService)_container.GetRequiredService<IExecute<RefreshMonitoredDownloadsCommand>>();
|
||||||
|
|
||||||
first.Should().BeSameAs(second);
|
first.Should().BeSameAs(second);
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
using System.ServiceProcess;
|
|
||||||
using Moq;
|
using Moq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using NzbDrone.Common;
|
using NzbDrone.Common;
|
||||||
@ -10,7 +9,7 @@
|
|||||||
namespace NzbDrone.App.Test
|
namespace NzbDrone.App.Test
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class RouterTest : TestBase<Router>
|
public class RouterTest : TestBase<UtilityModeRouter>
|
||||||
{
|
{
|
||||||
[SetUp]
|
[SetUp]
|
||||||
public void Setup()
|
public void Setup()
|
||||||
@ -48,33 +47,6 @@ public void Route_should_call_uninstall_service_when_application_mode_is_uninsta
|
|||||||
serviceProviderMock.Verify(c => c.Uninstall(ServiceProvider.SERVICE_NAME), Times.Once());
|
serviceProviderMock.Verify(c => c.Uninstall(ServiceProvider.SERVICE_NAME), Times.Once());
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void Route_should_call_console_service_when_application_mode_is_console()
|
|
||||||
{
|
|
||||||
Mocker.GetMock<IRuntimeInfo>().SetupGet(c => c.IsUserInteractive).Returns(true);
|
|
||||||
|
|
||||||
Subject.Route(ApplicationModes.Interactive);
|
|
||||||
|
|
||||||
Mocker.GetMock<INzbDroneConsoleFactory>().Verify(c => c.Start(), Times.Once());
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void Route_should_call_service_start_when_run_in_service_mode()
|
|
||||||
{
|
|
||||||
var envMock = Mocker.GetMock<IRuntimeInfo>();
|
|
||||||
var serviceProvider = Mocker.GetMock<IServiceProvider>();
|
|
||||||
|
|
||||||
envMock.SetupGet(c => c.IsUserInteractive).Returns(false);
|
|
||||||
|
|
||||||
serviceProvider.Setup(c => c.Run(It.IsAny<ServiceBase>()));
|
|
||||||
serviceProvider.Setup(c => c.ServiceExist(It.IsAny<string>())).Returns(true);
|
|
||||||
serviceProvider.Setup(c => c.GetStatus(It.IsAny<string>())).Returns(ServiceControllerStatus.StartPending);
|
|
||||||
|
|
||||||
Subject.Route(ApplicationModes.Service);
|
|
||||||
|
|
||||||
serviceProvider.Verify(c => c.Run(It.IsAny<ServiceBase>()), Times.Once());
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void show_error_on_install_if_service_already_exist()
|
public void show_error_on_install_if_service_already_exist()
|
||||||
{
|
{
|
||||||
|
@ -2,6 +2,11 @@
|
|||||||
|
|
||||||
namespace Radarr.Host.AccessControl
|
namespace Radarr.Host.AccessControl
|
||||||
{
|
{
|
||||||
|
public interface IRemoteAccessAdapter
|
||||||
|
{
|
||||||
|
void MakeAccessible(bool passive);
|
||||||
|
}
|
||||||
|
|
||||||
public class RemoteAccessAdapter : IRemoteAccessAdapter
|
public class RemoteAccessAdapter : IRemoteAccessAdapter
|
||||||
{
|
{
|
||||||
private readonly IRuntimeInfo _runtimeInfo;
|
private readonly IRuntimeInfo _runtimeInfo;
|
120
src/NzbDrone.Host/AppLifetime.cs
Normal file
120
src/NzbDrone.Host/AppLifetime.cs
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using NLog;
|
||||||
|
using NzbDrone.Common.EnvironmentInfo;
|
||||||
|
using NzbDrone.Common.Processes;
|
||||||
|
using NzbDrone.Core.Configuration;
|
||||||
|
using NzbDrone.Core.Lifecycle;
|
||||||
|
using NzbDrone.Core.Messaging.Events;
|
||||||
|
using Radarr.Host;
|
||||||
|
|
||||||
|
namespace NzbDrone.Host
|
||||||
|
{
|
||||||
|
public class AppLifetime : IHostedService, IHandle<ApplicationShutdownRequested>
|
||||||
|
{
|
||||||
|
private readonly IHostApplicationLifetime _appLifetime;
|
||||||
|
private readonly IConfigFileProvider _configFileProvider;
|
||||||
|
private readonly IRuntimeInfo _runtimeInfo;
|
||||||
|
private readonly IStartupContext _startupContext;
|
||||||
|
private readonly IBrowserService _browserService;
|
||||||
|
private readonly IProcessProvider _processProvider;
|
||||||
|
private readonly IEventAggregator _eventAggregator;
|
||||||
|
private readonly IUtilityModeRouter _utilityModeRouter;
|
||||||
|
private readonly Logger _logger;
|
||||||
|
|
||||||
|
public AppLifetime(IHostApplicationLifetime appLifetime,
|
||||||
|
IConfigFileProvider configFileProvider,
|
||||||
|
IRuntimeInfo runtimeInfo,
|
||||||
|
IStartupContext startupContext,
|
||||||
|
IBrowserService browserService,
|
||||||
|
IProcessProvider processProvider,
|
||||||
|
IEventAggregator eventAggregator,
|
||||||
|
IUtilityModeRouter utilityModeRouter,
|
||||||
|
Logger logger)
|
||||||
|
{
|
||||||
|
_appLifetime = appLifetime;
|
||||||
|
_configFileProvider = configFileProvider;
|
||||||
|
_runtimeInfo = runtimeInfo;
|
||||||
|
_startupContext = startupContext;
|
||||||
|
_browserService = browserService;
|
||||||
|
_processProvider = processProvider;
|
||||||
|
_eventAggregator = eventAggregator;
|
||||||
|
_utilityModeRouter = utilityModeRouter;
|
||||||
|
_logger = logger;
|
||||||
|
|
||||||
|
appLifetime.ApplicationStarted.Register(OnAppStarted);
|
||||||
|
appLifetime.ApplicationStopped.Register(OnAppStopped);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task StartAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task StopAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnAppStarted()
|
||||||
|
{
|
||||||
|
_runtimeInfo.IsExiting = false;
|
||||||
|
|
||||||
|
if (!_startupContext.Flags.Contains(StartupContext.NO_BROWSER)
|
||||||
|
&& _configFileProvider.LaunchBrowser)
|
||||||
|
{
|
||||||
|
_browserService.LaunchWebUI();
|
||||||
|
}
|
||||||
|
|
||||||
|
_eventAggregator.PublishEvent(new ApplicationStartedEvent());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnAppStopped()
|
||||||
|
{
|
||||||
|
if (_runtimeInfo.RestartPending)
|
||||||
|
{
|
||||||
|
var restartArgs = GetRestartArgs();
|
||||||
|
|
||||||
|
_logger.Info("Attempting restart with arguments: {0}", restartArgs);
|
||||||
|
_processProvider.SpawnNewProcess(_runtimeInfo.ExecutingApplication, restartArgs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Shutdown()
|
||||||
|
{
|
||||||
|
_logger.Info("Attempting to stop application.");
|
||||||
|
_logger.Info("Application has finished stop routine.");
|
||||||
|
_runtimeInfo.IsExiting = true;
|
||||||
|
_appLifetime.StopApplication();
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetRestartArgs()
|
||||||
|
{
|
||||||
|
var args = _startupContext.PreservedArguments;
|
||||||
|
|
||||||
|
args += " /restart";
|
||||||
|
|
||||||
|
if (!args.Contains("/nobrowser"))
|
||||||
|
{
|
||||||
|
args += " /nobrowser";
|
||||||
|
}
|
||||||
|
|
||||||
|
return args;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Handle(ApplicationShutdownRequested message)
|
||||||
|
{
|
||||||
|
if (!_runtimeInfo.IsWindowsService)
|
||||||
|
{
|
||||||
|
if (message.Restarting)
|
||||||
|
{
|
||||||
|
_runtimeInfo.RestartPending = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
LogManager.Configuration = null;
|
||||||
|
Shutdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,135 +0,0 @@
|
|||||||
using System.ServiceProcess;
|
|
||||||
using NLog;
|
|
||||||
using NzbDrone.Common.Composition;
|
|
||||||
using NzbDrone.Common.EnvironmentInfo;
|
|
||||||
using NzbDrone.Core.Configuration;
|
|
||||||
using NzbDrone.Core.Datastore;
|
|
||||||
using NzbDrone.Core.Lifecycle;
|
|
||||||
using NzbDrone.Core.Messaging.Events;
|
|
||||||
|
|
||||||
namespace Radarr.Host
|
|
||||||
{
|
|
||||||
public interface INzbDroneServiceFactory
|
|
||||||
{
|
|
||||||
ServiceBase Build();
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface INzbDroneConsoleFactory
|
|
||||||
{
|
|
||||||
void Start();
|
|
||||||
void Shutdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
public class NzbDroneServiceFactory : ServiceBase, INzbDroneServiceFactory
|
|
||||||
{
|
|
||||||
private readonly INzbDroneConsoleFactory _consoleFactory;
|
|
||||||
|
|
||||||
public NzbDroneServiceFactory(INzbDroneConsoleFactory consoleFactory)
|
|
||||||
{
|
|
||||||
_consoleFactory = consoleFactory;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnStart(string[] args)
|
|
||||||
{
|
|
||||||
_consoleFactory.Start();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnStop()
|
|
||||||
{
|
|
||||||
_consoleFactory.Shutdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
public ServiceBase Build()
|
|
||||||
{
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class DummyNzbDroneServiceFactory : INzbDroneServiceFactory
|
|
||||||
{
|
|
||||||
public ServiceBase Build()
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class NzbDroneConsoleFactory : INzbDroneConsoleFactory, IHandle<ApplicationShutdownRequested>
|
|
||||||
{
|
|
||||||
private readonly IConfigFileProvider _configFileProvider;
|
|
||||||
private readonly IRuntimeInfo _runtimeInfo;
|
|
||||||
private readonly IHostController _hostController;
|
|
||||||
private readonly IStartupContext _startupContext;
|
|
||||||
private readonly IBrowserService _browserService;
|
|
||||||
private readonly IContainer _container;
|
|
||||||
private readonly Logger _logger;
|
|
||||||
|
|
||||||
// private CancelHandler _cancelHandler;
|
|
||||||
public NzbDroneConsoleFactory(IConfigFileProvider configFileProvider,
|
|
||||||
IHostController hostController,
|
|
||||||
IRuntimeInfo runtimeInfo,
|
|
||||||
IStartupContext startupContext,
|
|
||||||
IBrowserService browserService,
|
|
||||||
IContainer container,
|
|
||||||
Logger logger)
|
|
||||||
{
|
|
||||||
_configFileProvider = configFileProvider;
|
|
||||||
_hostController = hostController;
|
|
||||||
_runtimeInfo = runtimeInfo;
|
|
||||||
_startupContext = startupContext;
|
|
||||||
_browserService = browserService;
|
|
||||||
_container = container;
|
|
||||||
_logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Start()
|
|
||||||
{
|
|
||||||
if (OsInfo.IsNotWindows)
|
|
||||||
{
|
|
||||||
//Console.CancelKeyPress += (sender, eventArgs) => eventArgs.Cancel = true;
|
|
||||||
//_cancelHandler = new CancelHandler();
|
|
||||||
}
|
|
||||||
|
|
||||||
_runtimeInfo.IsExiting = false;
|
|
||||||
DbFactory.RegisterDatabase(_container);
|
|
||||||
|
|
||||||
_container.Resolve<IEventAggregator>().PublishEvent(new ApplicationStartingEvent());
|
|
||||||
|
|
||||||
if (_runtimeInfo.IsExiting)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_hostController.StartServer();
|
|
||||||
|
|
||||||
if (!_startupContext.Flags.Contains(StartupContext.NO_BROWSER)
|
|
||||||
&& _configFileProvider.LaunchBrowser)
|
|
||||||
{
|
|
||||||
_browserService.LaunchWebUI();
|
|
||||||
}
|
|
||||||
|
|
||||||
_container.Resolve<IEventAggregator>().PublishEvent(new ApplicationStartedEvent());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Shutdown()
|
|
||||||
{
|
|
||||||
_logger.Info("Attempting to stop application.");
|
|
||||||
_hostController.StopServer();
|
|
||||||
_logger.Info("Application has finished stop routine.");
|
|
||||||
_runtimeInfo.IsExiting = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Handle(ApplicationShutdownRequested message)
|
|
||||||
{
|
|
||||||
if (!_runtimeInfo.IsWindowsService)
|
|
||||||
{
|
|
||||||
if (message.Restarting)
|
|
||||||
{
|
|
||||||
_runtimeInfo.RestartPending = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
LogManager.Configuration = null;
|
|
||||||
Shutdown();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,25 +1,45 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using System.Security.Cryptography.X509Certificates;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using DryIoc;
|
||||||
|
using DryIoc.Microsoft.DependencyInjection;
|
||||||
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using Microsoft.Extensions.Hosting.WindowsServices;
|
||||||
using NLog;
|
using NLog;
|
||||||
using NzbDrone.Common.Composition;
|
using NzbDrone.Common.Composition.Extensions;
|
||||||
|
using NzbDrone.Common.Disk;
|
||||||
using NzbDrone.Common.EnvironmentInfo;
|
using NzbDrone.Common.EnvironmentInfo;
|
||||||
using NzbDrone.Common.Exceptions;
|
using NzbDrone.Common.Exceptions;
|
||||||
|
using NzbDrone.Common.Extensions;
|
||||||
using NzbDrone.Common.Instrumentation;
|
using NzbDrone.Common.Instrumentation;
|
||||||
using NzbDrone.Common.Processes;
|
using NzbDrone.Common.Instrumentation.Extensions;
|
||||||
using NzbDrone.Core.Configuration;
|
using NzbDrone.Core.Configuration;
|
||||||
using NzbDrone.Core.Instrumentation;
|
using NzbDrone.Core.Datastore.Extensions;
|
||||||
|
using NzbDrone.Host;
|
||||||
|
|
||||||
namespace Radarr.Host
|
namespace Radarr.Host
|
||||||
{
|
{
|
||||||
public static class Bootstrap
|
public static class Bootstrap
|
||||||
{
|
{
|
||||||
private static readonly Logger Logger = NzbDroneLogger.GetLogger(typeof(Bootstrap));
|
private static readonly Logger Logger = NzbDroneLogger.GetLogger(typeof(Bootstrap));
|
||||||
private static IContainer _container;
|
|
||||||
|
|
||||||
public static void Start(StartupContext startupContext, IUserAlert userAlert, Action<IContainer> startCallback = null)
|
public static readonly List<string> ASSEMBLIES = new List<string>
|
||||||
|
{
|
||||||
|
"Radarr.Host",
|
||||||
|
"Radarr.Core",
|
||||||
|
"Radarr.SignalR",
|
||||||
|
"Radarr.Api.V3",
|
||||||
|
"Radarr.Http"
|
||||||
|
};
|
||||||
|
|
||||||
|
public static void Start(string[] args, Action<IHostBuilder> trayCallback = null)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -27,31 +47,47 @@ public static void Start(StartupContext startupContext, IUserAlert userAlert, Ac
|
|||||||
Process.GetCurrentProcess().MainModule.FileName,
|
Process.GetCurrentProcess().MainModule.FileName,
|
||||||
Assembly.GetExecutingAssembly().GetName().Version);
|
Assembly.GetExecutingAssembly().GetName().Version);
|
||||||
|
|
||||||
if (!PlatformValidation.IsValidate(userAlert))
|
var startupContext = new StartupContext(args);
|
||||||
{
|
|
||||||
throw new TerminateApplicationException("Missing system requirements");
|
|
||||||
}
|
|
||||||
|
|
||||||
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
|
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
|
||||||
|
|
||||||
_container = MainAppContainerBuilder.BuildContainer(startupContext);
|
|
||||||
_container.Resolve<InitializeLogger>().Initialize();
|
|
||||||
_container.Resolve<IAppFolderFactory>().Register();
|
|
||||||
_container.Resolve<IProvidePidFile>().Write();
|
|
||||||
|
|
||||||
var appMode = GetApplicationMode(startupContext);
|
var appMode = GetApplicationMode(startupContext);
|
||||||
|
|
||||||
Start(appMode, startupContext);
|
switch (appMode)
|
||||||
|
|
||||||
_container.Resolve<ICancelHandler>().Attach();
|
|
||||||
|
|
||||||
if (startCallback != null)
|
|
||||||
{
|
{
|
||||||
startCallback(_container);
|
case ApplicationModes.Service:
|
||||||
}
|
{
|
||||||
else
|
Logger.Debug("Service selected");
|
||||||
{
|
|
||||||
SpinToExit(appMode);
|
CreateConsoleHostBuilder(args, startupContext).UseWindowsService().Build().Run();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case ApplicationModes.Interactive:
|
||||||
|
{
|
||||||
|
Logger.Debug(trayCallback != null ? "Tray selected" : "Console selected");
|
||||||
|
var builder = CreateConsoleHostBuilder(args, startupContext);
|
||||||
|
|
||||||
|
if (trayCallback != null)
|
||||||
|
{
|
||||||
|
trayCallback(builder);
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.Build().Run();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Utility mode
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
new Container(rules => rules.WithNzbDroneRules())
|
||||||
|
.AutoAddServices(ASSEMBLIES)
|
||||||
|
.AddNzbDroneLogger()
|
||||||
|
.AddStartupContext(startupContext)
|
||||||
|
.Resolve<UtilityModeRouter>()
|
||||||
|
.Route(appMode);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (InvalidConfigFileException ex)
|
catch (InvalidConfigFileException ex)
|
||||||
@ -65,61 +101,57 @@ public static void Start(StartupContext startupContext, IUserAlert userAlert, Ac
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void Start(ApplicationModes applicationModes, StartupContext startupContext)
|
public static IHostBuilder CreateConsoleHostBuilder(string[] args, StartupContext context)
|
||||||
{
|
{
|
||||||
_container.Resolve<ReconfigureLogging>().Reconfigure();
|
var config = GetConfiguration(context);
|
||||||
|
|
||||||
if (!IsInUtilityMode(applicationModes))
|
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())
|
||||||
{
|
{
|
||||||
if (startupContext.Flags.Contains(StartupContext.RESTART))
|
urls.Add(BuildUrl("https", bindAddress, sslPort));
|
||||||
|
}
|
||||||
|
|
||||||
|
return new HostBuilder()
|
||||||
|
.UseContentRoot(Directory.GetCurrentDirectory())
|
||||||
|
.UseServiceProviderFactory(new DryIocServiceProviderFactory(new Container(rules => rules.WithNzbDroneRules())))
|
||||||
|
.ConfigureContainer<IContainer>(c =>
|
||||||
{
|
{
|
||||||
Thread.Sleep(2000);
|
c.AutoAddServices(Bootstrap.ASSEMBLIES)
|
||||||
}
|
.AddNzbDroneLogger()
|
||||||
|
.AddDatabase()
|
||||||
EnsureSingleInstance(applicationModes == ApplicationModes.Service, startupContext);
|
.AddStartupContext(context);
|
||||||
}
|
})
|
||||||
|
.ConfigureWebHost(builder =>
|
||||||
_container.Resolve<Router>().Route(applicationModes);
|
{
|
||||||
|
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.UseStartup<Startup>();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void SpinToExit(ApplicationModes applicationModes)
|
public static ApplicationModes GetApplicationMode(IStartupContext startupContext)
|
||||||
{
|
|
||||||
if (IsInUtilityMode(applicationModes))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_container.Resolve<IWaitForExit>().Spin();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void EnsureSingleInstance(bool isService, IStartupContext startupContext)
|
|
||||||
{
|
|
||||||
if (startupContext.Flags.Contains(StartupContext.NO_SINGLE_INSTANCE_CHECK))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var instancePolicy = _container.Resolve<ISingleInstancePolicy>();
|
|
||||||
|
|
||||||
if (startupContext.Flags.Contains(StartupContext.TERMINATE))
|
|
||||||
{
|
|
||||||
instancePolicy.KillAllOtherInstance();
|
|
||||||
}
|
|
||||||
else if (startupContext.Args.ContainsKey(StartupContext.APPDATA))
|
|
||||||
{
|
|
||||||
instancePolicy.WarnIfAlreadyRunning();
|
|
||||||
}
|
|
||||||
else if (isService)
|
|
||||||
{
|
|
||||||
instancePolicy.KillAllOtherInstance();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
instancePolicy.PreventStartIfAlreadyRunning();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ApplicationModes GetApplicationMode(IStartupContext startupContext)
|
|
||||||
{
|
{
|
||||||
if (startupContext.Help)
|
if (startupContext.Help)
|
||||||
{
|
{
|
||||||
@ -141,7 +173,7 @@ private static ApplicationModes GetApplicationMode(IStartupContext startupContex
|
|||||||
return ApplicationModes.UninstallService;
|
return ApplicationModes.UninstallService;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_container.Resolve<IRuntimeInfo>().IsWindowsService)
|
if (OsInfo.IsWindows && WindowsServiceHelpers.IsWindowsService())
|
||||||
{
|
{
|
||||||
return ApplicationModes.Service;
|
return ApplicationModes.Service;
|
||||||
}
|
}
|
||||||
@ -149,23 +181,39 @@ private static ApplicationModes GetApplicationMode(IStartupContext startupContex
|
|||||||
return ApplicationModes.Interactive;
|
return ApplicationModes.Interactive;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool IsInUtilityMode(ApplicationModes applicationMode)
|
private static IConfiguration GetConfiguration(StartupContext context)
|
||||||
{
|
{
|
||||||
switch (applicationMode)
|
var appFolder = new AppFolderInfo(context);
|
||||||
{
|
return new ConfigurationBuilder()
|
||||||
case ApplicationModes.InstallService:
|
.AddXmlFile(appFolder.GetConfigPath(), optional: true, reloadOnChange: false)
|
||||||
case ApplicationModes.UninstallService:
|
.Build();
|
||||||
case ApplicationModes.RegisterUrl:
|
}
|
||||||
case ApplicationModes.Help:
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
private static string BuildUrl(string scheme, string bindAddress, int port)
|
||||||
{
|
{
|
||||||
return false;
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +0,0 @@
|
|||||||
namespace Radarr.Host
|
|
||||||
{
|
|
||||||
public interface IHostController
|
|
||||||
{
|
|
||||||
void StartServer();
|
|
||||||
void StopServer();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,7 +0,0 @@
|
|||||||
namespace Radarr.Host.AccessControl
|
|
||||||
{
|
|
||||||
public interface IRemoteAccessAdapter
|
|
||||||
{
|
|
||||||
void MakeAccessible(bool passive);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,7 +0,0 @@
|
|||||||
namespace Radarr.Host
|
|
||||||
{
|
|
||||||
public interface IUserAlert
|
|
||||||
{
|
|
||||||
void Alert(string message);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,40 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using NzbDrone.Common.Composition;
|
|
||||||
using NzbDrone.Common.EnvironmentInfo;
|
|
||||||
using NzbDrone.SignalR;
|
|
||||||
using Radarr.Http;
|
|
||||||
|
|
||||||
namespace Radarr.Host
|
|
||||||
{
|
|
||||||
public class MainAppContainerBuilder : ContainerBuilderBase
|
|
||||||
{
|
|
||||||
public static IContainer BuildContainer(StartupContext args)
|
|
||||||
{
|
|
||||||
var assemblies = new List<string>
|
|
||||||
{
|
|
||||||
"Radarr.Host",
|
|
||||||
"Radarr.Core",
|
|
||||||
"Radarr.SignalR",
|
|
||||||
"Radarr.Api.V3",
|
|
||||||
"Radarr.Http"
|
|
||||||
};
|
|
||||||
|
|
||||||
return new MainAppContainerBuilder(args, assemblies).Container;
|
|
||||||
}
|
|
||||||
|
|
||||||
private MainAppContainerBuilder(StartupContext args, List<string> assemblies)
|
|
||||||
: base(args, assemblies)
|
|
||||||
{
|
|
||||||
AutoRegisterImplementations<MessageHub>();
|
|
||||||
|
|
||||||
if (OsInfo.IsWindows)
|
|
||||||
{
|
|
||||||
Container.Register<INzbDroneServiceFactory, NzbDroneServiceFactory>();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Container.Register<INzbDroneServiceFactory, DummyNzbDroneServiceFactory>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,56 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Reflection;
|
|
||||||
using NLog;
|
|
||||||
using NzbDrone.Common.EnvironmentInfo;
|
|
||||||
using NzbDrone.Common.Instrumentation;
|
|
||||||
|
|
||||||
namespace Radarr.Host
|
|
||||||
{
|
|
||||||
public static class PlatformValidation
|
|
||||||
{
|
|
||||||
private const string DOWNLOAD_LINK = "http://www.microsoft.com/en-us/download/details.aspx?id=42643";
|
|
||||||
|
|
||||||
private static readonly Logger Logger = NzbDroneLogger.GetLogger(typeof(PlatformValidation));
|
|
||||||
|
|
||||||
public static bool IsValidate(IUserAlert userAlert)
|
|
||||||
{
|
|
||||||
if (OsInfo.IsNotWindows)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!IsAssemblyAvailable("System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"))
|
|
||||||
{
|
|
||||||
userAlert.Alert("It looks like you don't have the correct version of .NET Framework installed. You will now be directed the download page.");
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Process.Start(DOWNLOAD_LINK);
|
|
||||||
}
|
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
userAlert.Alert("Oops. Couldn't start your browser. Please visit http://www.microsoft.com/net to download the latest version of .NET Framework");
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool IsAssemblyAvailable(string assemblyString)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Assembly.Load(assemblyString);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Logger.Warn("Couldn't load {0}", e.Message);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -4,9 +4,10 @@
|
|||||||
<OutputType>Library</OutputType>
|
<OutputType>Library</OutputType>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Owin" Version="5.0.10" />
|
|
||||||
<PackageReference Include="NLog.Extensions.Logging" Version="1.6.2" />
|
|
||||||
<PackageReference Include="System.Text.Encoding.CodePages" Version="5.0.0" />
|
<PackageReference Include="System.Text.Encoding.CodePages" Version="5.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="5.0.1" />
|
||||||
|
<PackageReference Include="DryIoc.dll" Version="4.7.4" />
|
||||||
|
<PackageReference Include="DryIoc.Microsoft.DependencyInjection" Version="5.1.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\NzbDrone.Common\Radarr.Common.csproj" />
|
<ProjectReference Include="..\NzbDrone.Common\Radarr.Common.csproj" />
|
||||||
|
@ -1,60 +0,0 @@
|
|||||||
using System.Threading;
|
|
||||||
using NLog;
|
|
||||||
using NzbDrone.Common.EnvironmentInfo;
|
|
||||||
using NzbDrone.Common.Processes;
|
|
||||||
|
|
||||||
namespace Radarr.Host
|
|
||||||
{
|
|
||||||
public interface IWaitForExit
|
|
||||||
{
|
|
||||||
void Spin();
|
|
||||||
}
|
|
||||||
|
|
||||||
public class SpinService : IWaitForExit
|
|
||||||
{
|
|
||||||
private readonly IRuntimeInfo _runtimeInfo;
|
|
||||||
private readonly IProcessProvider _processProvider;
|
|
||||||
private readonly IStartupContext _startupContext;
|
|
||||||
private readonly Logger _logger;
|
|
||||||
|
|
||||||
public SpinService(IRuntimeInfo runtimeInfo, IProcessProvider processProvider, IStartupContext startupContext, Logger logger)
|
|
||||||
{
|
|
||||||
_runtimeInfo = runtimeInfo;
|
|
||||||
_processProvider = processProvider;
|
|
||||||
_startupContext = startupContext;
|
|
||||||
_logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Spin()
|
|
||||||
{
|
|
||||||
while (!_runtimeInfo.IsExiting)
|
|
||||||
{
|
|
||||||
Thread.Sleep(1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
_logger.Debug("Wait loop was terminated.");
|
|
||||||
|
|
||||||
if (_runtimeInfo.RestartPending)
|
|
||||||
{
|
|
||||||
var restartArgs = GetRestartArgs();
|
|
||||||
|
|
||||||
_logger.Info("Attempting restart with arguments: {0}", restartArgs);
|
|
||||||
_processProvider.SpawnNewProcess(_runtimeInfo.ExecutingApplication, restartArgs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private string GetRestartArgs()
|
|
||||||
{
|
|
||||||
var args = _startupContext.PreservedArguments;
|
|
||||||
|
|
||||||
args += " /restart";
|
|
||||||
|
|
||||||
if (!args.Contains("/nobrowser"))
|
|
||||||
{
|
|
||||||
args += " /nobrowser";
|
|
||||||
}
|
|
||||||
|
|
||||||
return args;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
202
src/NzbDrone.Host/Startup.cs
Normal file
202
src/NzbDrone.Host/Startup.cs
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using NLog.Extensions.Logging;
|
||||||
|
using NzbDrone.Common.EnvironmentInfo;
|
||||||
|
using NzbDrone.Common.Instrumentation;
|
||||||
|
using NzbDrone.Common.Processes;
|
||||||
|
using NzbDrone.Common.Serializer;
|
||||||
|
using NzbDrone.Core.Configuration;
|
||||||
|
using NzbDrone.Core.Datastore;
|
||||||
|
using NzbDrone.Core.Instrumentation;
|
||||||
|
using NzbDrone.Http.Authentication;
|
||||||
|
using NzbDrone.SignalR;
|
||||||
|
using Radarr.Api.V3.System;
|
||||||
|
using Radarr.Host;
|
||||||
|
using Radarr.Host.AccessControl;
|
||||||
|
using Radarr.Http;
|
||||||
|
using Radarr.Http.Authentication;
|
||||||
|
using Radarr.Http.ErrorManagement;
|
||||||
|
using Radarr.Http.Frontend;
|
||||||
|
using Radarr.Http.Middleware;
|
||||||
|
using LogLevel = Microsoft.Extensions.Logging.LogLevel;
|
||||||
|
|
||||||
|
namespace NzbDrone.Host
|
||||||
|
{
|
||||||
|
public class Startup
|
||||||
|
{
|
||||||
|
public Startup(IConfiguration configuration)
|
||||||
|
{
|
||||||
|
Configuration = configuration;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IConfiguration Configuration { get; }
|
||||||
|
|
||||||
|
public void ConfigureServices(IServiceCollection services)
|
||||||
|
{
|
||||||
|
services.AddLogging(b =>
|
||||||
|
{
|
||||||
|
b.ClearProviders();
|
||||||
|
b.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Trace);
|
||||||
|
b.AddFilter("Microsoft.AspNetCore", Microsoft.Extensions.Logging.LogLevel.Warning);
|
||||||
|
b.AddFilter("Radarr.Http.Authentication", LogLevel.Information);
|
||||||
|
b.AddNLog();
|
||||||
|
});
|
||||||
|
|
||||||
|
services.AddRouting(options => options.LowercaseUrls = true);
|
||||||
|
|
||||||
|
services.AddResponseCompression();
|
||||||
|
|
||||||
|
services.AddCors(options =>
|
||||||
|
{
|
||||||
|
options.AddPolicy(VersionedApiControllerAttribute.API_CORS_POLICY,
|
||||||
|
builder =>
|
||||||
|
builder.AllowAnyOrigin()
|
||||||
|
.AllowAnyMethod()
|
||||||
|
.AllowAnyHeader());
|
||||||
|
|
||||||
|
options.AddPolicy("AllowGet",
|
||||||
|
builder =>
|
||||||
|
builder.AllowAnyOrigin()
|
||||||
|
.WithMethods("GET", "OPTIONS")
|
||||||
|
.AllowAnyHeader());
|
||||||
|
});
|
||||||
|
|
||||||
|
services
|
||||||
|
.AddControllers(options =>
|
||||||
|
{
|
||||||
|
options.ReturnHttpNotAcceptable = true;
|
||||||
|
})
|
||||||
|
.AddApplicationPart(typeof(SystemController).Assembly)
|
||||||
|
.AddApplicationPart(typeof(StaticResourceController).Assembly)
|
||||||
|
.AddJsonOptions(options =>
|
||||||
|
{
|
||||||
|
STJson.ApplySerializerSettings(options.JsonSerializerOptions);
|
||||||
|
})
|
||||||
|
.AddControllersAsServices();
|
||||||
|
|
||||||
|
services
|
||||||
|
.AddSignalR()
|
||||||
|
.AddJsonProtocol(options =>
|
||||||
|
{
|
||||||
|
options.PayloadSerializerOptions = STJson.GetSerializerSettings();
|
||||||
|
});
|
||||||
|
|
||||||
|
services.AddSingleton<IAuthorizationPolicyProvider, UiAuthorizationPolicyProvider>();
|
||||||
|
services.AddAuthorization(options =>
|
||||||
|
{
|
||||||
|
options.AddPolicy("SignalR", policy =>
|
||||||
|
{
|
||||||
|
policy.AuthenticationSchemes.Add("SignalR");
|
||||||
|
policy.RequireAuthenticatedUser();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Require auth on everything except those marked [AllowAnonymous]
|
||||||
|
options.FallbackPolicy = new AuthorizationPolicyBuilder("API")
|
||||||
|
.RequireAuthenticatedUser()
|
||||||
|
.Build();
|
||||||
|
});
|
||||||
|
|
||||||
|
services.AddAppAuthentication();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Configure(IApplicationBuilder app,
|
||||||
|
IStartupContext startupContext,
|
||||||
|
Lazy<IMainDatabase> mainDatabaseFactory,
|
||||||
|
Lazy<ILogDatabase> logDatabaseFactory,
|
||||||
|
DatabaseTarget dbTarget,
|
||||||
|
ISingleInstancePolicy singleInstancePolicy,
|
||||||
|
InitializeLogger initializeLogger,
|
||||||
|
ReconfigureLogging reconfigureLogging,
|
||||||
|
IAppFolderFactory appFolderFactory,
|
||||||
|
IProvidePidFile pidFileProvider,
|
||||||
|
IConfigFileProvider configFileProvider,
|
||||||
|
IRuntimeInfo runtimeInfo,
|
||||||
|
IFirewallAdapter firewallAdapter,
|
||||||
|
RadarrErrorPipeline errorHandler)
|
||||||
|
{
|
||||||
|
initializeLogger.Initialize();
|
||||||
|
appFolderFactory.Register();
|
||||||
|
pidFileProvider.Write();
|
||||||
|
|
||||||
|
reconfigureLogging.Reconfigure();
|
||||||
|
|
||||||
|
EnsureSingleInstance(false, startupContext, singleInstancePolicy);
|
||||||
|
|
||||||
|
// instantiate the databases to initialize/migrate them
|
||||||
|
_ = mainDatabaseFactory.Value;
|
||||||
|
_ = logDatabaseFactory.Value;
|
||||||
|
|
||||||
|
dbTarget.Register();
|
||||||
|
|
||||||
|
if (OsInfo.IsNotWindows)
|
||||||
|
{
|
||||||
|
Console.CancelKeyPress += (sender, eventArgs) => NLog.LogManager.Configuration = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (OsInfo.IsWindows && runtimeInfo.IsAdmin)
|
||||||
|
{
|
||||||
|
firewallAdapter.MakeAccessible();
|
||||||
|
}
|
||||||
|
|
||||||
|
app.UseMiddleware<LoggingMiddleware>();
|
||||||
|
app.UsePathBase(new PathString(configFileProvider.UrlBase));
|
||||||
|
app.UseExceptionHandler(new ExceptionHandlerOptions
|
||||||
|
{
|
||||||
|
AllowStatusCode404Response = true,
|
||||||
|
ExceptionHandler = errorHandler.HandleException
|
||||||
|
});
|
||||||
|
|
||||||
|
app.UseRouting();
|
||||||
|
app.UseCors();
|
||||||
|
app.UseAuthentication();
|
||||||
|
app.UseAuthorization();
|
||||||
|
app.UseResponseCompression();
|
||||||
|
app.Properties["host.AppName"] = BuildInfo.AppName;
|
||||||
|
|
||||||
|
app.UseMiddleware<VersionMiddleware>();
|
||||||
|
app.UseMiddleware<UrlBaseMiddleware>(configFileProvider.UrlBase);
|
||||||
|
app.UseMiddleware<CacheHeaderMiddleware>();
|
||||||
|
app.UseMiddleware<IfModifiedMiddleware>();
|
||||||
|
app.UseMiddleware<BufferingMiddleware>(new List<string> { "/api/v3/command" });
|
||||||
|
|
||||||
|
app.UseWebSockets();
|
||||||
|
|
||||||
|
app.UseEndpoints(x =>
|
||||||
|
{
|
||||||
|
x.MapHub<MessageHub>("/signalr/messages").RequireAuthorization("SignalR");
|
||||||
|
x.MapControllers();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EnsureSingleInstance(bool isService, IStartupContext startupContext, ISingleInstancePolicy instancePolicy)
|
||||||
|
{
|
||||||
|
if (startupContext.Flags.Contains(StartupContext.NO_SINGLE_INSTANCE_CHECK))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (startupContext.Flags.Contains(StartupContext.TERMINATE))
|
||||||
|
{
|
||||||
|
instancePolicy.KillAllOtherInstance();
|
||||||
|
}
|
||||||
|
else if (startupContext.Args.ContainsKey(StartupContext.APPDATA))
|
||||||
|
{
|
||||||
|
instancePolicy.WarnIfAlreadyRunning();
|
||||||
|
}
|
||||||
|
else if (isService)
|
||||||
|
{
|
||||||
|
instancePolicy.KillAllOtherInstance();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
instancePolicy.PreventStartIfAlreadyRunning();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -7,10 +7,13 @@
|
|||||||
|
|
||||||
namespace Radarr.Host
|
namespace Radarr.Host
|
||||||
{
|
{
|
||||||
public class Router
|
public interface IUtilityModeRouter
|
||||||
|
{
|
||||||
|
void Route(ApplicationModes applicationModes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class UtilityModeRouter : IUtilityModeRouter
|
||||||
{
|
{
|
||||||
private readonly INzbDroneConsoleFactory _nzbDroneConsoleFactory;
|
|
||||||
private readonly INzbDroneServiceFactory _nzbDroneServiceFactory;
|
|
||||||
private readonly IServiceProvider _serviceProvider;
|
private readonly IServiceProvider _serviceProvider;
|
||||||
private readonly IConsoleService _consoleService;
|
private readonly IConsoleService _consoleService;
|
||||||
private readonly IRuntimeInfo _runtimeInfo;
|
private readonly IRuntimeInfo _runtimeInfo;
|
||||||
@ -18,17 +21,13 @@ public class Router
|
|||||||
private readonly IRemoteAccessAdapter _remoteAccessAdapter;
|
private readonly IRemoteAccessAdapter _remoteAccessAdapter;
|
||||||
private readonly Logger _logger;
|
private readonly Logger _logger;
|
||||||
|
|
||||||
public Router(INzbDroneConsoleFactory nzbDroneConsoleFactory,
|
public UtilityModeRouter(IServiceProvider serviceProvider,
|
||||||
INzbDroneServiceFactory nzbDroneServiceFactory,
|
|
||||||
IServiceProvider serviceProvider,
|
|
||||||
IConsoleService consoleService,
|
IConsoleService consoleService,
|
||||||
IRuntimeInfo runtimeInfo,
|
IRuntimeInfo runtimeInfo,
|
||||||
IProcessProvider processProvider,
|
IProcessProvider processProvider,
|
||||||
IRemoteAccessAdapter remoteAccessAdapter,
|
IRemoteAccessAdapter remoteAccessAdapter,
|
||||||
Logger logger)
|
Logger logger)
|
||||||
{
|
{
|
||||||
_nzbDroneConsoleFactory = nzbDroneConsoleFactory;
|
|
||||||
_nzbDroneServiceFactory = nzbDroneServiceFactory;
|
|
||||||
_serviceProvider = serviceProvider;
|
_serviceProvider = serviceProvider;
|
||||||
_consoleService = consoleService;
|
_consoleService = consoleService;
|
||||||
_runtimeInfo = runtimeInfo;
|
_runtimeInfo = runtimeInfo;
|
||||||
@ -43,20 +42,6 @@ public void Route(ApplicationModes applicationModes)
|
|||||||
|
|
||||||
switch (applicationModes)
|
switch (applicationModes)
|
||||||
{
|
{
|
||||||
case ApplicationModes.Service:
|
|
||||||
{
|
|
||||||
_logger.Debug("Service selected");
|
|
||||||
_serviceProvider.Run(_nzbDroneServiceFactory.Build());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case ApplicationModes.Interactive:
|
|
||||||
{
|
|
||||||
_logger.Debug(_runtimeInfo.IsWindowsTray ? "Tray selected" : "Console selected");
|
|
||||||
_nzbDroneConsoleFactory.Start();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case ApplicationModes.InstallService:
|
case ApplicationModes.InstallService:
|
||||||
{
|
{
|
||||||
_logger.Debug("Install Service selected");
|
_logger.Debug("Install Service selected");
|
@ -1,26 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Microsoft.AspNetCore.Mvc.Controllers;
|
|
||||||
using NzbDrone.Common.Composition;
|
|
||||||
|
|
||||||
namespace NzbDrone.Host
|
|
||||||
{
|
|
||||||
public class ControllerActivator : IControllerActivator
|
|
||||||
{
|
|
||||||
private readonly IContainer _container;
|
|
||||||
|
|
||||||
public ControllerActivator(IContainer container)
|
|
||||||
{
|
|
||||||
_container = container;
|
|
||||||
}
|
|
||||||
|
|
||||||
public object Create(ControllerContext context)
|
|
||||||
{
|
|
||||||
return _container.Resolve(context.ActionDescriptor.ControllerTypeInfo.AsType());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Release(ControllerContext context, object controller)
|
|
||||||
{
|
|
||||||
// Nothing to do
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,275 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Security.Cryptography;
|
|
||||||
using System.Security.Cryptography.X509Certificates;
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using Microsoft.AspNetCore.Builder;
|
|
||||||
using Microsoft.AspNetCore.Hosting;
|
|
||||||
using Microsoft.AspNetCore.Http;
|
|
||||||
using Microsoft.AspNetCore.Mvc.Controllers;
|
|
||||||
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
|
||||||
using Microsoft.AspNetCore.Routing;
|
|
||||||
using Microsoft.AspNetCore.Routing.Internal;
|
|
||||||
using Microsoft.AspNetCore.SignalR;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using NLog;
|
|
||||||
using NLog.Extensions.Logging;
|
|
||||||
using NzbDrone.Common.Composition;
|
|
||||||
using NzbDrone.Common.EnvironmentInfo;
|
|
||||||
using NzbDrone.Common.Exceptions;
|
|
||||||
using NzbDrone.Common.Extensions;
|
|
||||||
using NzbDrone.Common.Serializer;
|
|
||||||
using NzbDrone.Core.Configuration;
|
|
||||||
using NzbDrone.Host;
|
|
||||||
using NzbDrone.SignalR;
|
|
||||||
using Radarr.Api.V3.System;
|
|
||||||
using Radarr.Host.AccessControl;
|
|
||||||
using Radarr.Http;
|
|
||||||
using Radarr.Http.Authentication;
|
|
||||||
using Radarr.Http.ErrorManagement;
|
|
||||||
using Radarr.Http.Frontend;
|
|
||||||
using Radarr.Http.Middleware;
|
|
||||||
using LogLevel = Microsoft.Extensions.Logging.LogLevel;
|
|
||||||
|
|
||||||
namespace Radarr.Host
|
|
||||||
{
|
|
||||||
public class WebHostController : IHostController
|
|
||||||
{
|
|
||||||
private readonly IContainer _container;
|
|
||||||
private readonly IRuntimeInfo _runtimeInfo;
|
|
||||||
private readonly IConfigFileProvider _configFileProvider;
|
|
||||||
private readonly IFirewallAdapter _firewallAdapter;
|
|
||||||
private readonly RadarrErrorPipeline _errorHandler;
|
|
||||||
private readonly Logger _logger;
|
|
||||||
private IWebHost _host;
|
|
||||||
|
|
||||||
public WebHostController(IContainer container,
|
|
||||||
IRuntimeInfo runtimeInfo,
|
|
||||||
IConfigFileProvider configFileProvider,
|
|
||||||
IFirewallAdapter firewallAdapter,
|
|
||||||
RadarrErrorPipeline errorHandler,
|
|
||||||
Logger logger)
|
|
||||||
{
|
|
||||||
_container = container;
|
|
||||||
_runtimeInfo = runtimeInfo;
|
|
||||||
_configFileProvider = configFileProvider;
|
|
||||||
_firewallAdapter = firewallAdapter;
|
|
||||||
_errorHandler = errorHandler;
|
|
||||||
_logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void StartServer()
|
|
||||||
{
|
|
||||||
if (OsInfo.IsWindows)
|
|
||||||
{
|
|
||||||
if (_runtimeInfo.IsAdmin)
|
|
||||||
{
|
|
||||||
_firewallAdapter.MakeAccessible();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var bindAddress = _configFileProvider.BindAddress;
|
|
||||||
var enableSsl = _configFileProvider.EnableSsl;
|
|
||||||
var sslCertPath = _configFileProvider.SslCertPath;
|
|
||||||
|
|
||||||
var urls = new List<string>();
|
|
||||||
|
|
||||||
urls.Add(BuildUrl("http", bindAddress, _configFileProvider.Port));
|
|
||||||
|
|
||||||
if (enableSsl && sslCertPath.IsNotNullOrWhiteSpace())
|
|
||||||
{
|
|
||||||
urls.Add(BuildUrl("https", bindAddress, _configFileProvider.SslPort));
|
|
||||||
}
|
|
||||||
|
|
||||||
_host = new WebHostBuilder()
|
|
||||||
.UseUrls(urls.ToArray())
|
|
||||||
.UseKestrel(options =>
|
|
||||||
{
|
|
||||||
if (enableSsl && sslCertPath.IsNotNullOrWhiteSpace())
|
|
||||||
{
|
|
||||||
options.ConfigureHttpsDefaults(configureOptions =>
|
|
||||||
{
|
|
||||||
X509Certificate2 certificate;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
certificate = new X509Certificate2(sslCertPath, _configFileProvider.SslCertPassword, X509KeyStorageFlags.DefaultKeySet);
|
|
||||||
}
|
|
||||||
catch (CryptographicException ex)
|
|
||||||
{
|
|
||||||
if (ex.HResult == 0x2 || ex.HResult == 0x2006D080)
|
|
||||||
{
|
|
||||||
throw new RadarrStartupException(ex, $"The SSL certificate file {sslCertPath} does not exist");
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new RadarrStartupException(ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
configureOptions.ServerCertificate = certificate;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.ConfigureKestrel(serverOptions =>
|
|
||||||
{
|
|
||||||
serverOptions.AllowSynchronousIO = true;
|
|
||||||
serverOptions.Limits.MaxRequestBodySize = null;
|
|
||||||
})
|
|
||||||
.ConfigureLogging(logging =>
|
|
||||||
{
|
|
||||||
logging.AddProvider(new NLogLoggerProvider());
|
|
||||||
logging.SetMinimumLevel(LogLevel.Warning);
|
|
||||||
})
|
|
||||||
.ConfigureServices(services =>
|
|
||||||
{
|
|
||||||
// So that we can resolve containers with our TinyIoC services
|
|
||||||
services.AddSingleton(_container);
|
|
||||||
services.AddSingleton<IControllerActivator, ControllerActivator>();
|
|
||||||
|
|
||||||
// Bits used in our custom middleware
|
|
||||||
services.AddSingleton(_container.Resolve<RadarrErrorPipeline>());
|
|
||||||
services.AddSingleton(_container.Resolve<ICacheableSpecification>());
|
|
||||||
|
|
||||||
// Used in authentication
|
|
||||||
services.AddSingleton(_container.Resolve<IAuthenticationService>());
|
|
||||||
|
|
||||||
services.AddRouting(options => options.LowercaseUrls = true);
|
|
||||||
|
|
||||||
services.AddResponseCompression();
|
|
||||||
|
|
||||||
services.AddCors(options =>
|
|
||||||
{
|
|
||||||
options.AddPolicy(VersionedApiControllerAttribute.API_CORS_POLICY,
|
|
||||||
builder =>
|
|
||||||
builder.AllowAnyOrigin()
|
|
||||||
.AllowAnyMethod()
|
|
||||||
.AllowAnyHeader());
|
|
||||||
|
|
||||||
options.AddPolicy("AllowGet",
|
|
||||||
builder =>
|
|
||||||
builder.AllowAnyOrigin()
|
|
||||||
.WithMethods("GET", "OPTIONS")
|
|
||||||
.AllowAnyHeader());
|
|
||||||
});
|
|
||||||
|
|
||||||
services
|
|
||||||
.AddControllers(options =>
|
|
||||||
{
|
|
||||||
options.ReturnHttpNotAcceptable = true;
|
|
||||||
})
|
|
||||||
.AddApplicationPart(typeof(SystemController).Assembly)
|
|
||||||
.AddApplicationPart(typeof(StaticResourceController).Assembly)
|
|
||||||
.AddJsonOptions(options =>
|
|
||||||
{
|
|
||||||
STJson.ApplySerializerSettings(options.JsonSerializerOptions);
|
|
||||||
});
|
|
||||||
|
|
||||||
services
|
|
||||||
.AddSignalR()
|
|
||||||
.AddJsonProtocol(options =>
|
|
||||||
{
|
|
||||||
options.PayloadSerializerOptions = STJson.GetSerializerSettings();
|
|
||||||
});
|
|
||||||
|
|
||||||
services.AddAuthorization(options =>
|
|
||||||
{
|
|
||||||
options.AddPolicy("UI", policy =>
|
|
||||||
{
|
|
||||||
policy.AuthenticationSchemes.Add(_configFileProvider.AuthenticationMethod.ToString());
|
|
||||||
policy.RequireAuthenticatedUser();
|
|
||||||
});
|
|
||||||
|
|
||||||
options.AddPolicy("SignalR", policy =>
|
|
||||||
{
|
|
||||||
policy.AuthenticationSchemes.Add("SignalR");
|
|
||||||
policy.RequireAuthenticatedUser();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Require auth on everything except those marked [AllowAnonymous]
|
|
||||||
options.DefaultPolicy = new AuthorizationPolicyBuilder("API")
|
|
||||||
.RequireAuthenticatedUser()
|
|
||||||
.Build();
|
|
||||||
});
|
|
||||||
|
|
||||||
services.AddAppAuthentication(_configFileProvider);
|
|
||||||
})
|
|
||||||
.Configure(app =>
|
|
||||||
{
|
|
||||||
app.UseMiddleware<LoggingMiddleware>();
|
|
||||||
app.UsePathBase(new PathString(_configFileProvider.UrlBase));
|
|
||||||
app.UseExceptionHandler(new ExceptionHandlerOptions
|
|
||||||
{
|
|
||||||
AllowStatusCode404Response = true,
|
|
||||||
ExceptionHandler = _errorHandler.HandleException
|
|
||||||
});
|
|
||||||
|
|
||||||
app.UseRouting();
|
|
||||||
app.UseCors();
|
|
||||||
app.UseAuthentication();
|
|
||||||
app.UseAuthorization();
|
|
||||||
app.UseResponseCompression();
|
|
||||||
app.Properties["host.AppName"] = BuildInfo.AppName;
|
|
||||||
|
|
||||||
app.UseMiddleware<VersionMiddleware>();
|
|
||||||
app.UseMiddleware<UrlBaseMiddleware>(_configFileProvider.UrlBase);
|
|
||||||
app.UseMiddleware<CacheHeaderMiddleware>();
|
|
||||||
app.UseMiddleware<IfModifiedMiddleware>();
|
|
||||||
|
|
||||||
app.Use((context, next) =>
|
|
||||||
{
|
|
||||||
if (context.Request.Path.StartsWithSegments("/api/v1/command", StringComparison.CurrentCultureIgnoreCase))
|
|
||||||
{
|
|
||||||
context.Request.EnableBuffering();
|
|
||||||
}
|
|
||||||
|
|
||||||
return next();
|
|
||||||
});
|
|
||||||
|
|
||||||
app.UseWebSockets();
|
|
||||||
|
|
||||||
app.UseEndpoints(x =>
|
|
||||||
{
|
|
||||||
x.MapHub<MessageHub>("/signalr/messages").RequireAuthorization("SignalR");
|
|
||||||
x.MapControllers();
|
|
||||||
});
|
|
||||||
|
|
||||||
// This is a side effect of haing multiple IoC containers, TinyIoC and whatever
|
|
||||||
// Kestrel/SignalR is using. Ideally we'd have one IoC container, but that's non-trivial with TinyIoC
|
|
||||||
// TODO: Use a single IoC container if supported for TinyIoC or if we switch to another system (ie Autofac).
|
|
||||||
_container.Register(app.ApplicationServices);
|
|
||||||
_container.Register(app.ApplicationServices.GetService<IHubContext<MessageHub>>());
|
|
||||||
_container.Register(app.ApplicationServices.GetService<IActionDescriptorCollectionProvider>());
|
|
||||||
_container.Register(app.ApplicationServices.GetService<EndpointDataSource>());
|
|
||||||
_container.Register(app.ApplicationServices.GetService<DfaGraphWriter>());
|
|
||||||
})
|
|
||||||
.UseContentRoot(Directory.GetCurrentDirectory())
|
|
||||||
.Build();
|
|
||||||
|
|
||||||
_logger.Info("Listening on the following URLs:");
|
|
||||||
|
|
||||||
foreach (var url in urls)
|
|
||||||
{
|
|
||||||
_logger.Info(" {0}", url);
|
|
||||||
}
|
|
||||||
|
|
||||||
_host.Start();
|
|
||||||
}
|
|
||||||
|
|
||||||
public async void StopServer()
|
|
||||||
{
|
|
||||||
_logger.Info("Attempting to stop OWIN host");
|
|
||||||
|
|
||||||
await _host.StopAsync(TimeSpan.FromSeconds(5));
|
|
||||||
_host.Dispose();
|
|
||||||
_host = null;
|
|
||||||
|
|
||||||
_logger.Info("Host has stopped");
|
|
||||||
}
|
|
||||||
|
|
||||||
private string BuildUrl(string scheme, string bindAddress, int port)
|
|
||||||
{
|
|
||||||
return $"{scheme}://{bindAddress}:{port}";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -148,11 +148,12 @@ private void SetupAutoMoqer(IUnityContainer container)
|
|||||||
_container = container;
|
_container = container;
|
||||||
container.RegisterInstance(this);
|
container.RegisterInstance(this);
|
||||||
|
|
||||||
RegisterPlatformLibrary(container);
|
|
||||||
|
|
||||||
_registeredMocks = new Dictionary<Type, object>();
|
_registeredMocks = new Dictionary<Type, object>();
|
||||||
|
|
||||||
|
RegisterPlatformLibrary(container);
|
||||||
AddTheAutoMockingContainerExtensionToTheContainer(container);
|
AddTheAutoMockingContainerExtensionToTheContainer(container);
|
||||||
ContainerBuilderBase.RegisterNativeResolver(new[] { "System.Data.SQLite", "Radarr.Core" });
|
|
||||||
|
AssemblyLoader.RegisterNativeResolver(new[] { "System.Data.SQLite", "Radarr.Core" });
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void AddTheAutoMockingContainerExtensionToTheContainer(IUnityContainer container)
|
private static void AddTheAutoMockingContainerExtensionToTheContainer(IUnityContainer container)
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
<PackageReference Include="FluentAssertions" Version="5.10.3" />
|
<PackageReference Include="FluentAssertions" Version="5.10.3" />
|
||||||
<PackageReference Include="FluentValidation" Version="8.6.2" />
|
<PackageReference Include="FluentValidation" Version="8.6.2" />
|
||||||
<PackageReference Include="Moq" Version="4.13.1" />
|
<PackageReference Include="Moq" Version="4.13.1" />
|
||||||
<PackageReference Include="NLog" Version="4.7.0" />
|
<PackageReference Include="NLog" Version="4.7.11" />
|
||||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||||
<PackageReference Include="RestSharp" Version="106.12.0" />
|
<PackageReference Include="RestSharp" Version="106.12.0" />
|
||||||
<PackageReference Include="Unity" Version="5.11.5" />
|
<PackageReference Include="Unity" Version="5.11.5" />
|
||||||
|
@ -4,7 +4,9 @@
|
|||||||
<TargetFrameworks>net5.0</TargetFrameworks>
|
<TargetFrameworks>net5.0</TargetFrameworks>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="NLog" Version="4.7.0" />
|
<PackageReference Include="DryIoc.dll" Version="4.7.4" />
|
||||||
|
<PackageReference Include="DryIoc.Microsoft.DependencyInjection" Version="5.1.0" />
|
||||||
|
<PackageReference Include="NLog" Version="4.7.11" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\NzbDrone.Common\Radarr.Common.csproj" />
|
<ProjectReference Include="..\NzbDrone.Common\Radarr.Common.csproj" />
|
||||||
|
@ -1,11 +1,14 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using DryIoc;
|
||||||
using NLog;
|
using NLog;
|
||||||
using NzbDrone.Common.Composition;
|
using NzbDrone.Common.Composition.Extensions;
|
||||||
using NzbDrone.Common.EnvironmentInfo;
|
using NzbDrone.Common.EnvironmentInfo;
|
||||||
using NzbDrone.Common.Extensions;
|
using NzbDrone.Common.Extensions;
|
||||||
using NzbDrone.Common.Instrumentation;
|
using NzbDrone.Common.Instrumentation;
|
||||||
|
using NzbDrone.Common.Instrumentation.Extensions;
|
||||||
using NzbDrone.Common.Processes;
|
using NzbDrone.Common.Processes;
|
||||||
using NzbDrone.Update.UpdateEngine;
|
using NzbDrone.Update.UpdateEngine;
|
||||||
|
|
||||||
@ -17,8 +20,6 @@ public class UpdateApp
|
|||||||
private readonly IProcessProvider _processProvider;
|
private readonly IProcessProvider _processProvider;
|
||||||
private static readonly Logger Logger = NzbDroneLogger.GetLogger(typeof(UpdateApp));
|
private static readonly Logger Logger = NzbDroneLogger.GetLogger(typeof(UpdateApp));
|
||||||
|
|
||||||
private static IContainer _container;
|
|
||||||
|
|
||||||
public UpdateApp(IInstallUpdateService installUpdateService, IProcessProvider processProvider)
|
public UpdateApp(IInstallUpdateService installUpdateService, IProcessProvider processProvider)
|
||||||
{
|
{
|
||||||
_installUpdateService = installUpdateService;
|
_installUpdateService = installUpdateService;
|
||||||
@ -34,9 +35,13 @@ public static void Main(string[] args)
|
|||||||
|
|
||||||
Logger.Info("Starting Radarr Update Client");
|
Logger.Info("Starting Radarr Update Client");
|
||||||
|
|
||||||
_container = UpdateContainerBuilder.Build(startupArgument);
|
var container = new Container(rules => rules.WithNzbDroneRules())
|
||||||
_container.Resolve<InitializeLogger>().Initialize();
|
.AutoAddServices(new List<string> { "Radarr.Update" })
|
||||||
_container.Resolve<UpdateApp>().Start(args);
|
.AddNzbDroneLogger()
|
||||||
|
.AddStartupContext(startupArgument);
|
||||||
|
|
||||||
|
container.Resolve<InitializeLogger>().Initialize();
|
||||||
|
container.Resolve<UpdateApp>().Start(args);
|
||||||
|
|
||||||
Logger.Info("Update completed successfully");
|
Logger.Info("Update completed successfully");
|
||||||
}
|
}
|
||||||
|
@ -1,24 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using NzbDrone.Common.Composition;
|
|
||||||
using NzbDrone.Common.EnvironmentInfo;
|
|
||||||
|
|
||||||
namespace NzbDrone.Update
|
|
||||||
{
|
|
||||||
public class UpdateContainerBuilder : ContainerBuilderBase
|
|
||||||
{
|
|
||||||
private UpdateContainerBuilder(IStartupContext startupContext, List<string> assemblies)
|
|
||||||
: base(startupContext, assemblies)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IContainer Build(IStartupContext startupContext)
|
|
||||||
{
|
|
||||||
var assemblies = new List<string>
|
|
||||||
{
|
|
||||||
"Radarr.Update"
|
|
||||||
};
|
|
||||||
|
|
||||||
return new UpdateContainerBuilder(startupContext, assemblies).Container;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -3,7 +3,7 @@
|
|||||||
<TargetFrameworks>net5.0</TargetFrameworks>
|
<TargetFrameworks>net5.0</TargetFrameworks>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="NLog" Version="4.7.0" />
|
<PackageReference Include="NLog" Version="4.7.11" />
|
||||||
<PackageReference Include="System.IO.FileSystem.AccessControl" Version="5.0.0" />
|
<PackageReference Include="System.IO.FileSystem.AccessControl" Version="5.0.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -1,13 +0,0 @@
|
|||||||
using System.Windows.Forms;
|
|
||||||
using Radarr.Host;
|
|
||||||
|
|
||||||
namespace NzbDrone
|
|
||||||
{
|
|
||||||
public class MessageBoxUserAlert : IUserAlert
|
|
||||||
{
|
|
||||||
public void Alert(string message)
|
|
||||||
{
|
|
||||||
MessageBox.Show(text: message, buttons: MessageBoxButtons.OK, icon: MessageBoxIcon.Warning, caption: "NzbDrone");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,6 +1,9 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
using NLog;
|
using NLog;
|
||||||
using NzbDrone.Common.EnvironmentInfo;
|
using NzbDrone.Common.EnvironmentInfo;
|
||||||
using NzbDrone.Common.Processes;
|
using NzbDrone.Common.Processes;
|
||||||
@ -8,12 +11,7 @@
|
|||||||
|
|
||||||
namespace NzbDrone.SysTray
|
namespace NzbDrone.SysTray
|
||||||
{
|
{
|
||||||
public interface ISystemTrayApp
|
public class SystemTrayApp : Form, IHostedService
|
||||||
{
|
|
||||||
void Start();
|
|
||||||
}
|
|
||||||
|
|
||||||
public class SystemTrayApp : Form, ISystemTrayApp
|
|
||||||
{
|
{
|
||||||
private readonly IBrowserService _browserService;
|
private readonly IBrowserService _browserService;
|
||||||
private readonly IRuntimeInfo _runtimeInfo;
|
private readonly IRuntimeInfo _runtimeInfo;
|
||||||
@ -34,8 +32,12 @@ public void Start()
|
|||||||
Application.ThreadException += OnThreadException;
|
Application.ThreadException += OnThreadException;
|
||||||
Application.ApplicationExit += OnApplicationExit;
|
Application.ApplicationExit += OnApplicationExit;
|
||||||
|
|
||||||
|
Application.SetHighDpiMode(HighDpiMode.PerMonitor);
|
||||||
|
Application.EnableVisualStyles();
|
||||||
|
Application.SetCompatibleTextRenderingDefault(false);
|
||||||
|
|
||||||
_trayMenu.Items.Add(new ToolStripMenuItem("Launch Browser", null, LaunchBrowser));
|
_trayMenu.Items.Add(new ToolStripMenuItem("Launch Browser", null, LaunchBrowser));
|
||||||
_trayMenu.Items.Add(new ToolStripMenuItem("-"));
|
_trayMenu.Items.Add(new ToolStripSeparator());
|
||||||
_trayMenu.Items.Add(new ToolStripMenuItem("Exit", null, OnExit));
|
_trayMenu.Items.Add(new ToolStripMenuItem("Exit", null, OnExit));
|
||||||
|
|
||||||
_trayIcon.Text = string.Format("Radarr - {0}", BuildInfo.Version);
|
_trayIcon.Text = string.Format("Radarr - {0}", BuildInfo.Version);
|
||||||
@ -48,6 +50,20 @@ public void Start()
|
|||||||
Application.Run(this);
|
Application.Run(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Task StartAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var thread = new Thread(Start);
|
||||||
|
thread.SetApartmentState(ApartmentState.STA);
|
||||||
|
thread.Start();
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task StopAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
protected override void OnClosing(CancelEventArgs e)
|
protected override void OnClosing(CancelEventArgs e)
|
||||||
{
|
{
|
||||||
DisposeTrayIcon();
|
DisposeTrayIcon();
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
using NLog;
|
using NLog;
|
||||||
using NzbDrone.Common.EnvironmentInfo;
|
using NzbDrone.Common.EnvironmentInfo;
|
||||||
using NzbDrone.Common.Instrumentation;
|
using NzbDrone.Common.Instrumentation;
|
||||||
@ -20,11 +22,9 @@ public static void Main(string[] args)
|
|||||||
|
|
||||||
NzbDroneLogger.Register(startupArgs, false, true);
|
NzbDroneLogger.Register(startupArgs, false, true);
|
||||||
|
|
||||||
Bootstrap.Start(startupArgs, new MessageBoxUserAlert(), container =>
|
Bootstrap.Start(args, e =>
|
||||||
{
|
{
|
||||||
container.Register<ISystemTrayApp, SystemTrayApp>();
|
e.ConfigureServices((_, s) => s.AddSingleton<IHostedService, SystemTrayApp>());
|
||||||
var trayApp = container.Resolve<ISystemTrayApp>();
|
|
||||||
trayApp.Start();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using NzbDrone.Common;
|
using NzbDrone.Common;
|
||||||
|
using NzbDrone.Common.Composition;
|
||||||
using NzbDrone.Common.Serializer;
|
using NzbDrone.Common.Serializer;
|
||||||
using NzbDrone.Common.TPL;
|
using NzbDrone.Common.TPL;
|
||||||
using NzbDrone.Core.Datastore.Events;
|
using NzbDrone.Core.Datastore.Events;
|
||||||
@ -22,17 +23,17 @@ namespace Radarr.Api.V3.Commands
|
|||||||
public class CommandController : RestControllerWithSignalR<CommandResource, CommandModel>, IHandle<CommandUpdatedEvent>
|
public class CommandController : RestControllerWithSignalR<CommandResource, CommandModel>, IHandle<CommandUpdatedEvent>
|
||||||
{
|
{
|
||||||
private readonly IManageCommandQueue _commandQueueManager;
|
private readonly IManageCommandQueue _commandQueueManager;
|
||||||
private readonly IServiceFactory _serviceFactory;
|
private readonly KnownTypes _knownTypes;
|
||||||
private readonly Debouncer _debouncer;
|
private readonly Debouncer _debouncer;
|
||||||
private readonly Dictionary<int, CommandResource> _pendingUpdates;
|
private readonly Dictionary<int, CommandResource> _pendingUpdates;
|
||||||
|
|
||||||
public CommandController(IManageCommandQueue commandQueueManager,
|
public CommandController(IManageCommandQueue commandQueueManager,
|
||||||
IBroadcastSignalRMessage signalRBroadcaster,
|
IBroadcastSignalRMessage signalRBroadcaster,
|
||||||
IServiceFactory serviceFactory)
|
KnownTypes knownTypes)
|
||||||
: base(signalRBroadcaster)
|
: base(signalRBroadcaster)
|
||||||
{
|
{
|
||||||
_commandQueueManager = commandQueueManager;
|
_commandQueueManager = commandQueueManager;
|
||||||
_serviceFactory = serviceFactory;
|
_knownTypes = knownTypes;
|
||||||
|
|
||||||
_debouncer = new Debouncer(SendUpdates, TimeSpan.FromSeconds(0.1));
|
_debouncer = new Debouncer(SendUpdates, TimeSpan.FromSeconds(0.1));
|
||||||
_pendingUpdates = new Dictionary<int, CommandResource>();
|
_pendingUpdates = new Dictionary<int, CommandResource>();
|
||||||
@ -49,7 +50,7 @@ public override CommandResource GetResourceById(int id)
|
|||||||
public ActionResult<CommandResource> StartCommand(CommandResource commandResource)
|
public ActionResult<CommandResource> StartCommand(CommandResource commandResource)
|
||||||
{
|
{
|
||||||
var commandType =
|
var commandType =
|
||||||
_serviceFactory.GetImplementations(typeof(Command))
|
_knownTypes.GetImplementations(typeof(Command))
|
||||||
.Single(c => c.Name.Replace("Command", "")
|
.Single(c => c.Name.Replace("Command", "")
|
||||||
.Equals(commandResource.Name, StringComparison.InvariantCultureIgnoreCase));
|
.Equals(commandResource.Name, StringComparison.InvariantCultureIgnoreCase));
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ public class ImportListController : ProviderControllerBase<ImportListResource, I
|
|||||||
{
|
{
|
||||||
public static readonly ImportListResourceMapper ResourceMapper = new ImportListResourceMapper();
|
public static readonly ImportListResourceMapper ResourceMapper = new ImportListResourceMapper();
|
||||||
|
|
||||||
public ImportListController(ImportListFactory importListFactory,
|
public ImportListController(IImportListFactory importListFactory,
|
||||||
ProfileExistsValidator profileExistsValidator)
|
ProfileExistsValidator profileExistsValidator)
|
||||||
: base(importListFactory, "importlist", ResourceMapper)
|
: base(importListFactory, "importlist", ResourceMapper)
|
||||||
{
|
{
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="FluentValidation" Version="8.6.2" />
|
<PackageReference Include="FluentValidation" Version="8.6.2" />
|
||||||
<PackageReference Include="Ical.Net" Version="4.1.11" />
|
<PackageReference Include="Ical.Net" Version="4.1.11" />
|
||||||
<PackageReference Include="NLog" Version="4.7.0" />
|
<PackageReference Include="NLog" Version="4.7.11" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\NzbDrone.Core\Radarr.Core.csproj" />
|
<ProjectReference Include="..\NzbDrone.Core\Radarr.Core.csproj" />
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
using Microsoft.AspNetCore.Authentication;
|
using Microsoft.AspNetCore.Authentication;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
|
using NzbDrone.Core.Configuration;
|
||||||
|
|
||||||
namespace Radarr.Http.Authentication
|
namespace Radarr.Http.Authentication
|
||||||
{
|
{
|
||||||
@ -17,17 +18,20 @@ public class ApiKeyAuthenticationOptions : AuthenticationSchemeOptions
|
|||||||
|
|
||||||
public string HeaderName { get; set; }
|
public string HeaderName { get; set; }
|
||||||
public string QueryName { get; set; }
|
public string QueryName { get; set; }
|
||||||
public string ApiKey { get; set; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ApiKeyAuthenticationHandler : AuthenticationHandler<ApiKeyAuthenticationOptions>
|
public class ApiKeyAuthenticationHandler : AuthenticationHandler<ApiKeyAuthenticationOptions>
|
||||||
{
|
{
|
||||||
|
private readonly string _apiKey;
|
||||||
|
|
||||||
public ApiKeyAuthenticationHandler(IOptionsMonitor<ApiKeyAuthenticationOptions> options,
|
public ApiKeyAuthenticationHandler(IOptionsMonitor<ApiKeyAuthenticationOptions> options,
|
||||||
ILoggerFactory logger,
|
ILoggerFactory logger,
|
||||||
UrlEncoder encoder,
|
UrlEncoder encoder,
|
||||||
ISystemClock clock)
|
ISystemClock clock,
|
||||||
|
IConfigFileProvider config)
|
||||||
: base(options, logger, encoder, clock)
|
: base(options, logger, encoder, clock)
|
||||||
{
|
{
|
||||||
|
_apiKey = config.ApiKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
private string ParseApiKey()
|
private string ParseApiKey()
|
||||||
@ -56,7 +60,7 @@ protected override Task<AuthenticateResult> HandleAuthenticateAsync()
|
|||||||
return Task.FromResult(AuthenticateResult.NoResult());
|
return Task.FromResult(AuthenticateResult.NoResult());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Options.ApiKey == providedApiKey)
|
if (_apiKey == providedApiKey)
|
||||||
{
|
{
|
||||||
var claims = new List<Claim>
|
var claims = new List<Claim>
|
||||||
{
|
{
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
using Microsoft.AspNetCore.Authentication;
|
using Microsoft.AspNetCore.Authentication;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using NzbDrone.Core.Authentication;
|
using NzbDrone.Core.Authentication;
|
||||||
using NzbDrone.Core.Configuration;
|
|
||||||
|
|
||||||
namespace Radarr.Http.Authentication
|
namespace Radarr.Http.Authentication
|
||||||
{
|
{
|
||||||
@ -13,53 +12,37 @@ public static AuthenticationBuilder AddApiKey(this AuthenticationBuilder authent
|
|||||||
return authenticationBuilder.AddScheme<ApiKeyAuthenticationOptions, ApiKeyAuthenticationHandler>(name, options);
|
return authenticationBuilder.AddScheme<ApiKeyAuthenticationOptions, ApiKeyAuthenticationHandler>(name, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static AuthenticationBuilder AddBasicAuthentication(this AuthenticationBuilder authenticationBuilder)
|
public static AuthenticationBuilder AddBasic(this AuthenticationBuilder authenticationBuilder, string name)
|
||||||
{
|
{
|
||||||
return authenticationBuilder.AddScheme<AuthenticationSchemeOptions, BasicAuthenticationHandler>(AuthenticationType.Basic.ToString(), options => { });
|
return authenticationBuilder.AddScheme<AuthenticationSchemeOptions, BasicAuthenticationHandler>(name, options => { });
|
||||||
}
|
}
|
||||||
|
|
||||||
public static AuthenticationBuilder AddNoAuthentication(this AuthenticationBuilder authenticationBuilder)
|
public static AuthenticationBuilder AddNone(this AuthenticationBuilder authenticationBuilder, string name)
|
||||||
{
|
{
|
||||||
return authenticationBuilder.AddScheme<AuthenticationSchemeOptions, NoAuthenticationHandler>(AuthenticationType.None.ToString(), options => { });
|
return authenticationBuilder.AddScheme<AuthenticationSchemeOptions, NoAuthenticationHandler>(name, options => { });
|
||||||
}
|
}
|
||||||
|
|
||||||
public static AuthenticationBuilder AddAppAuthentication(this IServiceCollection services, IConfigFileProvider config)
|
public static AuthenticationBuilder AddAppAuthentication(this IServiceCollection services)
|
||||||
{
|
{
|
||||||
var authBuilder = services.AddAuthentication(config.AuthenticationMethod.ToString());
|
return services.AddAuthentication()
|
||||||
|
.AddNone(AuthenticationType.None.ToString())
|
||||||
if (config.AuthenticationMethod == AuthenticationType.Basic)
|
.AddBasic(AuthenticationType.Basic.ToString())
|
||||||
{
|
.AddCookie(AuthenticationType.Forms.ToString(), options =>
|
||||||
authBuilder.AddBasicAuthentication();
|
|
||||||
}
|
|
||||||
else if (config.AuthenticationMethod == AuthenticationType.Forms)
|
|
||||||
{
|
|
||||||
authBuilder.AddCookie(AuthenticationType.Forms.ToString(), options =>
|
|
||||||
{
|
{
|
||||||
options.AccessDeniedPath = "/login?loginFailed=true";
|
options.AccessDeniedPath = "/login?loginFailed=true";
|
||||||
options.LoginPath = "/login";
|
options.LoginPath = "/login";
|
||||||
options.ExpireTimeSpan = TimeSpan.FromDays(7);
|
options.ExpireTimeSpan = TimeSpan.FromDays(7);
|
||||||
|
})
|
||||||
|
.AddApiKey("API", options =>
|
||||||
|
{
|
||||||
|
options.HeaderName = "X-Api-Key";
|
||||||
|
options.QueryName = "apikey";
|
||||||
|
})
|
||||||
|
.AddApiKey("SignalR", options =>
|
||||||
|
{
|
||||||
|
options.HeaderName = "X-Api-Key";
|
||||||
|
options.QueryName = "access_token";
|
||||||
});
|
});
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
authBuilder.AddNoAuthentication();
|
|
||||||
}
|
|
||||||
|
|
||||||
authBuilder.AddApiKey("API", options =>
|
|
||||||
{
|
|
||||||
options.HeaderName = "X-Api-Key";
|
|
||||||
options.QueryName = "apikey";
|
|
||||||
options.ApiKey = config.ApiKey;
|
|
||||||
});
|
|
||||||
|
|
||||||
authBuilder.AddApiKey("SignalR", options =>
|
|
||||||
{
|
|
||||||
options.HeaderName = "X-Api-Key";
|
|
||||||
options.QueryName = "access_token";
|
|
||||||
options.ApiKey = config.ApiKey;
|
|
||||||
});
|
|
||||||
|
|
||||||
return authBuilder;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
using Microsoft.AspNetCore.Authentication;
|
using Microsoft.AspNetCore.Authentication;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using NzbDrone.Core.Authentication;
|
||||||
using NzbDrone.Core.Configuration;
|
using NzbDrone.Core.Configuration;
|
||||||
|
|
||||||
namespace Radarr.Http.Authentication
|
namespace Radarr.Http.Authentication
|
||||||
@ -35,14 +36,14 @@ public async Task<IActionResult> Login([FromForm] LoginResource resource, [FromQ
|
|||||||
{
|
{
|
||||||
new Claim("user", user.Username),
|
new Claim("user", user.Username),
|
||||||
new Claim("identifier", user.Identifier.ToString()),
|
new Claim("identifier", user.Identifier.ToString()),
|
||||||
new Claim("UiAuth", "true")
|
new Claim("AuthType", AuthenticationType.Forms.ToString())
|
||||||
};
|
};
|
||||||
|
|
||||||
var authProperties = new AuthenticationProperties
|
var authProperties = new AuthenticationProperties
|
||||||
{
|
{
|
||||||
IsPersistent = resource.RememberMe == "on"
|
IsPersistent = resource.RememberMe == "on"
|
||||||
};
|
};
|
||||||
await HttpContext.SignInAsync(new ClaimsPrincipal(new ClaimsIdentity(claims, "Cookies", "user", "identifier")), authProperties);
|
await HttpContext.SignInAsync(AuthenticationType.Forms.ToString(), new ClaimsPrincipal(new ClaimsIdentity(claims, "Cookies", "user", "identifier")), authProperties);
|
||||||
|
|
||||||
return Redirect("/");
|
return Redirect("/");
|
||||||
}
|
}
|
||||||
@ -51,7 +52,7 @@ public async Task<IActionResult> Login([FromForm] LoginResource resource, [FromQ
|
|||||||
public async Task<IActionResult> Logout()
|
public async Task<IActionResult> Logout()
|
||||||
{
|
{
|
||||||
_authService.Logout(HttpContext);
|
_authService.Logout(HttpContext);
|
||||||
await HttpContext.SignOutAsync();
|
await HttpContext.SignOutAsync(AuthenticationType.Forms.ToString());
|
||||||
return Redirect("/");
|
return Redirect("/");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using NzbDrone.Common.EnvironmentInfo;
|
using NzbDrone.Common.EnvironmentInfo;
|
||||||
|
using NzbDrone.Core.Authentication;
|
||||||
|
|
||||||
namespace Radarr.Http.Authentication
|
namespace Radarr.Http.Authentication
|
||||||
{
|
{
|
||||||
@ -58,7 +59,7 @@ protected override Task<AuthenticateResult> HandleAuthenticateAsync()
|
|||||||
{
|
{
|
||||||
new Claim("user", user.Username),
|
new Claim("user", user.Username),
|
||||||
new Claim("identifier", user.Identifier.ToString()),
|
new Claim("identifier", user.Identifier.ToString()),
|
||||||
new Claim("UiAuth", "true")
|
new Claim("AuthType", AuthenticationType.Basic.ToString())
|
||||||
};
|
};
|
||||||
|
|
||||||
var identity = new ClaimsIdentity(claims, "Basic", "user", "identifier");
|
var identity = new ClaimsIdentity(claims, "Basic", "user", "identifier");
|
||||||
|
@ -5,13 +5,13 @@
|
|||||||
using Microsoft.AspNetCore.Authentication;
|
using Microsoft.AspNetCore.Authentication;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
|
using NzbDrone.Core.Authentication;
|
||||||
|
|
||||||
namespace Radarr.Http.Authentication
|
namespace Radarr.Http.Authentication
|
||||||
{
|
{
|
||||||
public class NoAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions>
|
public class NoAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions>
|
||||||
{
|
{
|
||||||
public NoAuthenticationHandler(IAuthenticationService authService,
|
public NoAuthenticationHandler(IOptionsMonitor<AuthenticationSchemeOptions> options,
|
||||||
IOptionsMonitor<AuthenticationSchemeOptions> options,
|
|
||||||
ILoggerFactory logger,
|
ILoggerFactory logger,
|
||||||
UrlEncoder encoder,
|
UrlEncoder encoder,
|
||||||
ISystemClock clock)
|
ISystemClock clock)
|
||||||
@ -24,7 +24,7 @@ protected override Task<AuthenticateResult> HandleAuthenticateAsync()
|
|||||||
var claims = new List<Claim>
|
var claims = new List<Claim>
|
||||||
{
|
{
|
||||||
new Claim("user", "Anonymous"),
|
new Claim("user", "Anonymous"),
|
||||||
new Claim("UiAuth", "true")
|
new Claim("AuthType", AuthenticationType.None.ToString())
|
||||||
};
|
};
|
||||||
|
|
||||||
var identity = new ClaimsIdentity(claims, "NoAuth", "user", "identifier");
|
var identity = new ClaimsIdentity(claims, "NoAuth", "user", "identifier");
|
||||||
|
@ -0,0 +1,39 @@
|
|||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using NzbDrone.Core.Configuration;
|
||||||
|
|
||||||
|
namespace NzbDrone.Http.Authentication
|
||||||
|
{
|
||||||
|
public class UiAuthorizationPolicyProvider : IAuthorizationPolicyProvider
|
||||||
|
{
|
||||||
|
private const string POLICY_NAME = "UI";
|
||||||
|
private readonly IConfigFileProvider _config;
|
||||||
|
|
||||||
|
public DefaultAuthorizationPolicyProvider FallbackPolicyProvider { get; }
|
||||||
|
|
||||||
|
public UiAuthorizationPolicyProvider(IOptions<AuthorizationOptions> options,
|
||||||
|
IConfigFileProvider config)
|
||||||
|
{
|
||||||
|
FallbackPolicyProvider = new DefaultAuthorizationPolicyProvider(options);
|
||||||
|
_config = config;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<AuthorizationPolicy> GetDefaultPolicyAsync() => FallbackPolicyProvider.GetDefaultPolicyAsync();
|
||||||
|
|
||||||
|
public Task<AuthorizationPolicy> GetFallbackPolicyAsync() => FallbackPolicyProvider.GetFallbackPolicyAsync();
|
||||||
|
|
||||||
|
public Task<AuthorizationPolicy> GetPolicyAsync(string policyName)
|
||||||
|
{
|
||||||
|
if (policyName.Equals(POLICY_NAME, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
var policy = new AuthorizationPolicyBuilder(_config.AuthenticationMethod.ToString())
|
||||||
|
.RequireAuthenticatedUser();
|
||||||
|
return Task.FromResult(policy.Build());
|
||||||
|
}
|
||||||
|
|
||||||
|
return FallbackPolicyProvider.GetPolicyAsync(policyName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -10,13 +10,13 @@ namespace Radarr.Http.Frontend.Mappers
|
|||||||
public abstract class HtmlMapperBase : StaticResourceMapperBase
|
public abstract class HtmlMapperBase : StaticResourceMapperBase
|
||||||
{
|
{
|
||||||
private readonly IDiskProvider _diskProvider;
|
private readonly IDiskProvider _diskProvider;
|
||||||
private readonly Func<ICacheBreakerProvider> _cacheBreakProviderFactory;
|
private readonly Lazy<ICacheBreakerProvider> _cacheBreakProviderFactory;
|
||||||
private static readonly Regex ReplaceRegex = new Regex(@"(?:(?<attribute>href|src)=\"")(?<path>.*?(?<extension>css|js|png|ico|ics|svg|json))(?:\"")(?:\s(?<nohash>data-no-hash))?", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
private static readonly Regex ReplaceRegex = new Regex(@"(?:(?<attribute>href|src)=\"")(?<path>.*?(?<extension>css|js|png|ico|ics|svg|json))(?:\"")(?:\s(?<nohash>data-no-hash))?", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||||
|
|
||||||
private string _generatedContent;
|
private string _generatedContent;
|
||||||
|
|
||||||
protected HtmlMapperBase(IDiskProvider diskProvider,
|
protected HtmlMapperBase(IDiskProvider diskProvider,
|
||||||
Func<ICacheBreakerProvider> cacheBreakProviderFactory,
|
Lazy<ICacheBreakerProvider> cacheBreakProviderFactory,
|
||||||
Logger logger)
|
Logger logger)
|
||||||
: base(diskProvider, logger)
|
: base(diskProvider, logger)
|
||||||
{
|
{
|
||||||
@ -47,7 +47,7 @@ protected string GetHtmlText()
|
|||||||
}
|
}
|
||||||
|
|
||||||
var text = _diskProvider.ReadAllText(HtmlPath);
|
var text = _diskProvider.ReadAllText(HtmlPath);
|
||||||
var cacheBreakProvider = _cacheBreakProviderFactory();
|
var cacheBreakProvider = _cacheBreakProviderFactory.Value;
|
||||||
|
|
||||||
text = ReplaceRegex.Replace(text, match =>
|
text = ReplaceRegex.Replace(text, match =>
|
||||||
{
|
{
|
||||||
|
@ -14,7 +14,7 @@ public class IndexHtmlMapper : HtmlMapperBase
|
|||||||
public IndexHtmlMapper(IAppFolderInfo appFolderInfo,
|
public IndexHtmlMapper(IAppFolderInfo appFolderInfo,
|
||||||
IDiskProvider diskProvider,
|
IDiskProvider diskProvider,
|
||||||
IConfigFileProvider configFileProvider,
|
IConfigFileProvider configFileProvider,
|
||||||
Func<ICacheBreakerProvider> cacheBreakProviderFactory,
|
Lazy<ICacheBreakerProvider> cacheBreakProviderFactory,
|
||||||
Logger logger)
|
Logger logger)
|
||||||
: base(diskProvider, cacheBreakProviderFactory, logger)
|
: base(diskProvider, cacheBreakProviderFactory, logger)
|
||||||
{
|
{
|
||||||
|
@ -11,7 +11,7 @@ public class LoginHtmlMapper : HtmlMapperBase
|
|||||||
{
|
{
|
||||||
public LoginHtmlMapper(IAppFolderInfo appFolderInfo,
|
public LoginHtmlMapper(IAppFolderInfo appFolderInfo,
|
||||||
IDiskProvider diskProvider,
|
IDiskProvider diskProvider,
|
||||||
Func<ICacheBreakerProvider> cacheBreakProviderFactory,
|
Lazy<ICacheBreakerProvider> cacheBreakProviderFactory,
|
||||||
IConfigFileProvider configFileProvider,
|
IConfigFileProvider configFileProvider,
|
||||||
Logger logger)
|
Logger logger)
|
||||||
: base(diskProvider, cacheBreakProviderFactory, logger)
|
: base(diskProvider, cacheBreakProviderFactory, logger)
|
||||||
|
30
src/Radarr.Http/Middleware/BufferingMiddleware.cs
Normal file
30
src/Radarr.Http/Middleware/BufferingMiddleware.cs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
|
||||||
|
namespace Radarr.Http.Middleware
|
||||||
|
{
|
||||||
|
public class BufferingMiddleware
|
||||||
|
{
|
||||||
|
private readonly RequestDelegate _next;
|
||||||
|
private readonly List<string> _urls;
|
||||||
|
|
||||||
|
public BufferingMiddleware(RequestDelegate next, List<string> urls)
|
||||||
|
{
|
||||||
|
_next = next;
|
||||||
|
_urls = urls;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task InvokeAsync(HttpContext context)
|
||||||
|
{
|
||||||
|
if (_urls.Any(p => context.Request.Path.StartsWithSegments(p, StringComparison.OrdinalIgnoreCase)))
|
||||||
|
{
|
||||||
|
context.Request.EnableBuffering();
|
||||||
|
}
|
||||||
|
|
||||||
|
await _next(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -5,7 +5,7 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="FluentValidation" Version="8.6.2" />
|
<PackageReference Include="FluentValidation" Version="8.6.2" />
|
||||||
<PackageReference Include="ImpromptuInterface" Version="7.0.1" />
|
<PackageReference Include="ImpromptuInterface" Version="7.0.1" />
|
||||||
<PackageReference Include="NLog" Version="4.7.0" />
|
<PackageReference Include="NLog" Version="4.7.11" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\NzbDrone.Core\Radarr.Core.csproj" />
|
<ProjectReference Include="..\NzbDrone.Core\Radarr.Core.csproj" />
|
||||||
|
@ -74,7 +74,7 @@ void LogDuplicates(IDfaNode node)
|
|||||||
|
|
||||||
// We can safely index into visited because this is a post-order traversal,
|
// We can safely index into visited because this is a post-order traversal,
|
||||||
// all of the children of this node are already in the dictionary.
|
// all of the children of this node are already in the dictionary.
|
||||||
var filteredMatches = node?.Matches?.Where(x => !x.DisplayName.StartsWith("Lidarr.Http.Frontend.StaticResourceController")).ToList();
|
var filteredMatches = node?.Matches?.Where(x => !x.DisplayName.StartsWith("Radarr.Http.Frontend.StaticResourceController")).ToList();
|
||||||
var matchCount = filteredMatches?.Count ?? 0;
|
var matchCount = filteredMatches?.Count ?? 0;
|
||||||
if (matchCount > 1)
|
if (matchCount > 1)
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user