Moving the webapp submodule and adding a csproj for it on the main repository

This commit is contained in:
Zoe Roux 2021-08-14 18:34:49 +02:00
parent 2891065e50
commit 332b8f0634
11 changed files with 231 additions and 50 deletions

2
.gitmodules vendored
View File

@ -3,6 +3,6 @@
url = ../Kyoo.Transcoder.git
branch = master
[submodule "WebApp"]
path = Kyoo.WebApp
path = Kyoo.WebApp/Front
url = ../Kyoo.WebApp.git
branch = master

View File

@ -53,9 +53,9 @@ namespace Kyoo.Authentication
private readonly IConfiguration _configuration;
/// <summary>
/// A logger factory to allow IdentityServer to log things.
/// The logger used to allow IdentityServer to log things.
/// </summary>
private readonly ILoggerFactory _loggerFactory;
private readonly ILogger<DefaultCorsPolicyService> _logger;
/// <summary>
/// The environment information to check if the app runs in debug mode
@ -67,14 +67,14 @@ namespace Kyoo.Authentication
/// Create a new authentication module instance and use the given configuration and environment.
/// </summary>
/// <param name="configuration">The configuration to use</param>
/// <param name="loggerFactory">The logger factory to allow IdentityServer to log things</param>
/// <param name="logger">The logger used to allow IdentityServer to log things</param>
/// <param name="environment">The environment information to check if the app runs in debug mode</param>
public AuthenticationModule(IConfiguration configuration,
ILoggerFactory loggerFactory,
ILogger<DefaultCorsPolicyService> logger,
IWebHostEnvironment environment)
{
_configuration = configuration;
_loggerFactory = loggerFactory;
_logger = logger;
_environment = environment;
}
@ -83,7 +83,7 @@ namespace Kyoo.Authentication
{
builder.RegisterType<PermissionValidatorFactory>().As<IPermissionValidator>().SingleInstance();
DefaultCorsPolicyService cors = new(_loggerFactory.CreateLogger<DefaultCorsPolicyService>())
DefaultCorsPolicyService cors = new(_logger)
{
AllowedOrigins = { new Uri(_configuration.GetPublicUrl()).GetLeftPart(UriPartial.Authority) }
};

View File

@ -93,5 +93,14 @@ namespace Kyoo.Controllers
{
// Skipped
}
/// <summary>
/// An optional callback function called when the startups ends and this plugin has been flagged has disabled.
/// It allow a plugin to log an error or warning message to inform why it has been disabled.
/// </summary>
void Disabled()
{
// Skipped
}
}
}

View File

@ -155,7 +155,8 @@ namespace Kyoo
IEnumerable<Type> types = genericType.IsInterface
? type.GetInterfaces()
: type.GetInheritanceTree();
return types.Any(x => x.IsGenericType && x.GetGenericTypeDefinition() == genericType);
return types.Prepend(type)
.Any(x => x.IsGenericType && x.GetGenericTypeDefinition() == genericType);
}
/// <summary>
@ -179,7 +180,8 @@ namespace Kyoo
IEnumerable<Type> types = genericType.IsInterface
? type.GetInterfaces()
: type.GetInheritanceTree();
return types.FirstOrDefault(x => x.IsGenericType && x.GetGenericTypeDefinition() == genericType);
return types.Prepend(type)
.FirstOrDefault(x => x.IsGenericType && x.GetGenericTypeDefinition() == genericType);
}
/// <summary>

@ -1 +0,0 @@
Subproject commit 49cf0c3d17f889f40fa9adbb383edfc0d2c99779

1
Kyoo.WebApp/Front Submodule

@ -0,0 +1 @@
Subproject commit dca10903ff54a8999732695b5c2a0a5c94f85200

View File

