WebApp: Moving SPA logics to a new module

This commit is contained in:
Zoe Roux 2021-08-13 21:40:30 +02:00
parent 16eca6da8e
commit e35b4f5527
18 changed files with 119 additions and 269 deletions

View File

@ -2,18 +2,12 @@
<PropertyGroup> <PropertyGroup>
<TargetFramework>net5.0</TargetFramework> <TargetFramework>net5.0</TargetFramework>
<OutputPath>../Kyoo/bin/$(Configuration)/$(TargetFramework)/plugins/authentication</OutputPath> <LoginRoot>../Kyoo.WebLogin/</LoginRoot>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<ProduceReferenceAssembly>false</ProduceReferenceAssembly>
<GenerateDependencyFile>false</GenerateDependencyFile>
<GenerateRuntimeConfigurationFiles>false</GenerateRuntimeConfigurationFiles>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
<Company>SDG</Company> <Company>SDG</Company>
<Authors>Zoe Roux</Authors> <Authors>Zoe Roux</Authors>
<RepositoryUrl>https://github.com/AnonymusRaccoon/Kyoo</RepositoryUrl> <RepositoryUrl>https://github.com/AnonymusRaccoon/Kyoo</RepositoryUrl>
<LangVersion>default</LangVersion> <LangVersion>default</LangVersion>
<LoginRoot>../Kyoo.WebLogin/</LoginRoot>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
@ -23,11 +17,7 @@
<PackageReference Include="Microsoft.AspNetCore.Authentication" Version="2.2.0" /> <PackageReference Include="Microsoft.AspNetCore.Authentication" Version="2.2.0" />
<PackageReference Include="Portable.BouncyCastle" Version="1.8.10" /> <PackageReference Include="Portable.BouncyCastle" Version="1.8.10" />
<ProjectReference Include="../Kyoo.Common/Kyoo.Common.csproj"> <ProjectReference Include="../Kyoo.Common/Kyoo.Common.csproj" />
<PrivateAssets>all</PrivateAssets>
<Private>false</Private>
<ExcludeAssets>runtime</ExcludeAssets>
</ProjectReference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -1,8 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Autofac;
using Kyoo.Models.Exceptions; using Kyoo.Models.Exceptions;
using Microsoft.Extensions.DependencyInjection;
namespace Kyoo.Controllers 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. /// You should not try to put plugins from the plugins directory here as they will get automatically loaded.
/// </param> /// </param>
public void LoadPlugins(params Type[] plugins); public void LoadPlugins(params Type[] plugins);
/// <summary>
/// Configure container adding or removing services as the plugins wants.
/// </summary>
/// <param name="builder">The container to populate</param>
void ConfigureContainer(ContainerBuilder builder);
/// <summary>
/// Configure services via the microsoft way. This allow libraries to add their services.
/// </summary>
/// <param name="services">The service collection to populate</param>
public void ConfigureServices(IServiceCollection services);
} }
} }

View File

@ -309,20 +309,20 @@ namespace Kyoo.Controllers
Task<Season> Get(string showSlug, int seasonNumber); Task<Season> Get(string showSlug, int seasonNumber);
/// <summary> /// <summary>
/// Get a season from it's showID and it's seasonNumber or null if it is not found. /// Get a season from it's showID and it's seasonNumber or null if it is not found.
/// </summary> /// </summary>
/// <param name="showID">The id of the show</param> /// <param name="showID">The id of the show</param>
/// <param name="seasonNumber">The season's number</param> /// <param name="seasonNumber">The season's number</param>
/// <returns>The season found</returns> /// <returns>The season found</returns>
Task<Season> GetOrDefault(int showID, int seasonNumber); Task<Season> GetOrDefault(int showID, int seasonNumber);
/// <summary> /// <summary>
/// Get a season from it's show slug and it's seasonNumber or null if it is not found. /// Get a season from it's show slug and it's seasonNumber or null if it is not found.
/// </summary> /// </summary>
/// <param name="showSlug">The slug of the show</param> /// <param name="showSlug">The slug of the show</param>
/// <param name="seasonNumber">The season's number</param> /// <param name="seasonNumber">The season's number</param>
/// <returns>The season found</returns> /// <returns>The season found</returns>
Task<Season> GetOrDefault(string showSlug, int seasonNumber); Task<Season> GetOrDefault(string showSlug, int seasonNumber);
} }
/// <summary> /// <summary>

