Plugins: Reworking ConfigureAspnet to use steps with prorities. Adding a configuration dictionary.

This commit is contained in:
Zoe Roux 2021-08-13 16:36:27 +02:00
parent b67e0a629e
commit d1b3769f4f
15 changed files with 348 additions and 235 deletions

View File

@ -9,7 +9,6 @@ using IdentityServer4.Services;
using Kyoo.Authentication.Models;
using Kyoo.Authentication.Views;
using Kyoo.Controllers;
using Kyoo.Models.Attributes;
using Kyoo.Models.Permissions;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
@ -38,6 +37,12 @@ namespace Kyoo.Authentication
/// <inheritdoc />
public string Description => "Enable OpenID authentication for Kyoo.";
/// <inheritdoc />
public Dictionary<string, Type> Configuration => new()
{
{ AuthenticationOption.Path, typeof(AuthenticationOption) }
};
/// <summary>
/// The configuration to use.
@ -53,11 +58,6 @@ namespace Kyoo.Authentication
/// The environment information to check if the app runs in debug mode
/// </summary>
private readonly IWebHostEnvironment _environment;
/// <summary>
/// The configuration manager used to register typed/untyped implementations.
/// </summary>
[Injected] public IConfigurationManager ConfigurationManager { private get; set; }
/// <summary>
@ -88,10 +88,6 @@ namespace Kyoo.Authentication
// TODO handle direct-videos with bearers (probably add a cookie and a app.Use to translate that for videos)
// TODO Check if tokens should be stored.
services.Configure<PermissionOption>(_configuration.GetSection(PermissionOption.Path));
services.Configure<CertificateOption>(_configuration.GetSection(CertificateOption.Path));
services.Configure<AuthenticationOption>(_configuration.GetSection(AuthenticationOption.Path));
List<Client> clients = new();
_configuration.GetSection("authentication:clients").Bind(clients);
@ -129,37 +125,43 @@ namespace Kyoo.Authentication
}
/// <inheritdoc />
public void ConfigureAspNet(IApplicationBuilder app)
public IEnumerable<IStartupAction> ConfigureSteps => new IStartupAction[]
{
ConfigurationManager.AddTyped<AuthenticationOption>(AuthenticationOption.Path);
app.UseCookiePolicy(new CookiePolicyOptions
SA.New<IApplicationBuilder>(app =>
{
MinimumSameSitePolicy = SameSiteMode.Strict
});
app.UseAuthentication();
app.Use((ctx, next) =>
PhysicalFileProvider provider = new(Path.Combine(
Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!,
"login"));
app.UseDefaultFiles(new DefaultFilesOptions
{
RequestPath = new PathString("/login"),
FileProvider = provider,
RedirectToAppendTrailingSlash = true
});
app.UseStaticFiles(new StaticFileOptions
{
RequestPath = new PathString("/login"),
FileProvider = provider
});
}, SA.StaticFiles),
SA.New<IApplicationBuilder>(app =>
{
ctx.SetIdentityServerOrigin(_configuration.GetPublicUrl());
return next();
});
app.UseIdentityServer();
app.UseAuthorization();
PhysicalFileProvider provider = new(Path.Combine(
Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!,
"login"));
app.UseDefaultFiles(new DefaultFilesOptions
app.UseCookiePolicy(new CookiePolicyOptions
{
MinimumSameSitePolicy = SameSiteMode.Strict
});
app.UseAuthentication();
}, SA.Authentication),
SA.New<IApplicationBuilder>(app =>
{
RequestPath = new PathString("/login"),
FileProvider = provider,
RedirectToAppendTrailingSlash = true
});
app.UseStaticFiles(new StaticFileOptions
{
RequestPath = new PathString("/login"),
FileProvider = provider
});
}
app.Use((ctx, next) =>
{
ctx.SetIdentityServerOrigin(_configuration.GetPublicUrl());
return next();
});
app.UseIdentityServer();
}, SA.Endpoint),
SA.New<IApplicationBuilder>(app => app.UseAuthorization(), SA.Authorization)
};
}
}

View File

