From e02af774a95c73ff650750346c176e1d4322e9b1 Mon Sep 17 00:00:00 2001 From: Masu Baumgartner <68913099+Masu-Baumgartner@users.noreply.github.com> Date: Mon, 28 Oct 2024 21:30:00 +0100 Subject: [PATCH] Cleaned up the startup sequence. --- .../Helpers/DatabaseContextCollection.cs | 17 ++ .../Implementations/Startup/ApiDocsStartup.cs | 46 +++ .../Startup/CoreDatabaseStartup.cs | 15 + .../Interfaces/Startup/IAppStartup.cs | 7 + .../Interfaces/Startup/IDatabaseStartup.cs | 9 + .../Interfaces/Startup/IEndpointStartup.cs | 6 + .../Moonlight.ApiServer.csproj | 2 +- Moonlight.ApiServer/Program.cs | 286 ++++++++---------- Moonlight.ApiServer/Startup.cs | 180 +++++++++++ Moonlight.Client/Styles/mappings/mooncore.map | 0 10 files changed, 402 insertions(+), 166 deletions(-) create mode 100644 Moonlight.ApiServer/Helpers/DatabaseContextCollection.cs create mode 100644 Moonlight.ApiServer/Implementations/Startup/ApiDocsStartup.cs create mode 100644 Moonlight.ApiServer/Implementations/Startup/CoreDatabaseStartup.cs create mode 100644 Moonlight.ApiServer/Interfaces/Startup/IAppStartup.cs create mode 100644 Moonlight.ApiServer/Interfaces/Startup/IDatabaseStartup.cs create mode 100644 Moonlight.ApiServer/Interfaces/Startup/IEndpointStartup.cs create mode 100644 Moonlight.ApiServer/Startup.cs mode change 100644 => 100755 Moonlight.Client/Styles/mappings/mooncore.map diff --git a/Moonlight.ApiServer/Helpers/DatabaseContextCollection.cs b/Moonlight.ApiServer/Helpers/DatabaseContextCollection.cs new file mode 100644 index 00000000..781dc97b --- /dev/null +++ b/Moonlight.ApiServer/Helpers/DatabaseContextCollection.cs @@ -0,0 +1,17 @@ +using System.Collections; + +namespace Moonlight.ApiServer.Helpers; + +public class DatabaseContextCollection : IEnumerable +{ + private readonly List Types = new(); + + public IEnumerator GetEnumerator() => Types.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => Types.GetEnumerator(); + + public void Add() where T : DatabaseContext + => Types.Add(typeof(T)); + + public void Remove(Type type) => Types.Remove(type); + public void Remove() where T : DatabaseContext => Remove(typeof(T)); +} \ No newline at end of file diff --git a/Moonlight.ApiServer/Implementations/Startup/ApiDocsStartup.cs b/Moonlight.ApiServer/Implementations/Startup/ApiDocsStartup.cs new file mode 100644 index 00000000..001a1b91 --- /dev/null +++ b/Moonlight.ApiServer/Implementations/Startup/ApiDocsStartup.cs @@ -0,0 +1,46 @@ +using Microsoft.OpenApi.Models; +using MoonCore.Services; +using Moonlight.ApiServer.Configuration; +using Moonlight.ApiServer.Interfaces.Startup; + +namespace Moonlight.ApiServer.Implementations.Startup; + +public class ApiDocsStartup : IAppStartup, IEndpointStartup +{ + private readonly ConfigService ConfigService; + private readonly ILogger Logger; + + public ApiDocsStartup(ConfigService configService, ILogger logger) + { + ConfigService = configService; + Logger = logger; + } + + public Task BuildApp(IHostApplicationBuilder builder) + { + if(!ConfigService.Get().Development.EnableApiDocs) + return Task.CompletedTask; + + builder.Services.AddEndpointsApiExplorer(); + + // Configure swagger api specification generator and set the document title for the api docs to use + builder.Services.AddSwaggerGen(options => options.SwaggerDoc("main", new OpenApiInfo() + { + Title = "Moonlight API" + })); + + return Task.CompletedTask; + } + + public Task ConfigureApp(IApplicationBuilder app) => Task.CompletedTask; + + public Task ConfigureEndpoints(IEndpointRouteBuilder routeBuilder) + { + if(!ConfigService.Get().Development.EnableApiDocs) + return Task.CompletedTask; + + routeBuilder.MapSwagger("/api/swagger/{documentName}"); + + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/Moonlight.ApiServer/Implementations/Startup/CoreDatabaseStartup.cs b/Moonlight.ApiServer/Implementations/Startup/CoreDatabaseStartup.cs new file mode 100644 index 00000000..7023d041 --- /dev/null +++ b/Moonlight.ApiServer/Implementations/Startup/CoreDatabaseStartup.cs @@ -0,0 +1,15 @@ +using Moonlight.ApiServer.Database; +using Moonlight.ApiServer.Helpers; +using Moonlight.ApiServer.Interfaces.Startup; + +namespace Moonlight.ApiServer.Implementations.Startup; + +public class CoreDatabaseStartup : IDatabaseStartup +{ + public Task ConfigureDatabase(DatabaseContextCollection collection) + { + collection.Add(); + + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/Moonlight.ApiServer/Interfaces/Startup/IAppStartup.cs b/Moonlight.ApiServer/Interfaces/Startup/IAppStartup.cs new file mode 100644 index 00000000..34b9ddfc --- /dev/null +++ b/Moonlight.ApiServer/Interfaces/Startup/IAppStartup.cs @@ -0,0 +1,7 @@ +namespace Moonlight.ApiServer.Interfaces.Startup; + +public interface IAppStartup +{ + public Task BuildApp(IHostApplicationBuilder builder); + public Task ConfigureApp(IApplicationBuilder app); +} \ No newline at end of file diff --git a/Moonlight.ApiServer/Interfaces/Startup/IDatabaseStartup.cs b/Moonlight.ApiServer/Interfaces/Startup/IDatabaseStartup.cs new file mode 100644 index 00000000..d8c9a952 --- /dev/null +++ b/Moonlight.ApiServer/Interfaces/Startup/IDatabaseStartup.cs @@ -0,0 +1,9 @@ +using Moonlight.ApiServer.Helpers; +using Moonlight.ApiServer.Models; + +namespace Moonlight.ApiServer.Interfaces.Startup; + +public interface IDatabaseStartup +{ + public Task ConfigureDatabase(DatabaseContextCollection collection); +} \ No newline at end of file diff --git a/Moonlight.ApiServer/Interfaces/Startup/IEndpointStartup.cs b/Moonlight.ApiServer/Interfaces/Startup/IEndpointStartup.cs new file mode 100644 index 00000000..d963b9ab --- /dev/null +++ b/Moonlight.ApiServer/Interfaces/Startup/IEndpointStartup.cs @@ -0,0 +1,6 @@ +namespace Moonlight.ApiServer.Interfaces.Startup; + +public interface IEndpointStartup +{ + public Task ConfigureEndpoints(IEndpointRouteBuilder routeBuilder); +} \ No newline at end of file diff --git a/Moonlight.ApiServer/Moonlight.ApiServer.csproj b/Moonlight.ApiServer/Moonlight.ApiServer.csproj index ae027ca5..b55cbc6b 100644 --- a/Moonlight.ApiServer/Moonlight.ApiServer.csproj +++ b/Moonlight.ApiServer/Moonlight.ApiServer.csproj @@ -13,7 +13,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/Moonlight.ApiServer/Program.cs b/Moonlight.ApiServer/Program.cs index 918c5cca..0c71fa86 100644 --- a/Moonlight.ApiServer/Program.cs +++ b/Moonlight.ApiServer/Program.cs @@ -1,55 +1,19 @@ using System.Reflection; -using System.Text.Json; -using Microsoft.OpenApi.Models; -using MoonCore.Extended.Abstractions; -using MoonCore.Extended.Extensions; using MoonCore.Extended.Helpers; -using MoonCore.Extended.OAuth2.ApiServer; using MoonCore.Extensions; using MoonCore.Helpers; -using MoonCore.Models; using MoonCore.PluginFramework.Extensions; -using MoonCore.PluginFramework.Services; using MoonCore.Services; +using Moonlight.ApiServer; using Moonlight.ApiServer.Configuration; -using Moonlight.ApiServer.Database; -using Moonlight.ApiServer.Database.Entities; using Moonlight.ApiServer.Helpers; -using Moonlight.ApiServer.Helpers.Authentication; using Moonlight.ApiServer.Http.Middleware; -using Moonlight.ApiServer.Implementations.OAuth2; using Moonlight.ApiServer.Interfaces.Auth; using Moonlight.ApiServer.Interfaces.OAuth2; +using Moonlight.ApiServer.Interfaces.Startup; -// Prepare file system -Directory.CreateDirectory(PathBuilder.Dir("storage")); -Directory.CreateDirectory(PathBuilder.Dir("storage", "plugins")); -Directory.CreateDirectory(PathBuilder.Dir("storage", "clientPlugins")); -Directory.CreateDirectory(PathBuilder.Dir("storage", "logs")); - -// Configuration -var configService = new ConfigService( - PathBuilder.File("storage", "config.json") -); - -var config = configService.Get(); - -ApplicationStateHelper.SetConfiguration(configService); - -// Build pre run logger -var providers = LoggerBuildHelper.BuildFromConfiguration(configuration => -{ - configuration.Console.Enable = true; - configuration.Console.EnableAnsiMode = true; - - configuration.FileLogging.Enable = true; - configuration.FileLogging.Path = PathBuilder.File("storage", "logs", "moonlight.log"); - configuration.FileLogging.EnableLogRotation = true; - configuration.FileLogging.RotateLogNameTemplate = PathBuilder.File("storage", "logs", "moonlight.log.{0}"); -}); - -using var loggerFactory = new LoggerFactory(providers); -var logger = loggerFactory.CreateLogger("Startup"); +// Cry about it +#pragma warning disable ASP0000 // Fancy start console output... yes very fancy :> var rainbow = new Crayon.Rainbow(0.5); @@ -65,144 +29,105 @@ foreach (var c in "Moonlight") Console.WriteLine(); -var builder = WebApplication.CreateBuilder(args); +// Storage i guess +Directory.CreateDirectory(PathBuilder.Dir("storage")); -// Configure application logging -builder.Logging.ClearProviders(); -builder.Logging.AddProviders(providers); - -// Logging levels -var logConfigPath = PathBuilder.File("storage", "logConfig.json"); - -// Ensure logging config, add a default one is missing -if (!File.Exists(logConfigPath)) -{ - await File.WriteAllTextAsync(logConfigPath, - "{\"LogLevel\":{\"Default\":\"Information\",\"Microsoft.AspNetCore\":\"Warning\",\"MoonCore.Extended.Helpers.JwtHelper\": \"Error\"}}"); -} - -builder.Logging.AddConfiguration(await File.ReadAllTextAsync(logConfigPath)); - -builder.Services.AddControllers(); - -builder.Services.AddEndpointsApiExplorer(); -builder.Services.AddSwaggerGen(); - -builder.Services.AddSingleton(configService); - -builder.Services.AutoAddServices(); - -// OAuth2 -builder.Services.AddSingleton(); - -builder.Services.AddHttpClient(); -builder.Services.AddOAuth2Consumer(configuration => -{ - configuration.ClientId = config.Authentication.OAuth2.ClientId; - configuration.ClientSecret = config.Authentication.OAuth2.ClientSecret; - configuration.AuthorizationRedirect = - config.Authentication.OAuth2.AuthorizationRedirect ?? $"{config.PublicUrl}/auth"; - - configuration.AccessEndpoint = config.Authentication.OAuth2.AccessEndpoint ?? $"{config.PublicUrl}/oauth2/access"; - configuration.RefreshEndpoint = config.Authentication.OAuth2.RefreshEndpoint ?? $"{config.PublicUrl}/oauth2/refresh"; - - if (config.Authentication.UseLocalOAuth2) - { - configuration.AuthorizationEndpoint = config.Authentication.OAuth2.AuthorizationRedirect ?? $"{config.PublicUrl}/oauth2/authorize"; - } - else - { - if(config.Authentication.OAuth2.AuthorizationUri == null) - logger.LogWarning("The 'AuthorizationUri' for the oauth2 client is not set. If you want to use an external oauth2 provider, you need to specify this url. If you want to use the local oauth2 service, set 'UseLocalOAuth2Service' to true"); - - configuration.AuthorizationEndpoint = config.Authentication.OAuth2.AuthorizationUri!; - } -}); - -if (config.Authentication.UseLocalOAuth2) -{ - logger.LogInformation("Using local oauth2 provider"); - - builder.Services.AddOAuth2Provider(configuration => - { - configuration.AccessSecret = config.Authentication.LocalOAuth2.AccessSecret; - configuration.RefreshSecret = config.Authentication.LocalOAuth2.RefreshSecret; - - configuration.ClientId = config.Authentication.OAuth2.ClientId; - configuration.ClientSecret = config.Authentication.OAuth2.ClientSecret; - configuration.CodeSecret = config.Authentication.LocalOAuth2.CodeSecret; - configuration.AuthorizationRedirect = - config.Authentication.OAuth2.AuthorizationRedirect ?? $"{config.PublicUrl}/auth"; - configuration.AccessTokenDuration = 60; - configuration.RefreshTokenDuration = 3600; - }); -} - -builder.Services.AddTokenAuthentication(configuration => -{ - configuration.AccessSecret = config.Authentication.AccessSecret; - configuration.DataLoader = async (data, provider, context) => - { - if (!data.TryGetValue("userId", out var userIdStr) || !userIdStr.TryGetInt32(out var userId)) - return false; - - var userRepo = provider.GetRequiredService>(); - var user = userRepo.Get().FirstOrDefault(x => x.Id == userId); - - if (user == null) - return false; - - // Load permissions, handle empty values - var permissions = JsonSerializer.Deserialize( - string.IsNullOrEmpty(user.PermissionsJson) ? "[]" : user.PermissionsJson - ) ?? []; - - // Save permission state - context.User = new PermClaimsPrinciple(permissions, user); - - return true; - }; -}); - -// Database -var databaseHelper = new DatabaseHelper( - loggerFactory.CreateLogger() +// Configuration +var configService = new ConfigService( + PathBuilder.File("storage", "config.json") ); -builder.Services.AddSingleton(databaseHelper); -builder.Services.AddScoped(typeof(DatabaseRepository<>)); -builder.Services.AddScoped(typeof(CrudHelper<,>)); +var config = configService.Get(); -builder.Services.AddDbContext(); -databaseHelper.AddDbContext(); +ApplicationStateHelper.SetConfiguration(configService); -databaseHelper.GenerateMappings(); +// TODO: Load plugin/module assemblies -// API Docs -if (configService.Get().Development.EnableApiDocs) +// Configure startup logger +var startupLoggerFactory = new LoggerFactory(); + +// TODO: Add direct extension method +var providers = LoggerBuildHelper.BuildFromConfiguration(configuration => { - // Configure swagger api specification generator and set the document title for the api docs to use - builder.Services.AddSwaggerGen(options => options.SwaggerDoc("main", new OpenApiInfo() + configuration.Console.Enable = true; + configuration.Console.EnableAnsiMode = true; + configuration.FileLogging.Enable = false; +}); + +startupLoggerFactory.AddProviders(providers); + +var startupLogger = startupLoggerFactory.CreateLogger("Startup"); + +// Configure startup interfaces +var startupServiceCollection = new ServiceCollection(); +startupServiceCollection.AddSingleton(configService); + +startupServiceCollection.AddLogging(loggingBuilder => { loggingBuilder.AddProviders(providers); }); + +startupServiceCollection.AddPlugins(configuration => +{ + // Configure startup interfaces + configuration.AddInterface(); + configuration.AddInterface(); + configuration.AddInterface(); + + // Configure assemblies to scan + configuration.AddAssembly(Assembly.GetEntryAssembly()!); +}, startupLogger); + + +var startupServiceProvider = startupServiceCollection.BuildServiceProvider(); +var appStartupInterfaces = startupServiceProvider.GetRequiredService(); + +// Start the actual app +var builder = WebApplication.CreateBuilder(args); + +await Startup.ConfigureLogging(builder); + +await Startup.ConfigureDatabase( + builder, + startupLoggerFactory, + startupServiceProvider.GetRequiredService() +); + +// Call interfaces +foreach (var startupInterface in appStartupInterfaces) +{ + try { - Title = "Moonlight API" - })); + await startupInterface.BuildApp(builder); + } + catch (Exception e) + { + startupLogger.LogCritical( + "An unhandled error occured while processing BuildApp call for interface '{interfaceName}': {e}", + startupInterface.GetType().FullName, + e + ); + } } +builder.Services.AddControllers(); +builder.Services.AddSingleton(configService); +builder.Services.AutoAddServices(); +builder.Services.AddSingleton(); +builder.Services.AddHttpClient(); + +await Startup.ConfigureTokenAuthentication(builder, config); +await Startup.ConfigureOAuth2(builder, startupLogger, config); + // Implementation service builder.Services.AddPlugins(configuration => { configuration.AddInterface(); configuration.AddInterface(); - + configuration.AddAssembly(Assembly.GetEntryAssembly()!); -}, logger); +}, startupLogger); var app = builder.Build(); -using (var scope = app.Services.CreateScope()) -{ - await databaseHelper.EnsureMigrated(scope.ServiceProvider); -} +await Startup.PrepareDatabase(app); if (app.Environment.IsDevelopment()) app.UseWebAssemblyDebugging(); @@ -214,16 +139,47 @@ app.UseRouting(); app.UseMiddleware(); -app.UseTokenAuthentication(); +await Startup.UseTokenAuthentication(app); + +// Call interfaces +foreach (var startupInterface in appStartupInterfaces) +{ + try + { + await startupInterface.ConfigureApp(app); + } + catch (Exception e) + { + startupLogger.LogCritical( + "An unhandled error occured while processing ConfigureApp call for interface '{interfaceName}': {e}", + startupInterface.GetType().FullName, + e + ); + } +} app.UseMiddleware(); -app.MapControllers(); +// Call interfaces +var endpointStartupInterfaces = startupServiceProvider.GetRequiredService(); +foreach (var endpointStartup in endpointStartupInterfaces) +{ + try + { + await endpointStartup.ConfigureEndpoints(app); + } + catch (Exception e) + { + startupLogger.LogCritical( + "An unhandled error occured while processing ConfigureEndpoints call for interface '{interfaceName}': {e}", + endpointStartup.GetType().FullName, + e + ); + } +} + +app.MapControllers(); app.MapFallbackToFile("index.html"); -// API Docs -if (configService.Get().Development.EnableApiDocs) - app.MapSwagger("/api/swagger/{documentName}"); - app.Run(); \ No newline at end of file diff --git a/Moonlight.ApiServer/Startup.cs b/Moonlight.ApiServer/Startup.cs new file mode 100644 index 00000000..8a291170 --- /dev/null +++ b/Moonlight.ApiServer/Startup.cs @@ -0,0 +1,180 @@ +using System.Text.Json; +using MoonCore.Extended.Abstractions; +using MoonCore.Extended.Extensions; +using MoonCore.Extended.Helpers; +using MoonCore.Extensions; +using MoonCore.Helpers; +using Moonlight.ApiServer.Configuration; +using Moonlight.ApiServer.Database.Entities; +using Moonlight.ApiServer.Helpers; +using Moonlight.ApiServer.Helpers.Authentication; +using Moonlight.ApiServer.Interfaces.Startup; + +namespace Moonlight.ApiServer; + +public static class Startup +{ + #region Logging + + public static async Task ConfigureLogging(IHostApplicationBuilder builder) + { + // Configure application logging + builder.Logging.ClearProviders(); + + builder.Logging.AddMoonCore(configuration => + { + configuration.Console.Enable = true; + configuration.Console.EnableAnsiMode = true; + + configuration.FileLogging.Enable = true; + configuration.FileLogging.Path = PathBuilder.File("storage", "logs", "moonlight.log"); + configuration.FileLogging.EnableLogRotation = true; + configuration.FileLogging.RotateLogNameTemplate = PathBuilder.File("storage", "logs", "moonlight.log.{0}"); + }); + + // Logging levels + var logConfigPath = PathBuilder.File("storage", "logConfig.json"); + + // Ensure logging config, add a default one is missing + if (!File.Exists(logConfigPath)) + { + await File.WriteAllTextAsync(logConfigPath, + "{\"LogLevel\":{\"Default\":\"Information\",\"Microsoft.AspNetCore\":\"Warning\",\"MoonCore.Extended.Helpers.JwtHelper\": \"Error\"}}"); + } + + builder.Logging.AddConfiguration(await File.ReadAllTextAsync(logConfigPath)); + } + + #endregion + + #region Token Authentication + + public static Task ConfigureTokenAuthentication(IHostApplicationBuilder builder, AppConfiguration config) + { + builder.Services.AddTokenAuthentication(configuration => + { + configuration.AccessSecret = config.Authentication.AccessSecret; + configuration.DataLoader = async (data, provider, context) => + { + if (!data.TryGetValue("userId", out var userIdStr) || !userIdStr.TryGetInt32(out var userId)) + return false; + + var userRepo = provider.GetRequiredService>(); + var user = userRepo.Get().FirstOrDefault(x => x.Id == userId); + + if (user == null) + return false; + + // Load permissions, handle empty values + var permissions = JsonSerializer.Deserialize( + string.IsNullOrEmpty(user.PermissionsJson) ? "[]" : user.PermissionsJson + ) ?? []; + + // Save permission state + context.User = new PermClaimsPrinciple(permissions, user); + + return true; + }; + }); + + return Task.CompletedTask; + } + + public static Task UseTokenAuthentication(IApplicationBuilder builder) + { + builder.UseTokenAuthentication(); + return Task.CompletedTask; + } + + #endregion + + #region Database + + public static async Task ConfigureDatabase(IHostApplicationBuilder builder, ILoggerFactory loggerFactory, + IDatabaseStartup[] databaseStartups) + { + var logger = loggerFactory.CreateLogger(); + var databaseHelper = new DatabaseHelper(logger); + + var databaseCollection = new DatabaseContextCollection(); + + foreach (var databaseStartup in databaseStartups) + await databaseStartup.ConfigureDatabase(databaseCollection); + + foreach (var database in databaseCollection) + { + databaseHelper.AddDbContext(database); + builder.Services.AddScoped(database); + } + + databaseHelper.GenerateMappings(); + + builder.Services.AddSingleton(databaseHelper); + builder.Services.AddScoped(typeof(DatabaseRepository<>)); + builder.Services.AddScoped(typeof(CrudHelper<,>)); + } + + public static async Task PrepareDatabase(IApplicationBuilder builder) + { + using var scope = builder.ApplicationServices.CreateScope(); + var databaseHelper = scope.ServiceProvider.GetRequiredService(); + + await databaseHelper.EnsureMigrated(scope.ServiceProvider); + } + + #endregion + + #region OAuth2 + + public static Task ConfigureOAuth2(IHostApplicationBuilder builder, ILogger logger, AppConfiguration config) + { + builder.Services.AddOAuth2Consumer(configuration => + { + configuration.ClientId = config.Authentication.OAuth2.ClientId; + configuration.ClientSecret = config.Authentication.OAuth2.ClientSecret; + configuration.AuthorizationRedirect = + config.Authentication.OAuth2.AuthorizationRedirect ?? $"{config.PublicUrl}/auth"; + + configuration.AccessEndpoint = + config.Authentication.OAuth2.AccessEndpoint ?? $"{config.PublicUrl}/oauth2/access"; + configuration.RefreshEndpoint = + config.Authentication.OAuth2.RefreshEndpoint ?? $"{config.PublicUrl}/oauth2/refresh"; + + if (config.Authentication.UseLocalOAuth2) + { + configuration.AuthorizationEndpoint = config.Authentication.OAuth2.AuthorizationRedirect ?? + $"{config.PublicUrl}/oauth2/authorize"; + } + else + { + if (config.Authentication.OAuth2.AuthorizationUri == null) + logger.LogWarning( + "The 'AuthorizationUri' for the oauth2 client is not set. If you want to use an external oauth2 provider, you need to specify this url. If you want to use the local oauth2 service, set 'UseLocalOAuth2Service' to true"); + + configuration.AuthorizationEndpoint = config.Authentication.OAuth2.AuthorizationUri!; + } + }); + + if (!config.Authentication.UseLocalOAuth2) return Task.CompletedTask; + + logger.LogInformation("Using local oauth2 provider"); + + builder.Services.AddOAuth2Provider(configuration => + { + configuration.AccessSecret = config.Authentication.LocalOAuth2.AccessSecret; + configuration.RefreshSecret = config.Authentication.LocalOAuth2.RefreshSecret; + + configuration.ClientId = config.Authentication.OAuth2.ClientId; + configuration.ClientSecret = config.Authentication.OAuth2.ClientSecret; + configuration.CodeSecret = config.Authentication.LocalOAuth2.CodeSecret; + configuration.AuthorizationRedirect = + config.Authentication.OAuth2.AuthorizationRedirect ?? $"{config.PublicUrl}/auth"; + configuration.AccessTokenDuration = 60; + configuration.RefreshTokenDuration = 3600; + }); + + return Task.CompletedTask; + } + + #endregion +} \ No newline at end of file diff --git a/Moonlight.Client/Styles/mappings/mooncore.map b/Moonlight.Client/Styles/mappings/mooncore.map old mode 100644 new mode 100755