Improved plugin loading and handling #17

Merged
ChiaraBm merged 2 commits from feat/ImprovePluginLoading into v2.1 2026-02-19 07:36:06 +00:00
15 changed files with 165 additions and 70 deletions

View File

@@ -7,9 +7,6 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="10.0.1"/>
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.1"> <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.1">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; analyzers; buildtransitive</IncludeAssets>

View File

@@ -1,27 +1,9 @@
using Moonlight.Api.Startup; using Moonlight.Api;
using SimplePlugin.Generated; using SimplePlugin.Generated;
var modules = PluginRegistry.Modules var plugins = PluginRegistry
.OfType<IAppStartup>() .Modules
.OfType<MoonlightPlugin>()
.ToArray(); .ToArray();
var builder = WebApplication.CreateBuilder(args); await StartupHandler.RunAsync(args, plugins);
foreach (var startup in modules)
startup.PreBuild(builder);
var app = builder.Build();
foreach (var startup in modules)
startup.PostBuild(app);
foreach (var startup in modules)
startup.PostMiddleware(app);
if (app.Environment.IsDevelopment())
app.UseWebAssemblyDebugging();
app.UseBlazorFrameworkFiles();
app.UseStaticFiles();
await app.RunAsync();

View File

@@ -1,20 +1,9 @@
using Microsoft.AspNetCore.Components.WebAssembly.Hosting; using Moonlight.Frontend;
using Moonlight.Frontend.Startup;
using SimplePlugin.Generated; using SimplePlugin.Generated;
var modules = PluginRegistry var plugins = PluginRegistry
.Modules .Modules
.OfType<IAppStartup>() .OfType<MoonlightPlugin>()
.ToArray(); .ToArray();
var builder = WebAssemblyHostBuilder.CreateDefault(args); await StartupHandler.RunAsync(args, plugins);
foreach (var startup in modules)
startup.PreBuild(builder);
var app = builder.Build();
foreach(var startup in modules)
startup.PostBuild(app);
await app.RunAsync();

View File

@@ -25,6 +25,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="10.0.1"/> <PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="10.0.1"/>
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="10.0.1"/>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="10.0.1"/> <PackageReference Include="Microsoft.EntityFrameworkCore" Version="10.0.1"/>
<PackageReference Include="Microsoft.Extensions.Caching.Hybrid" Version="10.3.0" /> <PackageReference Include="Microsoft.Extensions.Caching.Hybrid" Version="10.3.0" />
<PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="10.0.3" /> <PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="10.0.3" />

View File

@@ -0,0 +1,28 @@
using System.Reflection;
using System.Text.Json.Serialization;
using Microsoft.AspNetCore.Builder;
using SimplePlugin.Abstractions;
namespace Moonlight.Api;
public abstract class MoonlightPlugin : IPluginModule
{
protected MoonlightPlugin[] Plugins { get; private set; }
public void Initialize(MoonlightPlugin[] plugins)
{
Plugins = plugins;
}
public virtual void PreBuild(WebApplicationBuilder builder)
{
}
public virtual void PostBuild(WebApplication application)
{
}
public virtual void PostMiddleware(WebApplication application)
{
}
}

View File

@@ -1,11 +0,0 @@
using Microsoft.AspNetCore.Builder;
using SimplePlugin.Abstractions;
namespace Moonlight.Api.Startup;
public interface IAppStartup : IPluginModule
{
public void PreBuild(WebApplicationBuilder builder);
public void PostBuild(WebApplication application);
public void PostMiddleware(WebApplication application);
}

View File