@ -1,7 +1,7 @@
using System;
using System.Collections.Generic;
using Autofac;
using JetBrains.Annotations;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
namespace Kyoo.Controllers
@ -41,8 +41,26 @@ namespace Kyoo.Controllers
/// By default, a plugin is always enabled. This method can be overriden to change this behavior.
/// </remarks>
virtual bool Enabled => true;
/// <summary>
/// A list of types that will be available via the IOptions interfaces and will be listed inside
/// an IConfiguration. If a field should be loosely typed, <see cref="Dictionary{TKey,TValue}"/> or <c>null</c>
/// can be specified.
/// </summary>
/// <remarks>
/// All use of the configuration must be specified here and not registered elsewhere, if a type is registered
/// elsewhere the configuration won't be editable via the <see cref="IConfigurationManager"/> and all values
/// will be discarded on edit.
/// </remarks>
Dictionary<string, Type> Configuration { get; }
/// <summary>
/// An optional configuration step to allow a plugin to change asp net configurations.
/// </summary>
/// <seealso cref="SA"/>
virtual IEnumerable<IStartupAction> ConfigureSteps => ArraySegment<IStartupAction>.Empty;
/// <summary>
/// A configure method that will be run on plugin's startup.
/// </summary>
/// <param name="builder">The autofac service container to register services.</param>
@ -61,20 +79,6 @@ namespace Kyoo.Controllers
{
// Skipped
}
/// <summary>
/// An optional configuration step to allow a plugin to change asp net configurations.
/// WARNING: This is only called on Kyoo's startup so you must restart the app to apply this changes.
/// </summary>
/// <param name="app">
/// The Asp.Net application builder. On most case it is not needed but you can use it to
/// add asp net functionalities.
/// </param>
void ConfigureAspNet(IApplicationBuilder app)
{
// Skipped
}
/// <summary>
/// An optional function to execute and initialize your plugin.

View File

@ -2,7 +2,6 @@ using System;
using System.Collections.Generic;
using Autofac;
using Kyoo.Models.Exceptions;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
namespace Kyoo.Controllers
@ -63,11 +62,5 @@ namespace Kyoo.Controllers
/// </summary>
/// <param name="services">The service collection to populate</param>
public void ConfigureServices(IServiceCollection services);
/// <summary>
/// Configure an asp net application applying plugins policies.
/// </summary>
/// <param name="app">The asp net application to configure</param>
public void ConfigureAspnet(IApplicationBuilder app);
}
}

View File

@ -5,7 +5,6 @@ using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Kyoo.Models;
using Kyoo.Models.Attributes;
using Kyoo.Models.Exceptions;
namespace Kyoo.Controllers
@ -182,11 +181,6 @@ namespace Kyoo.Controllers
/// <param name="cancellationToken">A token to request the task's cancellation.
/// If this task is not cancelled quickly, it might be killed by the runner.
/// </param>
/// <remarks>
/// Your task can have any service as a public field and use the <see cref="InjectedAttribute"/>,
/// they will be set to an available service from the service container before calling this method.
/// They also will be removed after this method return (or throw) to prevent dangling services.
/// </remarks>
/// <exception cref="TaskFailedException">
/// An exception meaning that the task has failed for handled reasons like invalid arguments,
/// invalid environment, missing plugins or failures not related to a default in the code.

View File