View File

@ -60,9 +60,9 @@ namespace Kyoo.Models
} }
/// <summary> /// <summary>
/// The slug of the episode that contain this track. If this is not set, this track is ill-formed. /// The slug of the episode that contain this track. If this is not set, this track is ill-formed.
/// </summary> /// </summary>
[SerializeIgnore] public string EpisodeSlug { private get; set; } [SerializeIgnore] public string EpisodeSlug { private get; set; }
/// <summary> /// <summary>
/// The title of the stream. /// The title of the stream.

View File

@ -21,17 +21,17 @@ namespace Kyoo
/// <exception cref="TaskCanceledException"></exception> /// <exception cref="TaskCanceledException"></exception>
/// <exception cref="TaskCanceledException">The source task has been canceled.</exception> /// <exception cref="TaskCanceledException">The source task has been canceled.</exception>
public static Task<T> Then<T>(this Task<T> task, Action<T> then) public static Task<T> Then<T>(this Task<T> task, Action<T> then)
{ {
return task.ContinueWith(x => return task.ContinueWith(x =>
{ {
if (x.IsFaulted) if (x.IsFaulted)
x.Exception!.InnerException!.ReThrow(); x.Exception!.InnerException!.ReThrow();
if (x.IsCanceled) if (x.IsCanceled)
throw new TaskCanceledException(); throw new TaskCanceledException();
then(x.Result); then(x.Result);
return x.Result; return x.Result;
}, TaskContinuationOptions.ExecuteSynchronously); }, TaskContinuationOptions.ExecuteSynchronously);
} }
/// <summary> /// <summary>
/// Map the result of a task to another result. /// Map the result of a task to another result.
@ -42,28 +42,28 @@ namespace Kyoo
/// <typeparam name="TResult">The resulting task after the mapping method</typeparam> /// <typeparam name="TResult">The resulting task after the mapping method</typeparam>
/// <returns>A task wrapping the initial task and mapping the initial result.</returns> /// <returns>A task wrapping the initial task and mapping the initial result.</returns>
/// <exception cref="TaskCanceledException">The source task has been canceled.</exception> /// <exception cref="TaskCanceledException">The source task has been canceled.</exception>
public static Task<TResult> Map<T, TResult>(this Task<T> task, Func<T, TResult> map) public static Task<TResult> Map<T, TResult>(this Task<T> task, Func<T, TResult> map)
{ {
return task.ContinueWith(x => return task.ContinueWith(x =>
{ {
if (x.IsFaulted) if (x.IsFaulted)
x.Exception!.InnerException!.ReThrow(); x.Exception!.InnerException!.ReThrow();
if (x.IsCanceled) if (x.IsCanceled)
throw new TaskCanceledException(); throw new TaskCanceledException();
return map(x.Result); return map(x.Result);
}, TaskContinuationOptions.ExecuteSynchronously); }, TaskContinuationOptions.ExecuteSynchronously);
} }
/// <summary> /// <summary>
/// A method to return the a default value from a task if the initial task is null. /// A method to return the a default value from a task if the initial task is null.
/// </summary> /// </summary>
/// <param name="value">The initial task</param> /// <param name="value">The initial task</param>
/// <typeparam name="T">The type that the task will return</typeparam> /// <typeparam name="T">The type that the task will return</typeparam>
/// <returns>A non-null task.</returns> /// <returns>A non-null task.</returns>
[NotNull] [NotNull]
public static Task<T> DefaultIfNull<T>([CanBeNull] Task<T> value) public static Task<T> DefaultIfNull<T>([CanBeNull] Task<T> value)
{ {
return value ?? Task.FromResult<T>(default); return value ?? Task.FromResult<T>(default);
} }
} }
} }

View File

