Implemented hybrid cache for user sessions, api keys and database provided settings. Cleaned up startup and adjusted caching option models for features
This commit is contained in:
@@ -17,20 +17,26 @@ public partial class Startup
|
||||
{
|
||||
private static void AddAuth(WebApplicationBuilder builder)
|
||||
{
|
||||
// OIDC
|
||||
var oidcOptions = new OidcOptions();
|
||||
builder.Configuration.GetSection("Moonlight:Oidc").Bind(oidcOptions);
|
||||
|
||||
// API Key
|
||||
var apiKeyOptions = new ApiOptions();
|
||||
builder.Configuration.GetSection("Moonlight:Api").Bind(apiKeyOptions);
|
||||
builder.Services.AddOptions<ApiOptions>().BindConfiguration("Moonlight:Api");
|
||||
|
||||
// Session
|
||||
builder.Services.AddOptions<UserOptions>().BindConfiguration("Moonlight:User");
|
||||
|
||||
builder.Services.AddScoped<UserAuthService>();
|
||||
|
||||
// Authentication
|
||||
builder.Services.AddAuthentication("Main")
|
||||
.AddPolicyScheme("Main", null, options =>
|
||||
{
|
||||
options.ForwardDefaultSelector += context => context.Request.Headers.Authorization.Count > 0 ? "ApiKey" : "Session";
|
||||
})
|
||||
.AddPolicyScheme("Main", null,
|
||||
options =>
|
||||
{
|
||||
options.ForwardDefaultSelector += context =>
|
||||
context.Request.Headers.Authorization.Count > 0 ? "ApiKey" : "Session";
|
||||
})
|
||||
.AddCookie("Session", null, options =>
|
||||
{
|
||||
options.Events.OnSigningIn += async context =>
|
||||
@@ -83,7 +89,7 @@ public partial class Startup
|
||||
var scopes = oidcOptions.Scopes ?? ["openid", "email", "profile"];
|
||||
|
||||
options.Scope.Clear();
|
||||
|
||||
|
||||
foreach (var scope in scopes)
|
||||
options.Scope.Add(scope);
|
||||
|
||||
@@ -97,18 +103,26 @@ public partial class Startup
|
||||
|
||||
options.GetClaimsFromUserInfoEndpoint = true;
|
||||
})
|
||||
.AddScheme<ApiKeySchemeOptions, ApiKeySchemeHandler>("ApiKey", null, options =>
|
||||
{
|
||||
options.LookupCacheTime = TimeSpan.FromMinutes(apiKeyOptions.LookupCacheMinutes);
|
||||
});
|
||||
|
||||
builder.Logging.AddFilter("Moonlight.Api.Implementations.ApiKeyScheme.ApiKeySchemeHandler", LogLevel.Warning);
|
||||
.AddScheme<ApiKeySchemeOptions, ApiKeySchemeHandler>("ApiKey", null,
|
||||
options =>
|
||||
{
|
||||
options.LookupL1CacheTime = apiKeyOptions.LookupCacheL1Expiry;
|
||||
options.LookupL2CacheTime = apiKeyOptions.LookupCacheL2Expiry;
|
||||
});
|
||||
|
||||
// Authorization
|
||||
builder.Services.AddAuthorization();
|
||||
|
||||
// Reduce log noise
|
||||
builder.Logging.AddFilter("Moonlight.Api.Implementations.ApiKeyScheme.ApiKeySchemeHandler", LogLevel.Warning);
|
||||
|
||||
// Custom permission handling using named policies
|
||||
builder.Services.AddSingleton<IAuthorizationHandler, PermissionAuthorizationHandler>();
|
||||
builder.Services.AddSingleton<IAuthorizationPolicyProvider, PermissionPolicyProvider>();
|
||||
|
||||
builder.Services.AddOptions<SettingsOptions>().BindConfiguration("Moonlight:Settings");
|
||||
builder.Services.AddScoped<SettingsService>();
|
||||
builder.Services.AddScoped<UserDeletionService>();
|
||||
builder.Services.AddScoped<UserLogoutService>();
|
||||
builder.Services.AddScoped<UserAuthService>();
|
||||
}
|
||||
|
||||
private static void UseAuth(WebApplication application)
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.DataProtection;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Logging.Console;
|
||||
@@ -9,7 +11,6 @@ using Moonlight.Api.Helpers;
|
||||
using Moonlight.Api.Implementations;
|
||||
using Moonlight.Api.Interfaces;
|
||||
using Moonlight.Api.Services;
|
||||
using SessionOptions = Moonlight.Api.Configuration.SessionOptions;
|
||||
|
||||
namespace Moonlight.Api.Startup;
|
||||
|
||||
@@ -17,44 +18,63 @@ public partial class Startup
|
||||
{
|
||||
private static void AddBase(WebApplicationBuilder builder)
|
||||
{
|
||||
// Create the base directory
|
||||
Directory.CreateDirectory("storage");
|
||||
|
||||
// Hook up source-generated serialization
|
||||
builder.Services.AddControllers().AddJsonOptions(options =>
|
||||
{
|
||||
options.JsonSerializerOptions.TypeInfoResolverChain.Add(SerializationContext.Default);
|
||||
});
|
||||
|
||||
// Configure logging
|
||||
builder.Logging.ClearProviders();
|
||||
builder.Logging.AddConsole(options => { options.FormatterName = nameof(AppConsoleFormatter); });
|
||||
builder.Logging.AddConsoleFormatter<AppConsoleFormatter, ConsoleFormatterOptions>();
|
||||
|
||||
// Application service
|
||||
builder.Services.AddSingleton<ApplicationService>();
|
||||
builder.Services.AddHostedService(sp => sp.GetRequiredService<ApplicationService>());
|
||||
|
||||
// Diagnose
|
||||
builder.Services.AddSingleton<DiagnoseService>();
|
||||
|
||||
builder.Services.AddSingleton<IDiagnoseProvider, UpdateDiagnoseProvider>();
|
||||
|
||||
builder.Services.AddMemoryCache();
|
||||
builder.Services.AddOptions<SessionOptions>().BindConfiguration("Moonlight:Session");
|
||||
|
||||
// Frontend
|
||||
builder.Services.AddOptions<FrontendOptions>().BindConfiguration("Moonlight:Frontend");
|
||||
builder.Services.AddScoped<FrontendService>();
|
||||
|
||||
// HTTP Client
|
||||
builder.Services.AddHttpClient();
|
||||
|
||||
|
||||
// Version fetching configuration
|
||||
builder.Services.AddOptions<VersionOptions>().BindConfiguration("Moonlight:Version");
|
||||
builder.Services.AddSingleton<VersionService>();
|
||||
|
||||
// Container Helper Options
|
||||
builder.Configuration.GetSection("Moonlight:ContainerHelper").Bind(builder.Configuration);
|
||||
|
||||
builder.Services.AddOptions<ContainerHelperOptions>().BindConfiguration("Moonlight:ContainerHelper");
|
||||
builder.Services.AddSingleton<ContainerHelperService>();
|
||||
|
||||
|
||||
builder.Services.AddHttpClient("ContainerHelper", (provider, client) =>
|
||||
{
|
||||
var options = provider.GetRequiredService<IOptions<ContainerHelperOptions>>();
|
||||
client.BaseAddress = new Uri(options.Value.IsEnabled ? options.Value.Url : "http://you-should-fail.invalid");
|
||||
var options = provider.GetRequiredService<IOptions<ContainerHelperOptions>>();
|
||||
client.BaseAddress =
|
||||
new Uri(options.Value.IsEnabled ? options.Value.Url : "http://you-should-fail.invalid");
|
||||
});
|
||||
|
||||
|
||||
// User management services
|
||||
builder.Services.AddScoped<UserDeletionService>();
|
||||
builder.Services.AddScoped<UserLogoutService>();
|
||||
|
||||
// Settings options
|
||||
builder.Services.AddOptions<SettingsOptions>().BindConfiguration("Moonlight:Settings");
|
||||
builder.Services.AddScoped<SettingsService>();
|
||||
|
||||
// Setup key loading
|
||||
var keysDirectory = new DirectoryInfo(Path.Combine("storage", "keys"));
|
||||
builder.Services.AddDataProtection().PersistKeysToFileSystem(keysDirectory);
|
||||
}
|
||||
|
||||
private static void UseBase(WebApplication application)
|
||||
@@ -67,8 +87,8 @@ public partial class Startup
|
||||
application.MapControllers();
|
||||
|
||||
var options = application.Services.GetRequiredService<IOptions<FrontendOptions>>();
|
||||
|
||||
if(options.Value.Enabled)
|
||||
|
||||
if (options.Value.Enabled)
|
||||
application.MapFallbackToFile("index.html");
|
||||
}
|
||||
}
|
||||
34
Moonlight.Api/Startup/Startup.Cache.cs
Normal file
34
Moonlight.Api/Startup/Startup.Cache.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Moonlight.Api.Configuration;
|
||||
|
||||
namespace Moonlight.Api.Startup;
|
||||
|
||||
public partial class Startup
|
||||
{
|
||||
private static void AddCache(WebApplicationBuilder builder)
|
||||
{
|
||||
// Load cache options
|
||||
var cacheOptions = new CacheOptions();
|
||||
builder.Configuration.GetSection("Moonlight:Cache").Bind(cacheOptions);
|
||||
|
||||
builder.Services.AddMemoryCache();
|
||||
builder.Services.AddHybridCache();
|
||||
|
||||
if (!cacheOptions.EnableLayer2)
|
||||
return;
|
||||
|
||||
var redisOptions = new RedisOptions();
|
||||
builder.Configuration.GetSection("Moonlight:Redis").Bind(redisOptions);
|
||||
|
||||
if(!redisOptions.Enable)
|
||||
return;
|
||||
|
||||
builder.Services.AddStackExchangeRedisCache(options =>
|
||||
{
|
||||
options.Configuration = redisOptions.ConnectionString;
|
||||
options.InstanceName = "Moonlight:";
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@ public partial class Startup : IAppStartup
|
||||
AddBase(builder);
|
||||
AddAuth(builder);
|
||||
AddDatabase(builder);
|
||||
AddCache(builder);
|
||||
}
|
||||
|
||||
public void PostBuild(WebApplication application)
|
||||
|
||||
Reference in New Issue
Block a user