@ -0,0 +1,221 @@
using System;
using Microsoft.Extensions.DependencyInjection;
namespace Kyoo.Controllers
{
/// <summary>
/// A list of constant priorities used for <see cref="IStartupAction"/>'s <see cref="IStartupAction.Priority"/>.
/// It also contains helper methods for creating new <see cref="StartupAction"/>.
/// </summary>
public static class SA
{
public const int Before = 5000;
public const int Routing = 4000;
public const int StaticFiles = 3000;
public const int Authentication = 2000;
public const int Authorization = 1000;
public const int Endpoint = 0;
public const int After = -1000;
/// <summary>
/// Create a new <see cref="StartupAction"/>.
/// </summary>
/// <param name="action">The action to run</param>
/// <param name="priority">The priority of the new action</param>
/// <returns>A new <see cref="StartupAction"/></returns>
public static StartupAction New(Action action, int priority)
=> new(action, priority);
/// <summary>
/// Create a new <see cref="StartupAction"/>.
/// </summary>
/// <param name="action">The action to run</param>
/// <param name="priority">The priority of the new action</param>
/// <typeparam name="T">A dependency that this action will use.</typeparam>
/// <returns>A new <see cref="StartupAction"/></returns>
public static StartupAction<T> New<T>(Action<T> action, int priority)
=> new(action, priority);
/// <summary>
/// Create a new <see cref="StartupAction"/>.
/// </summary>
/// <param name="action">The action to run</param>
/// <param name="priority">The priority of the new action</param>
/// <typeparam name="T">A dependency that this action will use.</typeparam>
/// <typeparam name="T2">A second dependency that this action will use.</typeparam>
/// <returns>A new <see cref="StartupAction"/></returns>
public static StartupAction<T, T2> New<T, T2>(Action<T, T2> action, int priority)
=> new(action, priority);
/// <summary>
/// Create a new <see cref="StartupAction"/>.
/// </summary>
/// <param name="action">The action to run</param>
/// <param name="priority">The priority of the new action</param>
/// <typeparam name="T">A dependency that this action will use.</typeparam>
/// <typeparam name="T2">A second dependency that this action will use.</typeparam>
/// <typeparam name="T3">A third dependency that this action will use.</typeparam>
/// <returns>A new <see cref="StartupAction"/></returns>
public static StartupAction<T, T2, T3> New<T, T2, T3>(Action<T, T2, T3> action, int priority)
=> new(action, priority);
}
/// <summary>
/// An action executed on kyoo's startup to initialize the asp-net container.
/// </summary>
/// <remarks>
/// This is the base interface, see <see cref="StartupAction"/> for a simpler use of this.
/// </remarks>
public interface IStartupAction
{
/// <summary>
/// The priority of this action. The actions will be executed on descending priority order.
/// If two actions have the same priority, their order is undefined.
/// </summary>
int Priority { get; }
/// <summary>
/// Run this action to configure the container, a service provider containing all services can be used.
/// </summary>
/// <param name="provider">The service provider containing all services can be used.</param>
void Run(IServiceProvider provider);
}
/// <summary>
/// A <see cref="IStartupAction"/> with no dependencies.
/// </summary>
public class StartupAction : IStartupAction
{
/// <summary>
/// The action to execute at startup.
/// </summary>
private readonly Action _action;
/// <inheritdoc />
public int Priority { get; }
/// <summary>
/// Create a new <see cref="StartupAction"/>.
/// </summary>
/// <param name="action">The action to execute on startup.</param>
/// <param name="priority">The priority of this action (see <see cref="Priority"/>).</param>
public StartupAction(Action action, int priority)
{
_action = action;
Priority = priority;
}
/// <inheritdoc />
public void Run(IServiceProvider provider)
{
_action.Invoke();
}
}
/// <summary>
/// A <see cref="IStartupAction"/> with one dependencies.
/// </summary>
/// <typeparam name="T">The dependency to use.</typeparam>
public class StartupAction<T> : IStartupAction
{
/// <summary>
/// The action to execute at startup.
/// </summary>
private readonly Action<T> _action;
/// <inheritdoc />
public int Priority { get; }
/// <summary>
/// Create a new <see cref="StartupAction{T}"/>.
/// </summary>
/// <param name="action">The action to execute on startup.</param>
/// <param name="priority">The priority of this action (see <see cref="Priority"/>).</param>
public StartupAction(Action<T> action, int priority)
{
_action = action;
Priority = priority;
}
/// <inheritdoc />
public void Run(IServiceProvider provider)
{
_action.Invoke(provider.GetRequiredService<T>());
}
}
/// <summary>
/// A <see cref="IStartupAction"/> with two dependencies.
/// </summary>
/// <typeparam name="T">The dependency to use.</typeparam>
/// <typeparam name="T2">The second dependency to use.</typeparam>
public class StartupAction<T, T2> : IStartupAction
{
/// <summary>
/// The action to execute at startup.
/// </summary>
private readonly Action<T, T2> _action;
/// <inheritdoc />
public int Priority { get; }
/// <summary>
/// Create a new <see cref="StartupAction{T, T2}"/>.
/// </summary>
/// <param name="action">The action to execute on startup.</param>
/// <param name="priority">The priority of this action (see <see cref="Priority"/>).</param>
public StartupAction(Action<T, T2> action, int priority)
{
_action = action;
Priority = priority;
}
/// <inheritdoc />
public void Run(IServiceProvider provider)
{
_action.Invoke(
provider.GetRequiredService<T>(),
provider.GetRequiredService<T2>()
);
}
}
/// <summary>
/// A <see cref="IStartupAction"/> with three dependencies.
/// </summary>
/// <typeparam name="T">The dependency to use.</typeparam>
/// <typeparam name="T2">The second dependency to use.</typeparam>
/// <typeparam name="T3">The third dependency to use.</typeparam>
public class StartupAction<T, T2, T3> : IStartupAction
{
/// <summary>
/// The action to execute at startup.
/// </summary>
private readonly Action<T, T2, T3> _action;
/// <inheritdoc />
public int Priority { get; }
/// <summary>
/// Create a new <see cref="StartupAction{T, T2, T3}"/>.
/// </summary>
/// <param name="action">The action to execute on startup.</param>
/// <param name="priority">The priority of this action (see <see cref="Priority"/>).</param>
public StartupAction(Action<T, T2, T3> action, int priority)
{
_action = action;
Priority = priority;
}
/// <inheritdoc />
public void Run(IServiceProvider provider)
{
_action.Invoke(
provider.GetRequiredService<T>(),
provider.GetRequiredService<T2>(),
provider.GetRequiredService<T3>()
);
}
}
}