@ -19,7 +19,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Kyoo.Common\Kyoo.Common.csproj" /> <ProjectReference Include="../Kyoo.Common/Kyoo.Common.csproj" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -18,9 +18,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="../Kyoo.CommonAPI/Kyoo.CommonAPI.csproj"> <ProjectReference Include="../Kyoo.CommonAPI/Kyoo.CommonAPI.csproj" />
</ProjectReference> <ProjectReference Include="../Kyoo.Common/Kyoo.Common.csproj" />
<ProjectReference Include="../Kyoo.Common/Kyoo.Common.csproj">
</ProjectReference>
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -18,9 +18,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="../Kyoo.CommonAPI/Kyoo.CommonAPI.csproj"> <ProjectReference Include="../Kyoo.CommonAPI/Kyoo.CommonAPI.csproj" />
</ProjectReference> <ProjectReference Include="../Kyoo.Common/Kyoo.Common.csproj" />
<ProjectReference Include="../Kyoo.Common/Kyoo.Common.csproj">
</ProjectReference>
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -17,7 +17,6 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="../Kyoo.Common/Kyoo.Common.csproj"> <ProjectReference Include="../Kyoo.Common/Kyoo.Common.csproj" />
</ProjectReference>
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -16,7 +16,6 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="../Kyoo.Common/Kyoo.Common.csproj"> <ProjectReference Include="../Kyoo.Common/Kyoo.Common.csproj" />
</ProjectReference>
</ItemGroup> </ItemGroup>
</Project> </Project>

@ -1 +1 @@
Subproject commit dca10903ff54a8999732695b5c2a0a5c94f85200 Subproject commit dc37ce398d6c9cacc7703e21552a116c42b548ed

View File

@ -17,6 +17,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kyoo.TheMovieDb", "Kyoo.The
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kyoo.Tests", "tests\Kyoo.Tests\Kyoo.Tests.csproj", "{0C8AA7EA-E723-4532-852F-35AA4E8AFED5}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kyoo.Tests", "tests\Kyoo.Tests\Kyoo.Tests.csproj", "{0C8AA7EA-E723-4532-852F-35AA4E8AFED5}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kyoo.WebApp", "Kyoo.WebApp\Kyoo.WebApp.csproj", "{2374D500-1ADB-4752-85DB-8BB0DDF5A8E8}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU 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}.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.ActiveCfg = Release|Any CPU
{0C8AA7EA-E723-4532-852F-35AA4E8AFED5}.Release|Any CPU.Build.0 = 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 EndGlobalSection
EndGlobal EndGlobal

View File

@ -20,7 +20,7 @@ namespace Kyoo.Controllers
/// <summary> /// <summary>
/// An extension provider to get content types from files extensions. /// An extension provider to get content types from files extensions.
/// </summary> /// </summary>
private FileExtensionContentTypeProvider _provider; private readonly IContentTypeProvider _provider;
/// <summary> /// <summary>
/// Options to check if the metadata should be kept in the show directory or in a kyoo's directory. /// 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 <see cref="LocalFileSystem"/> with the specified options. /// Create a new <see cref="LocalFileSystem"/> with the specified options.
/// </summary> /// </summary>
/// <param name="options">The options to use.</param> /// <param name="options">The options to use.</param>
public LocalFileSystem(IOptionsMonitor<BasicOptions> options) /// <param name="provider">An extension provider to get content types from files extensions.</param>
public LocalFileSystem(IOptionsMonitor<BasicOptions> options, IContentTypeProvider provider)
{ {
_options = options; _options = options;
_provider = provider;
} }
/// <summary> /// <summary>
@ -44,15 +46,6 @@ namespace Kyoo.Controllers
/// <returns>The content type of the file</returns> /// <returns>The content type of the file</returns>
private string _GetContentType(string path) 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)) if (_provider.TryGetContentType(path, out string contentType))
return contentType; return contentType;
throw new NotImplementedException($"Can't get the content type of the file at: {path}"); throw new NotImplementedException($"Can't get the content type of the file at: {path}");

View File

@ -4,7 +4,6 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Runtime.Loader; using System.Runtime.Loader;
using Autofac;
using Kyoo.Models.Options; using Kyoo.Models.Options;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
@ -133,20 +132,6 @@ namespace Kyoo.Controllers
); );
} }
/// <inheritdoc />
public void ConfigureContainer(ContainerBuilder builder)
{
foreach (IPlugin plugin in _plugins)
plugin.Configure(builder);
}
/// <inheritdoc />
public void ConfigureServices(IServiceCollection services)
{
foreach (IPlugin plugin in _plugins)
plugin.Configure(services);
}
/// <summary> /// <summary>
/// A custom <see cref="AssemblyLoadContext"/> to load plugin's dependency if they are on the same folder. /// A custom <see cref="AssemblyLoadContext"/> to load plugin's dependency if they are on the same folder.
/// </summary> /// </summary>

