using System; using System.Collections.Generic; using System.IO; using System.Linq; using Autofac; using Autofac.Extras.AttributeMetadata; using Kyoo.Authentication; using Kyoo.Controllers; using Kyoo.Models.Options; using Kyoo.Postgresql; using Kyoo.SqLite; using Kyoo.Tasks; using Kyoo.TheMovieDb; using Kyoo.TheTvdb; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.SpaServices.AngularCli; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; namespace Kyoo { /// /// The Startup class is used to configure the AspNet's webhost. /// public class PluginsStartup { /// /// A plugin manager used to load plugins and allow them to configure services / asp net. /// private readonly IPluginManager _plugins; /// /// The configuration used to register and so on for plugin's specified types. /// private readonly IConfiguration _configuration; /// /// Created from the DI container, those services are needed to load information and instantiate plugins.s /// /// The plugin manager to use to load new plugins and configure the host. /// /// The configuration used to register and so on for plugin's specified types. /// public PluginsStartup(IPluginManager plugins, IConfiguration configuration) { _plugins = plugins; _configuration = configuration; _plugins.LoadPlugins( typeof(CoreModule), typeof(AuthenticationModule), typeof(PostgresModule), typeof(SqLiteModule), typeof(PluginTvdb), typeof(PluginTmdb) ); } /// /// Configure the services context via the . /// /// The service collection to fill. public void ConfigureServices(IServiceCollection services) { services.AddMvc().AddControllersAsServices(); services.AddSpaStaticFiles(x => { x.RootPath = Path.Join(AppDomain.CurrentDomain.BaseDirectory, "wwwroot"); }); services.AddResponseCompression(x => { x.EnableForHttps = true; }); services.AddHttpClient(); _plugins.ConfigureServices(services); IEnumerable> configTypes = _plugins.GetAllPlugins() .SelectMany(x => x.Configuration) .Where(x => x.Value != null); foreach ((string path, Type type) in configTypes) { Utility.RunGenericMethod( typeof(OptionsConfigurationServiceCollectionExtensions), nameof(OptionsConfigurationServiceCollectionExtensions.Configure), type, services, _configuration.GetSection(path) ); } } /// /// Configure the autofac container via the . /// /// The builder to configure. public void ConfigureContainer(ContainerBuilder builder) { builder.RegisterModule(); builder.RegisterInstance(_plugins).As().ExternallyOwned(); builder.RegisterTask(); _plugins.ConfigureContainer(builder); } /// /// Configure the asp net host. /// /// The asp net host to configure /// The host environment (is the app in development mode?) public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IConfigurationManager config, ILifetimeScope container) { if (!env.IsDevelopment()) app.UseSpaStaticFiles(); app.Use((ctx, next) => { ctx.Response.Headers.Remove("X-Powered-By"); ctx.Response.Headers.Remove("Server"); ctx.Response.Headers.Add("Feature-Policy", "autoplay 'self'; fullscreen"); ctx.Response.Headers.Add("Content-Security-Policy", "default-src 'self' blob:; script-src 'self' blob: 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; frame-src 'self' https://www.youtube.com"); ctx.Response.Headers.Add("X-Frame-Options", "SAMEORIGIN"); ctx.Response.Headers.Add("Referrer-Policy", "no-referrer"); ctx.Response.Headers.Add("Access-Control-Allow-Origin", "null"); ctx.Response.Headers.Add("X-Content-Type-Options", "nosniff"); return next(); }); app.UseResponseCompression(); IEnumerable steps = _plugins.GetAllPlugins() .SelectMany(x => x.ConfigureSteps) .OrderByDescending(x => x.Priority); using ILifetimeScope scope = container.BeginLifetimeScope(x => x.RegisterInstance(app).SingleInstance().ExternallyOwned()); IServiceProvider provider = scope.Resolve(); foreach (IStartupAction step in steps) step.Run(provider); IEnumerable> pluginConfig = _plugins.GetAllPlugins() .SelectMany(x => x.Configuration) .GroupBy(x => x.Key.Split(':').First()) .Select(x => x .OrderBy(y => y.Key.Length) .First() ); foreach ((string path, Type type) in pluginConfig) config.Register(path, type); app.UseSpa(spa => { spa.Options.SourcePath = Path.Join(AppDomain.CurrentDomain.BaseDirectory, "Kyoo.WebApp"); if (env.IsDevelopment()) spa.UseAngularCliServer("start"); }); } /// /// Create a new from a webhost. /// This is meant to be used from . /// /// The context of the web host. /// /// The method used to configure the logger factory used by the plugin manager and plugins during startup. /// /// A new . public static PluginsStartup FromWebHost(WebHostBuilderContext host, Action loggingConfiguration) { HostBuilderContext genericHost = new(new Dictionary()) { Configuration = host.Configuration, HostingEnvironment = host.HostingEnvironment }; ILoggerFactory logger = LoggerFactory.Create(builder => loggingConfiguration(genericHost, builder)); HostServiceProvider hostProvider = new(host.HostingEnvironment, host.Configuration, logger); PluginManager plugins = new( hostProvider, Options.Create(host.Configuration.GetSection(BasicOptions.Path).Get()), logger.CreateLogger() ); return new PluginsStartup(plugins, host.Configuration); } /// /// A simple host service provider used to activate plugins instance. /// The same services as a generic host are available and an has been added. /// private class HostServiceProvider : IServiceProvider { /// /// The host environment that could be used by plugins to configure themself. /// private readonly IWebHostEnvironment _hostEnvironment; /// /// The configuration context. /// private readonly IConfiguration _configuration; /// /// A logger factory used to create a logger for the plugin manager. /// private readonly ILoggerFactory _loggerFactory; /// /// Create a new that will return given services when asked. /// /// /// The host environment that could be used by plugins to configure themself. /// /// The configuration context /// A logger factory used to create a logger for the plugin manager. public HostServiceProvider(IWebHostEnvironment hostEnvironment, IConfiguration configuration, ILoggerFactory loggerFactory) { _hostEnvironment = hostEnvironment; _configuration = configuration; _loggerFactory = loggerFactory; } /// public object GetService(Type serviceType) { if (serviceType == typeof(IWebHostEnvironment) || serviceType == typeof(IHostEnvironment)) return _hostEnvironment; if (serviceType == typeof(IConfiguration)) return _configuration; if (serviceType == typeof(ILoggerFactory)) return _loggerFactory; return null; } } } }