View File

@ -1,17 +0,0 @@
using System;
using JetBrains.Annotations;
using Kyoo.Controllers;
namespace Kyoo.Models.Attributes
{
/// <summary>
/// An attribute to inform that the service will be injected automatically by a service provider.
/// </summary>
/// <remarks>
/// It should only be used on <see cref="IPlugin"/> and it will be injected before
/// calling <see cref="IPlugin.ConfigureAspNet"/>.
/// </remarks>
[AttributeUsage(AttributeTargets.Property)]
[MeansImplicitUse(ImplicitUseKindFlags.Assign)]
public class InjectedAttribute : Attribute { }
}

View File

@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using Kyoo.Controllers;
using Microsoft.AspNetCore.Hosting;
using Microsoft.EntityFrameworkCore;
@ -22,6 +23,9 @@ namespace Kyoo.Postgresql
/// <inheritdoc />
public string Description => "A database context for postgresql.";
/// <inheritdoc />
public Dictionary<string, Type> Configuration => new();
/// <inheritdoc />
public bool Enabled => _configuration.GetSelectedDatabase() == "postgres";

View File

@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using Kyoo.Controllers;
using Microsoft.AspNetCore.Hosting;
using Microsoft.EntityFrameworkCore;
@ -22,6 +23,9 @@ namespace Kyoo.SqLite
/// <inheritdoc />
public string Description => "A database context for sqlite.";
/// <inheritdoc />
public Dictionary<string, Type> Configuration => new();
/// <inheritdoc />
public bool Enabled => _configuration.GetSelectedDatabase() == "sqlite";

View File

@ -1,10 +1,8 @@
using System;
using System.Collections.Generic;
using Autofac;
using Kyoo.Controllers;
using Kyoo.Models.Attributes;
using Kyoo.TheMovieDb.Models;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
namespace Kyoo.TheMovieDb
{
@ -21,45 +19,17 @@ namespace Kyoo.TheMovieDb
/// <inheritdoc />
public string Description => "A metadata provider for TheMovieDB.";
/// <summary>
/// The configuration to use.
/// </summary>
private readonly IConfiguration _configuration;
/// <summary>
/// The configuration manager used to register typed/untyped implementations.
/// </summary>
[Injected] public IConfigurationManager ConfigurationManager { private get; set; }
/// <summary>
/// Create a new tmdb module instance and use the given configuration.
/// </summary>
/// <param name="configuration">The configuration to use</param>
public PluginTmdb(IConfiguration configuration)
/// <inheritdoc />
public Dictionary<string, Type> Configuration => new()
{
_configuration = configuration;
}
{ TheMovieDbOptions.Path, typeof(TheMovieDbOptions) }
};
/// <inheritdoc />
public void Configure(ContainerBuilder builder)
{
builder.RegisterProvider<TheMovieDbProvider>();
}
/// <inheritdoc />
public void Configure(IServiceCollection services)
{
services.Configure<TheMovieDbOptions>(_configuration.GetSection(TheMovieDbOptions.Path));
}
/// <inheritdoc />
public void ConfigureAspNet(IApplicationBuilder app)
{
ConfigurationManager.AddTyped<TheMovieDbOptions>(TheMovieDbOptions.Path);
}
}
}

View File