View File

@ -1,9 +1,9 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using Autofac; using Autofac;
using Autofac.Core; using Autofac.Core;
using Autofac.Core.Registration; using Autofac.Core.Registration;
using Autofac.Extras.AttributeMetadata;
using Kyoo.Controllers; using Kyoo.Controllers;
using Kyoo.Models.Options; using Kyoo.Models.Options;
using Kyoo.Models.Permissions; using Kyoo.Models.Permissions;
@ -12,8 +12,8 @@ using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.StaticFiles; using Microsoft.AspNetCore.StaticFiles;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
using IMetadataProvider = Kyoo.Controllers.IMetadataProvider;
namespace Kyoo namespace Kyoo
{ {
@ -60,6 +60,8 @@ namespace Kyoo
/// <inheritdoc /> /// <inheritdoc />
public void Configure(ContainerBuilder builder) public void Configure(ContainerBuilder builder)
{ {
builder.RegisterModule<AttributedMetadataModule>();
builder.RegisterComposite<FileSystemComposite, IFileSystem>().InstancePerLifetimeScope(); builder.RegisterComposite<FileSystemComposite, IFileSystem>().InstancePerLifetimeScope();
builder.RegisterType<LocalFileSystem>().As<IFileSystem>().SingleInstance(); builder.RegisterType<LocalFileSystem>().As<IFileSystem>().SingleInstance();
builder.RegisterType<HttpFileSystem>().As<IFileSystem>().SingleInstance(); builder.RegisterType<HttpFileSystem>().As<IFileSystem>().SingleInstance();
@ -98,13 +100,24 @@ namespace Kyoo
builder.RegisterType<PassthroughPermissionValidator>().As<IPermissionValidator>() builder.RegisterType<PassthroughPermissionValidator>().As<IPermissionValidator>()
.IfNotRegistered(typeof(IPermissionValidator)); .IfNotRegistered(typeof(IPermissionValidator));
builder.RegisterType<FileExtensionContentTypeProvider>().As<IContentTypeProvider>().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";
});
} }
/// <inheritdoc /> /// <inheritdoc />
public void Configure(IServiceCollection services) public void Configure(IServiceCollection services)
{ {
string publicUrl = _configuration.GetPublicUrl(); string publicUrl = _configuration.GetPublicUrl();
services.AddMvc().AddControllersAsServices();
services.AddControllers() services.AddControllers()
.AddNewtonsoftJson(x => .AddNewtonsoftJson(x =>
{ {
@ -112,6 +125,13 @@ namespace Kyoo
x.SerializerSettings.Converters.Add(new PeopleRoleConverter()); x.SerializerSettings.Converters.Add(new PeopleRoleConverter());
}); });
services.AddResponseCompression(x =>
{
x.EnableForHttps = true;
});
services.AddHttpClient();
services.AddHostedService(x => x.GetService<ITaskManager>() as TaskManager); services.AddHostedService(x => x.GetService<ITaskManager>() as TaskManager);
} }
@ -128,17 +148,8 @@ namespace Kyoo
app.UseHsts(); app.UseHsts();
} }
}, SA.Before), }, SA.Before),
SA.New<IApplicationBuilder>(app => app.UseResponseCompression(), SA.Routing + 1),
SA.New<IApplicationBuilder>(app => app.UseRouting(), SA.Routing), SA.New<IApplicationBuilder>(app => app.UseRouting(), SA.Routing),
SA.New<IApplicationBuilder>(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<IApplicationBuilder>(app => app.UseEndpoints(x => x.MapControllers()), SA.Endpoint) SA.New<IApplicationBuilder>(app => app.UseEndpoints(x => x.MapControllers()), SA.Endpoint)
}; };
} }

View File

