|
|
|
using System;
|
|
|
|
using System.Collections.Generic;
|
|
|
|
using DryIoc;
|
|
|
|
using Microsoft.AspNetCore.Authorization;
|
|
|
|
using Microsoft.AspNetCore.Builder;
|
|
|
|
using Microsoft.AspNetCore.Http;
|
|
|
|
using Microsoft.AspNetCore.HttpOverrides;
|
|
|
|
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.Host.AccessControl;
|
|
|
|
using NzbDrone.Http.Authentication;
|
|
|
|
using NzbDrone.SignalR;
|
|
|
|
using Readarr.Api.V1.System;
|
|
|
|
using Readarr.Http;
|
|
|
|
using Readarr.Http.Authentication;
|
|
|
|
using Readarr.Http.ErrorManagement;
|
|
|
|
using Readarr.Http.Frontend;
|
|
|
|
using Readarr.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("Readarr.Http.Authentication", LogLevel.Information);
|
|
|
|
b.AddNLog();
|
|
|
|
});
|
|
|
|
|
|
|
|
services.Configure<ForwardedHeadersOptions>(options =>
|
|
|
|
{
|
|
|
|
options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
|
|
|
|
options.KnownNetworks.Clear();
|
|
|
|
options.KnownProxies.Clear();
|
|
|
|
});
|
|
|
|
|
|
|
|
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,
|
|
|
|
Lazy<ICacheDatabase> cacheDatabaseFactory,
|
|
|
|
DatabaseTarget dbTarget,
|
|
|
|
ISingleInstancePolicy singleInstancePolicy,
|
|
|
|
InitializeLogger initializeLogger,
|
|
|
|
ReconfigureLogging reconfigureLogging,
|
|
|
|
IAppFolderFactory appFolderFactory,
|
|
|
|
IProvidePidFile pidFileProvider,
|
|
|
|
IConfigFileProvider configFileProvider,
|
|
|
|
IRuntimeInfo runtimeInfo,
|
|
|
|
IFirewallAdapter firewallAdapter,
|
|
|
|
ReadarrErrorPipeline errorHandler)
|
|
|
|
{
|
|
|
|
initializeLogger.Initialize();
|
|
|
|
appFolderFactory.Register();
|
|
|
|
pidFileProvider.Write();
|
|
|
|
|
|
|
|
reconfigureLogging.Reconfigure();
|
|
|
|
|
|
|
|
EnsureSingleInstance(false, startupContext, singleInstancePolicy);
|
|
|
|
|
|
|
|
// instantiate the databases to initialize/migrate them
|
|
|
|
_ = mainDatabaseFactory.Value;
|
|
|
|
_ = logDatabaseFactory.Value;
|
|
|
|
_ = cacheDatabaseFactory.Value;
|
|
|
|
|
|
|
|
dbTarget.Register();
|
|
|
|
|
|
|
|
if (OsInfo.IsNotWindows)
|
|
|
|
{
|
|
|
|
Console.CancelKeyPress += (sender, eventArgs) => NLog.LogManager.Configuration = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (OsInfo.IsWindows && runtimeInfo.IsAdmin)
|
|
|
|
{
|
|
|
|
firewallAdapter.MakeAccessible();
|
|
|
|
}
|
|
|
|
|
|
|
|
app.UseForwardedHeaders();
|
|
|
|
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/v1/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();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|