@ -1,10 +1,8 @@
using System;
using System.Collections.Generic;
using Autofac;
using Kyoo.Controllers;
using Kyoo.Models.Attributes;
using Kyoo.TheTvdb.Models;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using TvDbSharper;
namespace Kyoo.TheTvdb
@ -23,44 +21,17 @@ namespace Kyoo.TheTvdb
/// <inheritdoc />
public string Description => "A metadata provider for The TVDB.";
/// <summary>
/// The configuration to use.
/// </summary>
private readonly IConfiguration _configuration;
/// <summary>
/// The configuration manager used to register typed/untyped implementations.
/// </summary>
[Injected] public IConfigurationManager ConfigurationManager { private get; set; }
/// <summary>
/// Create a new tvdb module instance and use the given configuration.
/// </summary>
/// <param name="configuration">The configuration to use</param>
public PluginTvdb(IConfiguration configuration)
/// <inheritdoc />
public Dictionary<string, Type> Configuration => new()
{
_configuration = configuration;
}
{ TvdbOption.Path, typeof(TvdbOption) }
};
/// <inheritdoc />
public void Configure(ContainerBuilder builder)
{
builder.RegisterType<TvDbClient>().As<ITvDbClient>();
builder.RegisterProvider<ProviderTvdb>();
}
/// <inheritdoc />
public void Configure(IServiceCollection services)
{
services.Configure<TvdbOption>(_configuration.GetSection(TvdbOption.Path));
}
/// <inheritdoc />
public void ConfigureAspNet(IApplicationBuilder app)
{
ConfigurationManager.AddTyped<TvdbOption>(TvdbOption.Path);
}
}
}

View File

@ -6,7 +6,6 @@ using System.Reflection;
using System.Runtime.Loader;
using Autofac;
using Kyoo.Models.Options;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
@ -148,18 +147,6 @@ namespace Kyoo.Controllers
plugin.Configure(services);
}
/// <inheritdoc />
public void ConfigureAspnet(IApplicationBuilder app)
{
foreach (IPlugin plugin in _plugins)
{
using IServiceScope scope = _provider.CreateScope();
Helper.InjectServices(plugin, x => scope.ServiceProvider.GetRequiredService(x));
plugin.ConfigureAspNet(app);
Helper.InjectServices(plugin, _ => null);
}
}
/// <summary>
/// A custom <see cref="AssemblyLoadContext"/> to load plugin's dependency if they are on the same folder.
/// </summary>

View File

@ -1,10 +1,10 @@
using System;
using System.Collections.Generic;
using System.IO;
using Autofac;
using Autofac.Core;
using Autofac.Core.Registration;
using Kyoo.Controllers;
using Kyoo.Models.Attributes;
using Kyoo.Models.Options;
using Kyoo.Models.Permissions;
using Kyoo.Tasks;
@ -13,6 +13,7 @@ using Microsoft.AspNetCore.StaticFiles;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Hosting;
namespace Kyoo
{
@ -30,18 +31,23 @@ namespace Kyoo
/// <inheritdoc />
public string Description => "The core module containing default implementations.";
/// <inheritdoc />
public Dictionary<string, Type> Configuration => new()
{
{ BasicOptions.Path, typeof(BasicOptions) },
{ TaskOptions.Path, typeof(TaskOptions) },
{ MediaOptions.Path, typeof(MediaOptions) },
{ "database", null },
{ "logging", null }
};
/// <summary>
/// The configuration to use.
/// </summary>
private readonly IConfiguration _configuration;
/// <summary>
/// The configuration manager used to register typed/untyped implementations.
/// </summary>
[Injected] public IConfigurationManager ConfigurationManager { private get; set; }
/// <summary>
/// Create a new core module instance and use the given configuration.
/// </summary>
@ -99,10 +105,6 @@ namespace Kyoo
{
string publicUrl = _configuration.GetPublicUrl();
services.Configure<BasicOptions>(_configuration.GetSection(BasicOptions.Path));
services.Configure<TaskOptions>(_configuration.GetSection(TaskOptions.Path));
services.Configure<MediaOptions>(_configuration.GetSection(MediaOptions.Path));
services.AddControllers()
.AddNewtonsoftJson(x =>
{
@ -114,26 +116,30 @@ namespace Kyoo
}
/// <inheritdoc />
public void ConfigureAspNet(IApplicationBuilder app)
public IEnumerable<IStartupAction> ConfigureSteps => new IStartupAction[]
{
ConfigurationManager.AddTyped<BasicOptions>(BasicOptions.Path);
ConfigurationManager.AddTyped<TaskOptions>(TaskOptions.Path);
ConfigurationManager.AddTyped<MediaOptions>(MediaOptions.Path);
ConfigurationManager.AddUntyped("database");
ConfigurationManager.AddUntyped("logging");
FileExtensionContentTypeProvider contentTypeProvider = new();
contentTypeProvider.Mappings[".data"] = "application/octet-stream";
app.UseStaticFiles(new StaticFileOptions
SA.New<IApplicationBuilder, IHostEnvironment>((app, env) =>
{
ContentTypeProvider = contentTypeProvider,
FileProvider = new PhysicalFileProvider(Path.Join(AppDomain.CurrentDomain.BaseDirectory, "wwwroot"))
});
app.UseEndpoints(endpoints =>
if (env.IsDevelopment())
app.UseDeveloperExceptionPage();
else
{
app.UseExceptionHandler("/error");
app.UseHsts();
}
}, SA.Before),
SA.New<IApplicationBuilder>(app => app.UseRouting(), SA.Routing),
SA.New<IApplicationBuilder>(app =>
{
endpoints.MapControllers();
});
}
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)
};
}
}