@ -0,0 +1,61 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>
<TypeScriptToolsVersion>Latest</TypeScriptToolsVersion>
<IsPackable>false</IsPackable>
<DefaultItemExcludes>$(DefaultItemExcludes);$(SpaRoot)node_modules/**</DefaultItemExcludes>
<SpaRoot>Front/</SpaRoot>
<!-- Set this to true if you enable server-side prerendering -->
<BuildServerSideRenderer>false</BuildServerSideRenderer>
<Company>SDG</Company>
<Authors>Zoe Roux</Authors>
<RepositoryUrl>https://github.com/AnonymusRaccoon/Kyoo</RepositoryUrl>
<LangVersion>default</LangVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.SpaServices" Version="5.0.0-preview.8.20414.8" />
<PackageReference Include="Microsoft.AspNetCore.SpaServices.Extensions" Version="5.0.8" />
<ProjectReference Include="../Kyoo.Common/Kyoo.Common.csproj" />
</ItemGroup>
<ItemGroup>
<Content Remove="$(SpaRoot)**" />
<Compile Remove="$(SpaRoot)**" />
<Content Include="$(SpaRoot)static/**" Visible="false">
<Link>wwwroot/%(RecursiveDir)%(Filename)%(Extension)</Link>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<None Remove="$(SpaRoot)dist/**; $(SpaRoot)dist-server/**" />
<Content Include="$(SpaRoot)dist/**; $(SpaRoot)dist-server/**" Visible="false" Condition="'$(Configuration)' != 'Debug'">
<Link>wwwroot/%(RecursiveDir)%(Filename)%(Extension)</Link>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
<Target Name="DebugEnsureNodeEnv" BeforeTargets="Build" Condition="'$(Configuration)' == 'Debug' And !Exists('$(SpaRoot)node_modules')">
<Exec Command="node --version" ContinueOnError="true">
<Output TaskParameter="ExitCode" PropertyName="ErrorCode" />
</Exec>
<Error Condition="'$(ErrorCode)' != '0'" Text="Node.js is required to build and run this project. To continue, please install Node.js from https://nodejs.org/, and then restart your command prompt or IDE." />
<Message Importance="high" Text="Restoring dependencies using 'npm'. This may take several minutes..." />
<Exec WorkingDirectory="$(SpaRoot)" Command="npm install" />
</Target>
<Target Name="PublishRunWebpack" BeforeTargets="Build" Condition="'$(SkipWebApp)' != 'true' And '$(Configuration)' != 'Debug'">
<Message Importance="high" Text="Restoring dependencies using 'npm'. This may take several minutes..." />
<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'" />
</Target>
<Target Name="SymlinkViews" AfterTargets="Build" Condition="$(Configuration) == 'Debug'">
<Exec WorkingDirectory="$(ProjectDir)../Kyoo" Command="ln -fs '$(ProjectDir)' $(OutDir)" Condition="$(OS) == 'Unix'" />
<Exec WorkingDirectory="$(ProjectDir)../Kyoo" Command="mklink /D '$(OutDir)$(ProjectName)' '$(ProjectDir)'" Condition="$(OS) != 'Unix'" />
</Target>
</Project>

110
Kyoo.WebApp/WebAppModule.cs Normal file
View File

@ -0,0 +1,110 @@
using System;
using System.Collections.Generic;
using System.IO;
using Kyoo.Controllers;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.SpaServices.AngularCli;
using Microsoft.AspNetCore.StaticFiles;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
namespace Kyoo.WebApp
{
/// <summary>
/// A module to enable the web-app (the front-end of kyoo).
/// </summary>
public class WebAppModule : IPlugin
{
/// <inheritdoc />
public string Slug => "webapp";
/// <inheritdoc />
public string Name => "WebApp";
/// <inheritdoc />
public string Description => "A module to enable the web-app (the front-end of kyoo).";
/// <inheritdoc />
public Dictionary<string, Type> Configuration => new();
/// <inheritdoc />
public bool Enabled => Directory.Exists(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "wwwroot"));
/// <summary>
/// A logger only used to inform the user if the webapp could not be enabled.
/// </summary>
private readonly ILogger<WebAppModule> _logger;
/// <summary>
/// Create a new <see cref="WebAppModule"/>.
/// </summary>
/// <param name="logger">A logger only used to inform the user if the webapp could not be enabled.</param>
public WebAppModule(ILogger<WebAppModule> logger)
{
_logger = logger;
}
/// <inheritdoc />
public void Disabled()
{
_logger.LogError("The web app files could not be found, it will be disabled. " +
"If you cloned the project, you probably forgot to use the --recurse flag");
}
/// <inheritdoc />
public void Configure(IServiceCollection services)
{
services.AddSpaStaticFiles(x =>
{
x.RootPath = Path.Join(AppDomain.CurrentDomain.BaseDirectory, "wwwroot");
});
}
/// <inheritdoc />
public IEnumerable<IStartupAction> ConfigureSteps => new IStartupAction[]
{
SA.New<IApplicationBuilder, IWebHostEnvironment>((app, env) =>
{
if (!env.IsDevelopment())
app.UseSpaStaticFiles();
}, SA.StaticFiles),
SA.New<IApplicationBuilder, IContentTypeProvider>((app, contentTypeProvider) =>
{
app.UseStaticFiles(new StaticFileOptions
{
ContentTypeProvider = contentTypeProvider,
FileProvider = new PhysicalFileProvider(Path.Join(AppDomain.CurrentDomain.BaseDirectory, "wwwroot"))
});
}, SA.StaticFiles),
SA.New<IApplicationBuilder>(app =>
{
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'");
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();
});
}, SA.Endpoint - 499),
SA.New<IApplicationBuilder, IWebHostEnvironment>((app, env) =>
{
app.UseSpa(spa =>
{
spa.Options.SourcePath = Path.Join(AppDomain.CurrentDomain.BaseDirectory, "Kyoo.WebApp", "Front");
if (env.IsDevelopment())
spa.UseAngularCliServer("start");
});
}, SA.Endpoint - 500)
};
}
}

View File

@ -110,12 +110,15 @@ namespace Kyoo.Controllers
_logger.LogTrace("Loading new plugins...");
string[] pluginsPaths = Directory.GetFiles(pluginFolder, "*.dll", SearchOption.AllDirectories);
_plugins.AddRange(plugins
IPlugin[] newPlugins = plugins
.Concat(pluginsPaths.SelectMany(LoadPlugin))
.GroupBy(x => x.Name)
.Select(x => x.First())
.Where(x => x.Enabled)
);
.ToArray();
_plugins.AddRange(newPlugins.Where(x => x.Enabled));
foreach (IPlugin plugin in newPlugins.Where(x => !x.Enabled))
plugin.Disabled();
if (!_plugins.Any())
_logger.LogInformation("No plugin enabled");

View File

@ -49,7 +49,7 @@ namespace Kyoo
_configuration = configuration;
// TODO enable the web app only if it was build with it.
_plugins.LoadPlugins(
typeof(CoreModule),
typeof(CoreModule),
typeof(WebAppModule),
typeof(AuthenticationModule),
typeof(PostgresModule),
@ -198,8 +198,16 @@ namespace Kyoo
return _hostEnvironment;
if (serviceType == typeof(IConfiguration))
return _configuration;
if (serviceType == typeof(ILoggerFactory))
return _loggerFactory;
if (serviceType.GetGenericTypeDefinition() == typeof(ILogger<>))
{
return Utility.RunGenericMethod<object>(
typeof(LoggerFactoryExtensions),
nameof(LoggerFactoryExtensions.CreateLogger),
serviceType.GetGenericArguments().First(),
_loggerFactory
);
}
return null;
}
}

View File

@ -1,5 +1,4 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Threading.Tasks;
using Autofac.Extensions.DependencyInjection;
@ -20,54 +19,38 @@ namespace Kyoo
/// The path of the json configuration of the application.
/// </summary>
public const string JsonConfigPath = "./settings.json";
/// <summary>
/// The string representation of the environment used in <see cref="IWebHostEnvironment"/>.
/// </summary>
#if DEBUG
private const string Environment = "Development";
#else
private const string Environment = "Production";
#endif
/// <summary>
/// Main function of the program
/// </summary>
/// <param name="args">Command line arguments</param>
[SuppressMessage("ReSharper", "ConditionIsAlwaysTrueOrFalse")]
public static async Task Main(string[] args)
{
if (!File.Exists(JsonConfigPath))
File.Copy(Path.Join(AppDomain.CurrentDomain.BaseDirectory, JsonConfigPath), JsonConfigPath);
IHostBuilder builder = CreateWebHostBuilder(args);
IHost host = CreateWebHostBuilder(args)
.UseEnvironment(Environment)
.Build();
// TODO remove ENVIRONEMENT handling and force it to the build env
bool? debug = Environment.GetEnvironmentVariable("ENVIRONMENT")?.ToLowerInvariant() switch
{
"d" => true,
"dev" => true,
"debug" => true,
"development" => true,
"p" => false,
"prod" => false,
"production" => false,
_ => null
};
if (debug == null && Environment.GetEnvironmentVariable("ENVIRONMENT") != null)
{
Console.WriteLine(
$"Invalid ENVIRONMENT variable. Supported values are \"debug\" and \"prod\". Ignoring...");
}
#if DEBUG
debug ??= true;
#endif
if (debug != null)
builder = builder.UseEnvironment(debug == true ? "Development" : "Production");
try
{
Console.WriteLine($"Running as {Environment.UserName}.");
await builder.Build().RunAsync();
host.Services.GetRequiredService<ILogger<Application>>()
.LogInformation("Running as {Name}", System.Environment.UserName);
await host.RunAsync();
}
catch (Exception ex)
{
await Console.Error.WriteLineAsync($"Unhandled exception: {ex}");
host.Services.GetRequiredService<ILogger<Application>>().LogCritical(ex, "Unhandled exception");
}
}
@ -79,7 +62,7 @@ namespace Kyoo
/// <returns>The modified configuration builder</returns>
private static IConfigurationBuilder SetupConfig(IConfigurationBuilder builder, string[] args)
{
return builder.SetBasePath(Environment.CurrentDirectory)
return builder.SetBasePath(System.Environment.CurrentDirectory)
.AddJsonFile(JsonConfigPath, false, true)
.AddEnvironmentVariables()
.AddCommandLine(args);
@ -129,5 +112,10 @@ namespace Kyoo
.UseStartup(host => PluginsStartup.FromWebHost(host, loggingConfiguration))
);
}
/// <summary>
/// An useless class only used to have a logger in the main.
/// </summary>
private class Application {}
}
}