using System; using System.Collections.Generic; using Lidarr.Api.V1.System; using Lidarr.Http; using Lidarr.Http.Authentication; using Lidarr.Http.ErrorManagement; using Lidarr.Http.Frontend; using Lidarr.Http.Middleware; 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 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("Lidarr.Http.Authentication", LogLevel.Information); b.AddNLog(); }); services.Configure(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(); 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 mainDatabaseFactory, Lazy logDatabaseFactory, DatabaseTarget dbTarget, ISingleInstancePolicy singleInstancePolicy, InitializeLogger initializeLogger, ReconfigureLogging reconfigureLogging, IAppFolderFactory appFolderFactory, IProvidePidFile pidFileProvider, IConfigFileProvider configFileProvider, IRuntimeInfo runtimeInfo, IFirewallAdapter firewallAdapter, LidarrErrorPipeline 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.UseForwardedHeaders(); app.UseMiddleware(); 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(); app.UseMiddleware(configFileProvider.UrlBase); app.UseMiddleware(); app.UseMiddleware(); app.UseMiddleware(new List { "/api/v1/command" }); app.UseWebSockets(); app.UseEndpoints(x => { x.MapHub("/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(); } } } }