@ -2,15 +2,7 @@
<PropertyGroup> <PropertyGroup>
<TargetFramework>net5.0</TargetFramework> <TargetFramework>net5.0</TargetFramework>
<TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>
<TypeScriptToolsVersion>Latest</TypeScriptToolsVersion>
<IsPackable>false</IsPackable>
<SpaRoot>../Kyoo.WebApp/</SpaRoot>
<TranscoderRoot>../Kyoo.Transcoder/</TranscoderRoot> <TranscoderRoot>../Kyoo.Transcoder/</TranscoderRoot>
<DefaultItemExcludes>$(DefaultItemExcludes);$(SpaRoot)node_modules/**</DefaultItemExcludes>
<!-- Set this to true if you enable server-side prerendering -->
<BuildServerSideRenderer>false</BuildServerSideRenderer>
<Company>SDG</Company> <Company>SDG</Company>
<Authors>Zoe Roux</Authors> <Authors>Zoe Roux</Authors>
@ -33,79 +25,30 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="System.Collections.Immutable" Version="5.0.0" /> <PackageReference Include="System.Collections.Immutable" Version="5.0.0" />
<ProjectReference Include="../Kyoo.Common/Kyoo.Common.csproj" />
<ProjectReference Include="../Kyoo.CommonAPI/Kyoo.CommonAPI.csproj" />
<PackageReference Include="Autofac" Version="6.2.0" /> <PackageReference Include="Autofac" Version="6.2.0" />
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="7.1.0" /> <PackageReference Include="Autofac.Extensions.DependencyInjection" Version="7.1.0" />
<PackageReference Include="Autofac.Extras.AttributeMetadata" Version="6.0.0" /> <PackageReference Include="Autofac.Extras.AttributeMetadata" Version="6.0.0" />
<PackageReference Include="Microsoft.AspNet.WebApi.Client" Version="5.2.7" /> <PackageReference Include="Microsoft.AspNet.WebApi.Client" Version="5.2.7" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="5.0.8" /> <PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="5.0.8" />
<PackageReference Include="Microsoft.AspNetCore.SpaServices" Version="5.0.0-preview.8.20414.8" />
<PackageReference Include="Microsoft.AspNetCore.SpaServices.Extensions" Version="5.0.8" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<ProjectReference Include="..\Kyoo.TheMovieDb\Kyoo.TheMovieDb.csproj" />
<ProjectReference Include="..\Kyoo.TheTvdb\Kyoo.TheTvdb.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="../Kyoo.Postgresql/Kyoo.Postgresql.csproj"> <ProjectReference Include="../Kyoo.Common/Kyoo.Common.csproj" />
<!-- <ExcludeAssets>all</ExcludeAssets>--> <ProjectReference Include="../Kyoo.CommonAPI/Kyoo.CommonAPI.csproj" />
</ProjectReference> <ProjectReference Include="../Kyoo.TheMovieDb/Kyoo.TheMovieDb.csproj" />
<ProjectReference Include="../Kyoo.Authentication/Kyoo.Authentication.csproj"> <ProjectReference Include="../Kyoo.TheTvdb/Kyoo.TheTvdb.csproj" />
<!-- <ExcludeAssets>all</ExcludeAssets>--> <ProjectReference Include="../Kyoo.Postgresql/Kyoo.Postgresql.csproj" />
</ProjectReference> <ProjectReference Include="../Kyoo.SqLite/Kyoo.SqLite.csproj" />
<ProjectReference Include="../Kyoo.SqLite/Kyoo.SqLite.csproj"> <ProjectReference Include="../Kyoo.Authentication/Kyoo.Authentication.csproj" />
<!-- <ExcludeAssets>all</ExcludeAssets>--> <ProjectReference Include="../Kyoo.WebApp/Kyoo.WebApp.csproj" Condition="'$(SkipWebApp)' != 'true'"/>
</ProjectReference>
</ItemGroup> </ItemGroup>
<ItemGroup>
<None Include="$(SpaRoot)**" Exclude="$(SpaRoot)node_modules/**" Visible="false" />
<StaticFiles Include="$(SpaRoot)static/**" Visible="false" />
<Content Remove="$(SpaRoot)**" />
</ItemGroup>
<Target Name="PublishRunWebpack" AfterTargets="ComputeFilesToPublish" Condition="'$(SkipWebApp)' != 'true'">
<Exec WorkingDirectory="$(SpaRoot)" Command="npm install" />
<Exec WorkingDirectory="$(SpaRoot)" Command="npm run build -- --prod" />
<Exec WorkingDirectory="$(SpaRoot)" Command="npm run build:ssr -- --prod" Condition="'$(BuildServerSideRenderer)' == 'true'" />
<ItemGroup>
<DistFiles Include="$(SpaRoot)dist/**; $(SpaRoot)dist-server/**" />
<DistFiles Include="$(SpaRoot)node_modules/**" Condition="'$(BuildServerSideRenderer)' == 'true'" />
<ResolvedFileToPublish Include="@(DistFiles->'%(FullPath)')" Exclude="@(ResolvedFileToPublish)">
<RelativePath>wwwroot/%(DistFiles.Filename)%(DistFiles.Extension)</RelativePath>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
</ResolvedFileToPublish>
</ItemGroup>
</Target>
<Target Name="Publish static files" AfterTargets="ComputeFilesToPublish">
<ItemGroup>
<ResolvedFileToPublish Include="@(StaticFiles->'%(FullPath)')" Exclude="@(ResolvedFileToPublish)">
<RelativePath>wwwroot/%(StaticFiles.RecursiveDir)%(StaticFiles.Filename)%(StaticFiles.Extension)</RelativePath>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
</ResolvedFileToPublish>
</ItemGroup>
</Target>
<Target Name="Prepare the web app" AfterTargets="Build" Condition="$(Configuration) == 'Debug' and '$(SkipWebApp)' != 'true'">
<Exec WorkingDirectory="$(SpaRoot)" Command="npm install" />
</Target>
<Target Name="Prepare static files" AfterTargets="Build" Condition="$(Configuration) == 'Debug'">
<Copy SourceFiles="@(StaticFiles)" DestinationFolder="$(OutputPath)/wwwroot/%(RecursiveDir)" />
</Target>
<Target Name="Symlink views to output - Linux" AfterTargets="Build" Condition="$(Configuration) == 'Debug' And $(OS) == 'Unix'">
<Exec WorkingDirectory="$(OutputPath)" Command="ln -fs $(ProjectDir)/$(SpaRoot)" />
</Target>
<Target Name="BuildTranscoder" BeforeTargets="BeforeBuild" Condition="'$(SkipTranscoder)' != 'true'"> <Target Name="BuildTranscoder" BeforeTargets="BeforeBuild" Condition="'$(SkipTranscoder)' != 'true'">
<Exec WorkingDirectory="$(TranscoderRoot)" Condition="'$(IsWindows)' != 'true'" Command="mkdir -p build %26%26 cd build %26%26 cmake .. %26%26 make -j" /> <Exec WorkingDirectory="$(TranscoderRoot)" Condition="'$(IsWindows)' != 'true'"
<Exec WorkingDirectory="$(TranscoderRoot)" Condition="'$(IsWindows)' == 'true'" Command="(if not exist build mkdir build) %26%26 cd build %26%26 cmake .. -G &quot;NMake Makefiles&quot; %26%26 nmake" /> Command="mkdir -p build %26%26 cd build %26%26 cmake .. %26%26 make -j" />
<Exec WorkingDirectory="$(TranscoderRoot)" Condition="'$(IsWindows)' == 'true'"
Command="(if not exist build mkdir build) %26%26 cd build %26%26 cmake .. -G &quot;NMake Makefiles&quot; %26%26 nmake" />
<Copy SourceFiles="$(TranscoderRoot)/build/$(TranscoderBinary)" DestinationFolder="." /> <Copy SourceFiles="$(TranscoderRoot)/build/$(TranscoderBinary)" DestinationFolder="." />
</Target> </Target>
@ -115,25 +58,4 @@
<Visible>false</Visible> <Visible>false</Visible>
</None> </None>
</ItemGroup> </ItemGroup>
<!--TODO remove this once plugins are reworked. This is useful because the authentication plugin is loaded manually and not by the plugin manager-->
<PropertyGroup>
<LoginRoot>../Kyoo.WebLogin/</LoginRoot>
</PropertyGroup>
<ItemGroup>
<LoginFiles Include="$(LoginRoot)**" Visible="false" />
</ItemGroup>
<Target Name="Publish login files" AfterTargets="ComputeFilesToPublish">
<ItemGroup>
<ResolvedFileToPublish Include="@(LoginFiles->'%(FullPath)')" Exclude="@(ResolvedFileToPublish)">
<RelativePath>login/%(LoginFiles.RecursiveDir)%(LoginFiles.Filename)%(LoginFiles.Extension)</RelativePath>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
</ResolvedFileToPublish>
</ItemGroup>
</Target>
<Target Name="Prepare static files" AfterTargets="Build" Condition="$(Configuration) == 'Debug'">
<Copy SourceFiles="@(LoginFiles)" DestinationFolder="$(OutputPath)/login/%(RecursiveDir)" />
</Target>
</Project> </Project>

View File

@ -1,9 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Linq; using System.Linq;
using Autofac; using Autofac;
using Autofac.Extras.AttributeMetadata;
using Kyoo.Authentication; using Kyoo.Authentication;
using Kyoo.Controllers; using Kyoo.Controllers;
using Kyoo.Models.Options; using Kyoo.Models.Options;
@ -14,7 +12,6 @@ using Kyoo.TheMovieDb;
using Kyoo.TheTvdb; using Kyoo.TheTvdb;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.SpaServices.AngularCli;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
@ -65,20 +62,9 @@ namespace Kyoo
/// <param name="services">The service collection to fill.</param> /// <param name="services">The service collection to fill.</param>
public void ConfigureServices(IServiceCollection services) 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<KeyValuePair<string, Type>> configTypes = _plugins.GetAllPlugins() IEnumerable<KeyValuePair<string, Type>> configTypes = _plugins.GetAllPlugins()
.SelectMany(x => x.Configuration) .SelectMany(x => x.Configuration)
.Where(x => x.Value != null); .Where(x => x.Value != null);
@ -99,41 +85,26 @@ namespace Kyoo
/// <param name="builder">The builder to configure.</param> /// <param name="builder">The builder to configure.</param>
public void ConfigureContainer(ContainerBuilder builder) public void ConfigureContainer(ContainerBuilder builder)
{ {
builder.RegisterModule<AttributedMetadataModule>();
builder.RegisterInstance(_plugins).As<IPluginManager>().ExternallyOwned(); builder.RegisterInstance(_plugins).As<IPluginManager>().ExternallyOwned();
builder.RegisterTask<PluginInitializer>(); builder.RegisterTask<PluginInitializer>();
_plugins.ConfigureContainer(builder);
foreach (IPlugin plugin in _plugins.GetAllPlugins())
plugin.Configure(builder);
} }
/// <summary> /// <summary>
/// Configure the asp net host. /// Configure the asp net host.
/// </summary> /// </summary>
/// <param name="app">The asp net host to configure</param> /// <param name="app">The asp net host to configure</param>
/// <param name="env">The host environment (is the app in development mode?)</param> /// <param name="container">An autofac container used to create a new scope to configure asp-net.</param>
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IConfigurationManager config, ILifetimeScope container) /// <param name="config">The configuration manager used to register strongly typed config.</param>
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<IStartupAction> steps = _plugins.GetAllPlugins() IEnumerable<IStartupAction> steps = _plugins.GetAllPlugins()
.SelectMany(x => x.ConfigureSteps) .SelectMany(x => x.ConfigureSteps)
.OrderByDescending(x => x.Priority); .OrderByDescending(x => x.Priority);
using ILifetimeScope scope = container.BeginLifetimeScope(x => using ILifetimeScope scope = container.BeginLifetimeScope(x =>
x.RegisterInstance(app).SingleInstance().ExternallyOwned()); x.RegisterInstance(app).SingleInstance().ExternallyOwned());
IServiceProvider provider = scope.Resolve<IServiceProvider>(); IServiceProvider provider = scope.Resolve<IServiceProvider>();
foreach (IStartupAction step in steps) foreach (IStartupAction step in steps)
@ -148,14 +119,6 @@ namespace Kyoo
); );
foreach ((string path, Type type) in pluginConfig) foreach ((string path, Type type) in pluginConfig)
config.Register(path, type); config.Register(path, type);
app.UseSpa(spa =>
{
spa.Options.SourcePath = Path.Join(AppDomain.CurrentDomain.BaseDirectory, "Kyoo.WebApp");
if (env.IsDevelopment())
spa.UseAngularCliServer("start");
});
} }

View File

@ -64,7 +64,7 @@ namespace Kyoo.Tests
[Fact] [Fact]
public async Task GetByFakeSlugTest() public async Task GetByFakeSlugTest()
{ {
await Assert.ThrowsAsync<ItemNotFoundException>(() => _repository.Get("non-existent")); await Assert.ThrowsAsync<ItemNotFoundException>(() => _repository.Get("non-existent"));
} }
[Fact] [Fact]