View File

@ -1,32 +1,11 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Reflection;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Kyoo.Models.Attributes;
using Newtonsoft.Json;
namespace Kyoo
{
public static class Helper
{
/// <summary>
/// Inject services into the <see cref="InjectedAttribute"/> marked properties of the given object.
/// </summary>
/// <param name="obj">The object to inject</param>
/// <param name="retrieve">The function used to retrieve services. (The function is called immediately)</param>
public static void InjectServices(object obj, [InstantHandle] Func<Type, object> retrieve)
{
IEnumerable<PropertyInfo> properties = obj.GetType().GetProperties()
.Where(x => x.GetCustomAttribute<InjectedAttribute>() != null)
.Where(x => x.CanWrite);
foreach (PropertyInfo property in properties)
property.SetValue(obj, retrieve(property.PropertyType));
}
/// <summary>
/// An helper method to get json content from an http server. This is a temporary thing and will probably be
/// replaced by a call to the function of the same name in the <c>System.Net.Http.Json</c> namespace when .net6

View File

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Autofac;
using Autofac.Extras.AttributeMetadata;
using Kyoo.Authentication;
@ -90,18 +91,9 @@ namespace Kyoo
/// <param name="env">The host environment (is the app in development mode?)</param>
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IServiceProvider provider)
{
if (env.IsDevelopment())
app.UseDeveloperExceptionPage();
else
{
app.UseExceptionHandler("/error");
app.UseHsts();
}
if (!env.IsDevelopment())
app.UseSpaStaticFiles();
app.UseRouting();
app.Use((ctx, next) =>
{
ctx.Response.Headers.Remove("X-Powered-By");
@ -116,9 +108,11 @@ namespace Kyoo
});
app.UseResponseCompression();
if (_plugins is PluginManager manager)
manager.SetProvider(provider);
_plugins.ConfigureAspnet(app);
IEnumerable<IStartupAction> steps = _plugins.GetAllPlugins()
.SelectMany(x => x.ConfigureSteps)
.OrderByDescending(x => x.Priority);
foreach (IStartupAction step in steps)
step.Run(provider);
app.UseSpa(spa =>
{

View File

@ -28,8 +28,8 @@ namespace Kyoo
[SuppressMessage("ReSharper", "ConditionIsAlwaysTrueOrFalse")]
public static async Task Main(string[] args)
{
if (!File.Exists("./settings.json"))
File.Copy(Path.Join(AppDomain.CurrentDomain.BaseDirectory, "settings.json"), "settings.json");
if (!File.Exists(JsonConfigPath))
File.Copy(Path.Join(AppDomain.CurrentDomain.BaseDirectory, JsonConfigPath), JsonConfigPath);
IHostBuilder builder = CreateWebHostBuilder(args);
@ -77,7 +77,8 @@ namespace Kyoo
/// <returns>The modified configuration builder</returns>
private static IConfigurationBuilder SetupConfig(IConfigurationBuilder builder, string[] args)
{
return builder.AddJsonFile(JsonConfigPath, false, true)
return builder.SetBasePath(Environment.CurrentDirectory)
.AddJsonFile(JsonConfigPath, false, true)
.AddEnvironmentVariables()
.AddCommandLine(args);
}