From c45e1770012e603d0f6ae8f894bc8c6db66cb301 Mon Sep 17 00:00:00 2001 From: ChiaraBm Date: Wed, 18 Feb 2026 15:36:45 +0100 Subject: [PATCH 1/2] Improved handling of moonlight plugins during startup, minimized host project code and moved startup handling to core --- .../Moonlight.Api.Host.csproj | 3 -- Hosts/Moonlight.Api.Host/Program.cs | 28 +++---------- Hosts/Moonlight.Frontend.Host/Program.cs | 19 ++------- Moonlight.Api/Moonlight.Api.csproj | 1 + Moonlight.Api/MoonlightPlugin.cs | 28 +++++++++++++ Moonlight.Api/Startup/IAppStartup.cs | 11 ----- Moonlight.Api/Startup/Startup.Base.cs | 19 +++++---- Moonlight.Api/Startup/Startup.cs | 13 +++--- Moonlight.Api/StartupHandler.cs | 42 +++++++++++++++++++ Moonlight.Frontend/MoonlightPlugin.cs | 10 +++++ Moonlight.Frontend/Startup/Startup.cs | 6 +-- Moonlight.Frontend/StartupHandler.cs | 23 ++++++++++ 12 files changed, 135 insertions(+), 68 deletions(-) create mode 100644 Moonlight.Api/MoonlightPlugin.cs delete mode 100644 Moonlight.Api/Startup/IAppStartup.cs create mode 100644 Moonlight.Api/StartupHandler.cs create mode 100644 Moonlight.Frontend/MoonlightPlugin.cs create mode 100644 Moonlight.Frontend/StartupHandler.cs diff --git a/Hosts/Moonlight.Api.Host/Moonlight.Api.Host.csproj b/Hosts/Moonlight.Api.Host/Moonlight.Api.Host.csproj index 052019ff..8a17de03 100644 --- a/Hosts/Moonlight.Api.Host/Moonlight.Api.Host.csproj +++ b/Hosts/Moonlight.Api.Host/Moonlight.Api.Host.csproj @@ -7,9 +7,6 @@ - - - all runtime; build; native; analyzers; buildtransitive diff --git a/Hosts/Moonlight.Api.Host/Program.cs b/Hosts/Moonlight.Api.Host/Program.cs index dc76f08c..6a748360 100644 --- a/Hosts/Moonlight.Api.Host/Program.cs +++ b/Hosts/Moonlight.Api.Host/Program.cs @@ -1,27 +1,9 @@ -using Moonlight.Api.Startup; +using Moonlight.Api; using SimplePlugin.Generated; -var modules = PluginRegistry.Modules - .OfType() +var plugins = PluginRegistry + .Modules + .OfType() .ToArray(); -var builder = WebApplication.CreateBuilder(args); - -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(); \ No newline at end of file +await StartupHandler.RunAsync(args, plugins); \ No newline at end of file diff --git a/Hosts/Moonlight.Frontend.Host/Program.cs b/Hosts/Moonlight.Frontend.Host/Program.cs index de4aecc0..eff3e47c 100644 --- a/Hosts/Moonlight.Frontend.Host/Program.cs +++ b/Hosts/Moonlight.Frontend.Host/Program.cs @@ -1,20 +1,9 @@ -using Microsoft.AspNetCore.Components.WebAssembly.Hosting; -using Moonlight.Frontend.Startup; +using Moonlight.Frontend; using SimplePlugin.Generated; -var modules = PluginRegistry +var plugins = PluginRegistry .Modules - .OfType() + .OfType() .ToArray(); -var builder = WebAssemblyHostBuilder.CreateDefault(args); - -foreach (var startup in modules) - startup.PreBuild(builder); - -var app = builder.Build(); - -foreach(var startup in modules) - startup.PostBuild(app); - -await app.RunAsync(); \ No newline at end of file +await StartupHandler.RunAsync(args, plugins); \ No newline at end of file diff --git a/Moonlight.Api/Moonlight.Api.csproj b/Moonlight.Api/Moonlight.Api.csproj index d1125b26..6befadc4 100644 --- a/Moonlight.Api/Moonlight.Api.csproj +++ b/Moonlight.Api/Moonlight.Api.csproj @@ -25,6 +25,7 @@ + diff --git a/Moonlight.Api/MoonlightPlugin.cs b/Moonlight.Api/MoonlightPlugin.cs new file mode 100644 index 00000000..2b5441fd --- /dev/null +++ b/Moonlight.Api/MoonlightPlugin.cs @@ -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) + { + } +} \ No newline at end of file diff --git a/Moonlight.Api/Startup/IAppStartup.cs b/Moonlight.Api/Startup/IAppStartup.cs deleted file mode 100644 index 20fcaf3f..00000000 --- a/Moonlight.Api/Startup/IAppStartup.cs +++ /dev/null @@ -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); -} \ No newline at end of file diff --git a/Moonlight.Api/Startup/Startup.Base.cs b/Moonlight.Api/Startup/Startup.Base.cs index 1c4d1e91..daa0c912 100644 --- a/Moonlight.Api/Startup/Startup.Base.cs +++ b/Moonlight.Api/Startup/Startup.Base.cs @@ -16,16 +16,19 @@ namespace Moonlight.Api.Startup; public partial class Startup { - private static void AddBase(WebApplicationBuilder builder) + private 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); - }); + // Hook up source-generated serialization and add controllers + builder.Services + .AddControllers() + .AddApplicationPart(typeof(Startup).Assembly) + .AddJsonOptions(options => + { + options.JsonSerializerOptions.TypeInfoResolverChain.Add(SerializationContext.Default); + }); // Configure logging builder.Logging.ClearProviders(); @@ -53,10 +56,10 @@ public partial class Startup // Container Helper Options builder.Configuration.GetSection("Moonlight:ContainerHelper").Bind(builder.Configuration); - + builder.Services.AddOptions().BindConfiguration("Moonlight:ContainerHelper"); builder.Services.AddSingleton(); - + builder.Services.AddHttpClient("ContainerHelper", (provider, client) => { var options = provider.GetRequiredService>(); diff --git a/Moonlight.Api/Startup/Startup.cs b/Moonlight.Api/Startup/Startup.cs index b785d7df..6844ebcb 100644 --- a/Moonlight.Api/Startup/Startup.cs +++ b/Moonlight.Api/Startup/Startup.cs @@ -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; namespace Moonlight.Api.Startup; [PluginModule] -public partial class Startup : IAppStartup +public partial class Startup : MoonlightPlugin { - public void PreBuild(WebApplicationBuilder builder) + public override void PreBuild(WebApplicationBuilder builder) { AddBase(builder); AddAuth(builder); @@ -14,13 +17,13 @@ public partial class Startup : IAppStartup AddCache(builder); } - public void PostBuild(WebApplication application) + public override void PostBuild(WebApplication application) { UseBase(application); UseAuth(application); } - public void PostMiddleware(WebApplication application) + public override void PostMiddleware(WebApplication application) { MapBase(application); } diff --git a/Moonlight.Api/StartupHandler.cs b/Moonlight.Api/StartupHandler.cs new file mode 100644 index 00000000..3495e373 --- /dev/null +++ b/Moonlight.Api/StartupHandler.cs @@ -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(); + } +} \ No newline at end of file diff --git a/Moonlight.Frontend/MoonlightPlugin.cs b/Moonlight.Frontend/MoonlightPlugin.cs new file mode 100644 index 00000000..c5ca0592 --- /dev/null +++ b/Moonlight.Frontend/MoonlightPlugin.cs @@ -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){} +} \ No newline at end of file diff --git a/Moonlight.Frontend/Startup/Startup.cs b/Moonlight.Frontend/Startup/Startup.cs index 3a03308a..8c461ca6 100644 --- a/Moonlight.Frontend/Startup/Startup.cs +++ b/Moonlight.Frontend/Startup/Startup.cs @@ -4,15 +4,15 @@ using SimplePlugin.Abstractions; namespace Moonlight.Frontend.Startup; [PluginModule] -public partial class Startup : IAppStartup +public partial class Startup : MoonlightPlugin { - public void PreBuild(WebAssemblyHostBuilder builder) + public override void PreBuild(WebAssemblyHostBuilder builder) { AddBase(builder); AddAuth(builder); } - public void PostBuild(WebAssemblyHost application) + public override void PostBuild(WebAssemblyHost application) { } diff --git a/Moonlight.Frontend/StartupHandler.cs b/Moonlight.Frontend/StartupHandler.cs new file mode 100644 index 00000000..f8522516 --- /dev/null +++ b/Moonlight.Frontend/StartupHandler.cs @@ -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(); + } +} \ No newline at end of file From 0f26aaf803d66a91c49ecfd344a354ccd43e0dea Mon Sep 17 00:00:00 2001 From: ChiaraBm Date: Thu, 19 Feb 2026 08:32:32 +0100 Subject: [PATCH 2/2] Added options for navigation assemblies for the router --- .../Configuration/NavigationAssemblyOptions.cs | 8 ++++++++ Moonlight.Frontend/Startup/Startup.Base.cs | 6 ++++++ Moonlight.Frontend/UI/App.razor | 18 ++++++++++++++++-- 3 files changed, 30 insertions(+), 2 deletions(-) create mode 100644 Moonlight.Frontend/Configuration/NavigationAssemblyOptions.cs diff --git a/Moonlight.Frontend/Configuration/NavigationAssemblyOptions.cs b/Moonlight.Frontend/Configuration/NavigationAssemblyOptions.cs new file mode 100644 index 00000000..bba1104a --- /dev/null +++ b/Moonlight.Frontend/Configuration/NavigationAssemblyOptions.cs @@ -0,0 +1,8 @@ +using System.Reflection; + +namespace Moonlight.Frontend.Configuration; + +public class NavigationAssemblyOptions +{ + public List Assemblies { get; private set; } = new(); +} \ No newline at end of file diff --git a/Moonlight.Frontend/Startup/Startup.Base.cs b/Moonlight.Frontend/Startup/Startup.Base.cs index ffa54244..f490ae57 100644 --- a/Moonlight.Frontend/Startup/Startup.Base.cs +++ b/Moonlight.Frontend/Startup/Startup.Base.cs @@ -1,6 +1,7 @@ using Microsoft.AspNetCore.Components.Web; using Microsoft.AspNetCore.Components.WebAssembly.Hosting; using Microsoft.Extensions.DependencyInjection; +using Moonlight.Frontend.Configuration; using Moonlight.Frontend.Implementations; using Moonlight.Frontend.Interfaces; using Moonlight.Frontend.Services; @@ -25,5 +26,10 @@ public partial class Startup builder.Services.AddSingleton(); builder.Services.AddScoped(); + + builder.Services.Configure(options => + { + options.Assemblies.Add(typeof(Startup).Assembly); + }); } } \ No newline at end of file diff --git a/Moonlight.Frontend/UI/App.razor b/Moonlight.Frontend/UI/App.razor index edadd0bd..c9eb9657 100644 --- a/Moonlight.Frontend/UI/App.razor +++ b/Moonlight.Frontend/UI/App.razor @@ -1,6 +1,9 @@ @using System.Net +@using System.Reflection @using LucideBlazor @using Microsoft.AspNetCore.Components.Authorization +@using Microsoft.Extensions.Options +@using Moonlight.Frontend.Configuration @using Moonlight.Frontend.UI.Shared @using Moonlight.Frontend.UI.Shared.Components @using ShadcnBlazor.Emptys @@ -8,12 +11,13 @@ @using Moonlight.Frontend.UI.Shared.Partials @inject NavigationManager Navigation +@inject IOptions NavigationOptions - + @@ -71,4 +75,14 @@ } - \ No newline at end of file + + +@code +{ + private Assembly[] Assemblies; + + protected override void OnInitialized() + { + Assemblies = NavigationOptions.Value.Assemblies.ToArray(); + } +}