@@ -16,16 +16,19 @@ namespace Moonlight.Api.Startup;
public partial class Startup public partial class Startup
{ {
private static void AddBase(WebApplicationBuilder builder) private void AddBase(WebApplicationBuilder builder)
{ {
// Create the base directory // Create the base directory
Directory.CreateDirectory("storage"); Directory.CreateDirectory("storage");
// Hook up source-generated serialization // Hook up source-generated serialization and add controllers
builder.Services.AddControllers().AddJsonOptions(options => builder.Services
{ .AddControllers()
options.JsonSerializerOptions.TypeInfoResolverChain.Add(SerializationContext.Default); .AddApplicationPart(typeof(Startup).Assembly)
}); .AddJsonOptions(options =>
{
options.JsonSerializerOptions.TypeInfoResolverChain.Add(SerializationContext.Default);
});
// Configure logging // Configure logging
builder.Logging.ClearProviders(); builder.Logging.ClearProviders();

View File

@@ -1,12 +1,15 @@
using Microsoft.AspNetCore.Builder; using System.Reflection;
using System.Text.Json.Serialization;
using Microsoft.AspNetCore.Builder;
using Moonlight.Shared.Http;
using SimplePlugin.Abstractions; using SimplePlugin.Abstractions;
namespace Moonlight.Api.Startup; namespace Moonlight.Api.Startup;
[PluginModule] [PluginModule]
public partial class Startup : IAppStartup public partial class Startup : MoonlightPlugin
{ {
public void PreBuild(WebApplicationBuilder builder) public override void PreBuild(WebApplicationBuilder builder)
{ {
AddBase(builder); AddBase(builder);
AddAuth(builder); AddAuth(builder);
@@ -14,13 +17,13 @@ public partial class Startup : IAppStartup
AddCache(builder); AddCache(builder);
} }
public void PostBuild(WebApplication application) public override void PostBuild(WebApplication application)
{ {
UseBase(application); UseBase(application);
UseAuth(application); UseAuth(application);
} }
public void PostMiddleware(WebApplication application) public override void PostMiddleware(WebApplication application)
{ {
MapBase(application); MapBase(application);
} }

View File

@@ -0,0 +1,42 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Hosting;
namespace Moonlight.Api;
public static class StartupHandler
{
public static async Task RunAsync(string[] args, MoonlightPlugin[] plugins)
{
Console.WriteLine($"Starting with: {string.Join(", ", plugins.Select(x => x.GetType().FullName))}");
var builder = WebApplication.CreateBuilder(args);
// Setting up context
foreach (var plugin in plugins)
plugin.Initialize(plugins);
// Stage 1: Pre Build
foreach (var startup in plugins)
startup.PreBuild(builder);
var app = builder.Build();
// Stage 2: Post Build
foreach (var startup in plugins)
startup.PostBuild(app);
// Stage 3: Post Middleware
foreach (var startup in plugins)
startup.PostMiddleware(app);
// Frontend debugging
if (app.Environment.IsDevelopment())
app.UseWebAssemblyDebugging();
// Frontend hosting
app.UseBlazorFrameworkFiles();
app.UseStaticFiles();
await app.RunAsync();
}
}

View File

@@ -0,0 +1,8 @@
using System.Reflection;
namespace Moonlight.Frontend.Configuration;
public class NavigationAssemblyOptions
{
public List<Assembly> Assemblies { get; private set; } = new();
}

View File

@@ -0,0 +1,10 @@
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using SimplePlugin.Abstractions;
namespace Moonlight.Frontend;
public abstract class MoonlightPlugin : IPluginModule
{
public virtual void PreBuild(WebAssemblyHostBuilder builder){}
public virtual void PostBuild(WebAssemblyHost application){}
}

View File

@@ -1,6 +1,7 @@
using Microsoft.AspNetCore.Components.Web; using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting; using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Moonlight.Frontend.Configuration;
using Moonlight.Frontend.Implementations; using Moonlight.Frontend.Implementations;
using Moonlight.Frontend.Interfaces; using Moonlight.Frontend.Interfaces;
using Moonlight.Frontend.Services; using Moonlight.Frontend.Services;
@@ -25,5 +26,10 @@ public partial class Startup
builder.Services.AddSingleton<ISidebarProvider, SidebarProvider>(); builder.Services.AddSingleton<ISidebarProvider, SidebarProvider>();
builder.Services.AddScoped<FrontendService>(); builder.Services.AddScoped<FrontendService>();
builder.Services.Configure<NavigationAssemblyOptions>(options =>
{
options.Assemblies.Add(typeof(Startup).Assembly);
});
} }
} }

View File

@@ -4,15 +4,15 @@ using SimplePlugin.Abstractions;
namespace Moonlight.Frontend.Startup; namespace Moonlight.Frontend.Startup;
[PluginModule] [PluginModule]
public partial class Startup : IAppStartup public partial class Startup : MoonlightPlugin
{ {
public void PreBuild(WebAssemblyHostBuilder builder) public override void PreBuild(WebAssemblyHostBuilder builder)
{ {
AddBase(builder); AddBase(builder);
AddAuth(builder); AddAuth(builder);
} }
public void PostBuild(WebAssemblyHost application) public override void PostBuild(WebAssemblyHost application)
{ {
} }

View File

@@ -0,0 +1,23 @@
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
namespace Moonlight.Frontend;
public static class StartupHandler
{
public static async Task RunAsync(string[] args, MoonlightPlugin[] plugins)
{
Console.WriteLine($"Starting with: {string.Join(", ", plugins.Select(x => x.GetType().FullName))}");
var builder = WebAssemblyHostBuilder.CreateDefault(args);
foreach (var plugin in plugins)
plugin.PreBuild(builder);
var app = builder.Build();
foreach(var plugin in plugins)
plugin.PostBuild(app);
await app.RunAsync();
}
}

View File

@@ -1,6 +1,9 @@
@using System.Net @using System.Net
@using System.Reflection
@using LucideBlazor @using LucideBlazor
@using Microsoft.AspNetCore.Components.Authorization @using Microsoft.AspNetCore.Components.Authorization
@using Microsoft.Extensions.Options
@using Moonlight.Frontend.Configuration
@using Moonlight.Frontend.UI.Shared @using Moonlight.Frontend.UI.Shared
@using Moonlight.Frontend.UI.Shared.Components @using Moonlight.Frontend.UI.Shared.Components
@using ShadcnBlazor.Emptys @using ShadcnBlazor.Emptys
@@ -8,12 +11,13 @@
@using Moonlight.Frontend.UI.Shared.Partials @using Moonlight.Frontend.UI.Shared.Partials
@inject NavigationManager Navigation @inject NavigationManager Navigation
@inject IOptions<NavigationAssemblyOptions> NavigationOptions
<ErrorBoundary> <ErrorBoundary>
<ChildContent> <ChildContent>
<AuthorizeView> <AuthorizeView>
<ChildContent> <ChildContent>
<Router AppAssembly="@typeof(App).Assembly" NotFoundPage="typeof(NotFound)"> <Router AppAssembly="@typeof(App).Assembly" AdditionalAssemblies="Assemblies" NotFoundPage="typeof(NotFound)">
<Found Context="routeData"> <Found Context="routeData">
<AuthorizeRouteView RouteData="routeData" DefaultLayout="typeof(MainLayout)"> <AuthorizeRouteView RouteData="routeData" DefaultLayout="typeof(MainLayout)">
<NotAuthorized Context="authRouteViewContext"> <NotAuthorized Context="authRouteViewContext">
@@ -72,3 +76,13 @@
} }
</ErrorContent> </ErrorContent>
</ErrorBoundary> </ErrorBoundary>
@code
{
private Assembly[] Assemblies;
protected override void OnInitialized()
{
Assemblies = NavigationOptions.Value.Assemblies.ToArray();
}
}