From e35b4f55277b7d230d0cc28a5f3631b79d632219 Mon Sep 17 00:00:00 2001 From: Zoe Roux Date: Fri, 13 Aug 2021 21:40:30 +0200 Subject: [PATCH] WebApp: Moving SPA logics to a new module --- .../Kyoo.Authentication.csproj | 16 +-- Kyoo.Common/Controllers/IPluginManager.cs | 14 --- Kyoo.Common/Controllers/IRepository.cs | 28 ++--- Kyoo.Common/Models/Resources/Track.cs | 6 +- Kyoo.Common/Utility/TaskUtils.cs | 66 +++++------ Kyoo.CommonAPI/Kyoo.CommonAPI.csproj | 2 +- Kyoo.Postgresql/Kyoo.Postgresql.csproj | 6 +- Kyoo.SqLite/Kyoo.SqLite.csproj | 6 +- Kyoo.TheMovieDb/Kyoo.TheMovieDb.csproj | 3 +- Kyoo.TheTvdb/Kyoo.TheTvdb.csproj | 3 +- Kyoo.WebApp | 2 +- Kyoo.sln | 6 + .../FileSystems/LocalFileSystem.cs | 15 +-- Kyoo/Controllers/PluginManager.cs | 15 --- Kyoo/CoreModule.cs | 37 ++++--- Kyoo/Kyoo.csproj | 104 +++--------------- Kyoo/PluginsStartup.cs | 57 ++-------- tests/Kyoo.Tests/Database/RepositoryTests.cs | 2 +- 18 files changed, 119 insertions(+), 269 deletions(-) diff --git a/Kyoo.Authentication/Kyoo.Authentication.csproj b/Kyoo.Authentication/Kyoo.Authentication.csproj index 2cde6c7e..47113130 100644 --- a/Kyoo.Authentication/Kyoo.Authentication.csproj +++ b/Kyoo.Authentication/Kyoo.Authentication.csproj @@ -2,18 +2,12 @@ net5.0 - ../Kyoo/bin/$(Configuration)/$(TargetFramework)/plugins/authentication - false - false - false - false - true - + ../Kyoo.WebLogin/ + SDG Zoe Roux https://github.com/AnonymusRaccoon/Kyoo default - ../Kyoo.WebLogin/ @@ -23,11 +17,7 @@ - - all - false - runtime - + diff --git a/Kyoo.Common/Controllers/IPluginManager.cs b/Kyoo.Common/Controllers/IPluginManager.cs index 294aabb2..7bf64baf 100644 --- a/Kyoo.Common/Controllers/IPluginManager.cs +++ b/Kyoo.Common/Controllers/IPluginManager.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; -using Autofac; using Kyoo.Models.Exceptions; -using Microsoft.Extensions.DependencyInjection; namespace Kyoo.Controllers { @@ -50,17 +48,5 @@ namespace Kyoo.Controllers /// You should not try to put plugins from the plugins directory here as they will get automatically loaded. /// public void LoadPlugins(params Type[] plugins); - - /// - /// Configure container adding or removing services as the plugins wants. - /// - /// The container to populate - void ConfigureContainer(ContainerBuilder builder); - - /// - /// Configure services via the microsoft way. This allow libraries to add their services. - /// - /// The service collection to populate - public void ConfigureServices(IServiceCollection services); } } \ No newline at end of file diff --git a/Kyoo.Common/Controllers/IRepository.cs b/Kyoo.Common/Controllers/IRepository.cs index c5fe7a7c..f221bbcf 100644 --- a/Kyoo.Common/Controllers/IRepository.cs +++ b/Kyoo.Common/Controllers/IRepository.cs @@ -309,20 +309,20 @@ namespace Kyoo.Controllers Task Get(string showSlug, int seasonNumber); /// - /// Get a season from it's showID and it's seasonNumber or null if it is not found. - /// - /// The id of the show - /// The season's number - /// The season found - Task GetOrDefault(int showID, int seasonNumber); - - /// - /// Get a season from it's show slug and it's seasonNumber or null if it is not found. - /// - /// The slug of the show - /// The season's number - /// The season found - Task GetOrDefault(string showSlug, int seasonNumber); + /// Get a season from it's showID and it's seasonNumber or null if it is not found. + /// + /// The id of the show + /// The season's number + /// The season found + Task GetOrDefault(int showID, int seasonNumber); + + /// + /// Get a season from it's show slug and it's seasonNumber or null if it is not found. + /// + /// The slug of the show + /// The season's number + /// The season found + Task GetOrDefault(string showSlug, int seasonNumber); } /// diff --git a/Kyoo.Common/Models/Resources/Track.cs b/Kyoo.Common/Models/Resources/Track.cs index d82cdbe1..d4454f5f 100644 --- a/Kyoo.Common/Models/Resources/Track.cs +++ b/Kyoo.Common/Models/Resources/Track.cs @@ -60,9 +60,9 @@ namespace Kyoo.Models } /// - /// The slug of the episode that contain this track. If this is not set, this track is ill-formed. - /// - [SerializeIgnore] public string EpisodeSlug { private get; set; } + /// The slug of the episode that contain this track. If this is not set, this track is ill-formed. + /// + [SerializeIgnore] public string EpisodeSlug { private get; set; } /// /// The title of the stream. diff --git a/Kyoo.Common/Utility/TaskUtils.cs b/Kyoo.Common/Utility/TaskUtils.cs index 78332cc9..a0d04b03 100644 --- a/Kyoo.Common/Utility/TaskUtils.cs +++ b/Kyoo.Common/Utility/TaskUtils.cs @@ -21,17 +21,17 @@ namespace Kyoo /// /// The source task has been canceled. public static Task Then(this Task task, Action then) - { - return task.ContinueWith(x => - { - if (x.IsFaulted) - x.Exception!.InnerException!.ReThrow(); - if (x.IsCanceled) - throw new TaskCanceledException(); - then(x.Result); - return x.Result; - }, TaskContinuationOptions.ExecuteSynchronously); - } + { + return task.ContinueWith(x => + { + if (x.IsFaulted) + x.Exception!.InnerException!.ReThrow(); + if (x.IsCanceled) + throw new TaskCanceledException(); + then(x.Result); + return x.Result; + }, TaskContinuationOptions.ExecuteSynchronously); + } /// /// Map the result of a task to another result. @@ -42,28 +42,28 @@ namespace Kyoo /// The resulting task after the mapping method /// A task wrapping the initial task and mapping the initial result. /// The source task has been canceled. - public static Task Map(this Task task, Func map) - { - return task.ContinueWith(x => - { - if (x.IsFaulted) - x.Exception!.InnerException!.ReThrow(); - if (x.IsCanceled) - throw new TaskCanceledException(); - return map(x.Result); - }, TaskContinuationOptions.ExecuteSynchronously); - } + public static Task Map(this Task task, Func map) + { + return task.ContinueWith(x => + { + if (x.IsFaulted) + x.Exception!.InnerException!.ReThrow(); + if (x.IsCanceled) + throw new TaskCanceledException(); + return map(x.Result); + }, TaskContinuationOptions.ExecuteSynchronously); + } - /// - /// A method to return the a default value from a task if the initial task is null. - /// - /// The initial task - /// The type that the task will return - /// A non-null task. - [NotNull] - public static Task DefaultIfNull([CanBeNull] Task value) - { - return value ?? Task.FromResult(default); - } + /// + /// A method to return the a default value from a task if the initial task is null. + /// + /// The initial task + /// The type that the task will return + /// A non-null task. + [NotNull] + public static Task DefaultIfNull([CanBeNull] Task value) + { + return value ?? Task.FromResult(default); + } } } \ No newline at end of file diff --git a/Kyoo.CommonAPI/Kyoo.CommonAPI.csproj b/Kyoo.CommonAPI/Kyoo.CommonAPI.csproj index 451c387d..0eca28f1 100644 --- a/Kyoo.CommonAPI/Kyoo.CommonAPI.csproj +++ b/Kyoo.CommonAPI/Kyoo.CommonAPI.csproj @@ -19,7 +19,7 @@ - + diff --git a/Kyoo.Postgresql/Kyoo.Postgresql.csproj b/Kyoo.Postgresql/Kyoo.Postgresql.csproj index a53ba3b7..a1bcaa3f 100644 --- a/Kyoo.Postgresql/Kyoo.Postgresql.csproj +++ b/Kyoo.Postgresql/Kyoo.Postgresql.csproj @@ -18,9 +18,7 @@ - - - - + + diff --git a/Kyoo.SqLite/Kyoo.SqLite.csproj b/Kyoo.SqLite/Kyoo.SqLite.csproj index cdedc4ca..e61fd702 100644 --- a/Kyoo.SqLite/Kyoo.SqLite.csproj +++ b/Kyoo.SqLite/Kyoo.SqLite.csproj @@ -18,9 +18,7 @@ - - - - + + diff --git a/Kyoo.TheMovieDb/Kyoo.TheMovieDb.csproj b/Kyoo.TheMovieDb/Kyoo.TheMovieDb.csproj index 72b8bdec..b423b046 100644 --- a/Kyoo.TheMovieDb/Kyoo.TheMovieDb.csproj +++ b/Kyoo.TheMovieDb/Kyoo.TheMovieDb.csproj @@ -17,7 +17,6 @@ - - + diff --git a/Kyoo.TheTvdb/Kyoo.TheTvdb.csproj b/Kyoo.TheTvdb/Kyoo.TheTvdb.csproj index 9553bef5..11392cf7 100644 --- a/Kyoo.TheTvdb/Kyoo.TheTvdb.csproj +++ b/Kyoo.TheTvdb/Kyoo.TheTvdb.csproj @@ -16,7 +16,6 @@ - - + diff --git a/Kyoo.WebApp b/Kyoo.WebApp index dca10903..dc37ce39 160000 --- a/Kyoo.WebApp +++ b/Kyoo.WebApp @@ -1 +1 @@ -Subproject commit dca10903ff54a8999732695b5c2a0a5c94f85200 +Subproject commit dc37ce398d6c9cacc7703e21552a116c42b548ed diff --git a/Kyoo.sln b/Kyoo.sln index fdb1f643..599922a3 100644 --- a/Kyoo.sln +++ b/Kyoo.sln @@ -17,6 +17,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kyoo.TheMovieDb", "Kyoo.The EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kyoo.Tests", "tests\Kyoo.Tests\Kyoo.Tests.csproj", "{0C8AA7EA-E723-4532-852F-35AA4E8AFED5}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kyoo.WebApp", "Kyoo.WebApp\Kyoo.WebApp.csproj", "{2374D500-1ADB-4752-85DB-8BB0DDF5A8E8}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -59,5 +61,9 @@ Global {0C8AA7EA-E723-4532-852F-35AA4E8AFED5}.Debug|Any CPU.Build.0 = Debug|Any CPU {0C8AA7EA-E723-4532-852F-35AA4E8AFED5}.Release|Any CPU.ActiveCfg = Release|Any CPU {0C8AA7EA-E723-4532-852F-35AA4E8AFED5}.Release|Any CPU.Build.0 = Release|Any CPU + {2374D500-1ADB-4752-85DB-8BB0DDF5A8E8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2374D500-1ADB-4752-85DB-8BB0DDF5A8E8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2374D500-1ADB-4752-85DB-8BB0DDF5A8E8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2374D500-1ADB-4752-85DB-8BB0DDF5A8E8}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal diff --git a/Kyoo/Controllers/FileSystems/LocalFileSystem.cs b/Kyoo/Controllers/FileSystems/LocalFileSystem.cs index 87575e0a..d5a19a68 100644 --- a/Kyoo/Controllers/FileSystems/LocalFileSystem.cs +++ b/Kyoo/Controllers/FileSystems/LocalFileSystem.cs @@ -20,7 +20,7 @@ namespace Kyoo.Controllers /// /// An extension provider to get content types from files extensions. /// - private FileExtensionContentTypeProvider _provider; + private readonly IContentTypeProvider _provider; /// /// Options to check if the metadata should be kept in the show directory or in a kyoo's directory. @@ -31,9 +31,11 @@ namespace Kyoo.Controllers /// Create a new with the specified options. /// /// The options to use. - public LocalFileSystem(IOptionsMonitor options) + /// An extension provider to get content types from files extensions. + public LocalFileSystem(IOptionsMonitor options, IContentTypeProvider provider) { _options = options; + _provider = provider; } /// @@ -44,15 +46,6 @@ namespace Kyoo.Controllers /// The content type of the file private string _GetContentType(string path) { - if (_provider == null) - { - _provider = new FileExtensionContentTypeProvider(); - _provider.Mappings[".mkv"] = "video/x-matroska"; - _provider.Mappings[".ass"] = "text/x-ssa"; - _provider.Mappings[".srt"] = "application/x-subrip"; - _provider.Mappings[".m3u8"] = "application/x-mpegurl"; - } - if (_provider.TryGetContentType(path, out string contentType)) return contentType; throw new NotImplementedException($"Can't get the content type of the file at: {path}"); diff --git a/Kyoo/Controllers/PluginManager.cs b/Kyoo/Controllers/PluginManager.cs index 04dacccc..2727124c 100644 --- a/Kyoo/Controllers/PluginManager.cs +++ b/Kyoo/Controllers/PluginManager.cs @@ -4,7 +4,6 @@ using System.IO; using System.Linq; using System.Reflection; using System.Runtime.Loader; -using Autofac; using Kyoo.Models.Options; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; @@ -133,20 +132,6 @@ namespace Kyoo.Controllers ); } - /// - public void ConfigureContainer(ContainerBuilder builder) - { - foreach (IPlugin plugin in _plugins) - plugin.Configure(builder); - } - - /// - public void ConfigureServices(IServiceCollection services) - { - foreach (IPlugin plugin in _plugins) - plugin.Configure(services); - } - /// /// A custom to load plugin's dependency if they are on the same folder. /// diff --git a/Kyoo/CoreModule.cs b/Kyoo/CoreModule.cs index 6c049aee..0ac9485a 100644 --- a/Kyoo/CoreModule.cs +++ b/Kyoo/CoreModule.cs @@ -1,9 +1,9 @@ using System; using System.Collections.Generic; -using System.IO; using Autofac; using Autofac.Core; using Autofac.Core.Registration; +using Autofac.Extras.AttributeMetadata; using Kyoo.Controllers; using Kyoo.Models.Options; using Kyoo.Models.Permissions; @@ -12,8 +12,8 @@ using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.StaticFiles; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Hosting; +using IMetadataProvider = Kyoo.Controllers.IMetadataProvider; namespace Kyoo { @@ -60,6 +60,8 @@ namespace Kyoo /// public void Configure(ContainerBuilder builder) { + builder.RegisterModule(); + builder.RegisterComposite().InstancePerLifetimeScope(); builder.RegisterType().As().SingleInstance(); builder.RegisterType().As().SingleInstance(); @@ -98,13 +100,24 @@ namespace Kyoo builder.RegisterType().As() .IfNotRegistered(typeof(IPermissionValidator)); + + builder.RegisterType().As().SingleInstance() + .OnActivating(x => + { + x.Instance.Mappings[".data"] = "application/octet-stream"; + x.Instance.Mappings[".mkv"] = "video/x-matroska"; + x.Instance.Mappings[".ass"] = "text/x-ssa"; + x.Instance.Mappings[".srt"] = "application/x-subrip"; + x.Instance.Mappings[".m3u8"] = "application/x-mpegurl"; + }); } /// - public void Configure(IServiceCollection services) + public void Configure(IServiceCollection services) { string publicUrl = _configuration.GetPublicUrl(); + services.AddMvc().AddControllersAsServices(); services.AddControllers() .AddNewtonsoftJson(x => { @@ -112,6 +125,13 @@ namespace Kyoo x.SerializerSettings.Converters.Add(new PeopleRoleConverter()); }); + services.AddResponseCompression(x => + { + x.EnableForHttps = true; + }); + + services.AddHttpClient(); + services.AddHostedService(x => x.GetService() as TaskManager); } @@ -128,17 +148,8 @@ namespace Kyoo app.UseHsts(); } }, SA.Before), + SA.New(app => app.UseResponseCompression(), SA.Routing + 1), SA.New(app => app.UseRouting(), SA.Routing), - SA.New(app => - { - FileExtensionContentTypeProvider contentTypeProvider = new(); - contentTypeProvider.Mappings[".data"] = "application/octet-stream"; - app.UseStaticFiles(new StaticFileOptions - { - ContentTypeProvider = contentTypeProvider, - FileProvider = new PhysicalFileProvider(Path.Join(AppDomain.CurrentDomain.BaseDirectory, "wwwroot")) - }); - }, SA.StaticFiles), SA.New(app => app.UseEndpoints(x => x.MapControllers()), SA.Endpoint) }; } diff --git a/Kyoo/Kyoo.csproj b/Kyoo/Kyoo.csproj index 0185da01..37200c34 100644 --- a/Kyoo/Kyoo.csproj +++ b/Kyoo/Kyoo.csproj @@ -2,15 +2,7 @@ net5.0 - true - Latest - false - ../Kyoo.WebApp/ ../Kyoo.Transcoder/ - $(DefaultItemExcludes);$(SpaRoot)node_modules/** - - - false SDG Zoe Roux @@ -33,79 +25,30 @@ - - - - - - - + - - - - - - - - - + + + + + + + + - - - - - - - - - - - - - - - - - wwwroot/%(DistFiles.Filename)%(DistFiles.Extension) - PreserveNewest - true - - - - - - - - wwwroot/%(StaticFiles.RecursiveDir)%(StaticFiles.Filename)%(StaticFiles.Extension) - PreserveNewest - true - - - - - - - - - - - - - - - - - + + @@ -115,25 +58,4 @@ false - - - - - ../Kyoo.WebLogin/ - - - - - - - - login/%(LoginFiles.RecursiveDir)%(LoginFiles.Filename)%(LoginFiles.Extension) - PreserveNewest - true - - - - - - diff --git a/Kyoo/PluginsStartup.cs b/Kyoo/PluginsStartup.cs index 189ebb92..22ba3e18 100644 --- a/Kyoo/PluginsStartup.cs +++ b/Kyoo/PluginsStartup.cs @@ -1,9 +1,7 @@ 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; @@ -14,7 +12,6 @@ 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; @@ -65,20 +62,9 @@ namespace Kyoo /// The service collection to fill. public void ConfigureServices(IServiceCollection services) { - services.AddMvc().AddControllersAsServices(); + foreach (IPlugin plugin in _plugins.GetAllPlugins()) + plugin.Configure(services); - 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); @@ -99,41 +85,26 @@ namespace Kyoo /// The builder to configure. public void ConfigureContainer(ContainerBuilder builder) { - builder.RegisterModule(); builder.RegisterInstance(_plugins).As().ExternallyOwned(); builder.RegisterTask(); - _plugins.ConfigureContainer(builder); + + foreach (IPlugin plugin in _plugins.GetAllPlugins()) + plugin.Configure(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) + /// An autofac container used to create a new scope to configure asp-net. + /// The configuration manager used to register strongly typed config. + public void Configure(IApplicationBuilder app, ILifetimeScope container, IConfigurationManager config) { - 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 => + using ILifetimeScope scope = container.BeginLifetimeScope(x => x.RegisterInstance(app).SingleInstance().ExternallyOwned()); IServiceProvider provider = scope.Resolve(); foreach (IStartupAction step in steps) @@ -148,14 +119,6 @@ namespace Kyoo ); 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"); - }); } diff --git a/tests/Kyoo.Tests/Database/RepositoryTests.cs b/tests/Kyoo.Tests/Database/RepositoryTests.cs index 4cc72b4c..2c984568 100644 --- a/tests/Kyoo.Tests/Database/RepositoryTests.cs +++ b/tests/Kyoo.Tests/Database/RepositoryTests.cs @@ -64,7 +64,7 @@ namespace Kyoo.Tests [Fact] public async Task GetByFakeSlugTest() { - await Assert.ThrowsAsync(() => _repository.Get("non-existent")); + await Assert.ThrowsAsync(() => _repository.Get("non-existent")); } [Fact]