Added api documentation/definition system system

This commit is contained in:
Baumgartner Marcel
2024-06-05 13:27:09 +02:00
parent e43d6bff06
commit 2bb3b0fd48
13 changed files with 305 additions and 45 deletions

View File

@@ -0,0 +1,66 @@
.light-mode {
--scalar-background-1: #fff;
--scalar-background-2: #f8fafc;
--scalar-background-3: #e7e7e7;
--scalar-background-accent: #8ab4f81f;
--scalar-color-1: #000;
--scalar-color-2: #6b7280;
--scalar-color-3: #9ca3af;
--scalar-color-accent: #00c16a;
--scalar-border-color: #e5e7eb;
--scalar-color-green: #069061;
--scalar-color-red: #ef4444;
--scalar-color-yellow: #f59e0b;
--scalar-color-blue: #1d4ed8;
--scalar-color-orange: #fb892c;
--scalar-color-purple: #6d28d9;
--scalar-button-1: #000;
--scalar-button-1-hover: rgba(0, 0, 0, 0.9);
--scalar-button-1-color: #fff;
}
.dark-mode {
--scalar-background-1: #020420;
--scalar-background-2: #121a31;
--scalar-background-3: #1e293b;
--scalar-background-accent: #8ab4f81f;
--scalar-color-1: #fff;
--scalar-color-2: #cbd5e1;
--scalar-color-3: #94a3b8;
--scalar-color-accent: #00dc82;
--scalar-border-color: #1e293b;
--scalar-color-green: #069061;
--scalar-color-red: #f87171;
--scalar-color-yellow: #fde68a;
--scalar-color-blue: #60a5fa;
--scalar-color-orange: #fb892c;
--scalar-color-purple: #ddd6fe;
--scalar-button-1: hsla(0, 0%, 100%, 0.9);
--scalar-button-1-hover: hsla(0, 0%, 100%, 0.8);
--scalar-button-1-color: #000;
}
.dark-mode .t-doc__sidebar,
.light-mode .t-doc__sidebar {
--scalar-sidebar-background-1: var(--scalar-background-1);
--scalar-sidebar-color-1: var(--scalar-color-1);
--scalar-sidebar-color-2: var(--scalar-color-3);
--scalar-sidebar-border-color: var(--scalar-border-color);
--scalar-sidebar-item-hover-background: transparent;
--scalar-sidebar-item-hover-color: var(--scalar-color-1);
--scalar-sidebar-item-active-background: transparent;
--scalar-sidebar-color-active: var(--scalar-color-accent);
--scalar-sidebar-search-background: transparent;
--scalar-sidebar-search-color: var(--scalar-color-3);
--scalar-sidebar-search-border-color: var(--scalar-border-color);
--scalar-sidebar-indent-border: var(--scalar-border-color);
--scalar-sidebar-indent-border-hover: var(--scalar-color-1);
--scalar-sidebar-indent-border-active: var(--scalar-color-accent);
}
.scalar-card .request-card-footer {
--scalar-background-3: var(--scalar-background-2);
--scalar-button-1: #0f172a;
--scalar-button-1-hover: rgba(30, 41, 59, 0.5);
--scalar-button-1-color: #fff;
}
.scalar-card .show-api-client-button {
border: 1px solid #334155 !important;
}

View File

@@ -0,0 +1,11 @@
namespace Moonlight.Core.Attributes;
public class ApiDocumentAttribute : Attribute
{
public string Name { get; set; }
public ApiDocumentAttribute(string name)
{
Name = name;
}
}

View File

@@ -17,6 +17,14 @@ public class CoreConfiguration
[JsonProperty("Customisation")] public CustomisationData Customisation { get; set; } = new(); [JsonProperty("Customisation")] public CustomisationData Customisation { get; set; } = new();
[JsonProperty("Security")] public SecurityData Security { get; set; } = new(); [JsonProperty("Security")] public SecurityData Security { get; set; } = new();
[JsonProperty("Development")] public DevelopmentData Development { get; set; } = new();
public class DevelopmentData
{
[JsonProperty("EnableApiReference")]
[Description("This enables the api reference at your-moonlight.domain/admin/api/reference. Changing this requires a restart")]
public bool EnableApiReference { get; set; } = false;
}
public class HttpData public class HttpData
{ {

View File

@@ -20,7 +20,10 @@ using Moonlight.Core.Models.Abstractions.Feature;
using Moonlight.Core.Models.Enums; using Moonlight.Core.Models.Enums;
using Moonlight.Core.Repositories; using Moonlight.Core.Repositories;
using Moonlight.Core.Services; using Moonlight.Core.Services;
using Moonlight.Core.UI.Components.Cards; using Microsoft.OpenApi.Models;
using Moonlight.Core.Attributes;
using Moonlight.Core.Implementations.ApiDefinition;
using Swashbuckle.AspNetCore.SwaggerGen;
namespace Moonlight.Core; namespace Moonlight.Core;
@@ -32,7 +35,7 @@ public class CoreFeature : MoonlightFeature
Author = "MasuOwO and contributors"; Author = "MasuOwO and contributors";
IssueTracker = "https://github.com/Moonlight-Panel/Moonlight/issues"; IssueTracker = "https://github.com/Moonlight-Panel/Moonlight/issues";
} }
public override Task OnPreInitialized(PreInitContext context) public override Task OnPreInitialized(PreInitContext context)
{ {
// Load configuration // Load configuration
@@ -41,17 +44,17 @@ public class CoreFeature : MoonlightFeature
); );
var config = configService.Get(); var config = configService.Get();
// Services // Services
context.EnableDependencyInjection<CoreFeature>(); context.EnableDependencyInjection<CoreFeature>();
var builder = context.Builder; var builder = context.Builder;
builder.Services.AddDbContext<DataContext>(); builder.Services.AddDbContext<DataContext>();
// //
builder.Services.AddSingleton(new JwtService<CoreJwtType>(config.Security.Token)); builder.Services.AddSingleton(new JwtService<CoreJwtType>(config.Security.Token));
// Mooncore services // Mooncore services
builder.Services.AddScoped(typeof(Repository<>), typeof(GenericRepository<>)); builder.Services.AddScoped(typeof(Repository<>), typeof(GenericRepository<>));
builder.Services.AddScoped<CookieService>(); builder.Services.AddScoped<CookieService>();
@@ -60,7 +63,7 @@ public class CoreFeature : MoonlightFeature
builder.Services.AddScoped<ToastService>(); builder.Services.AddScoped<ToastService>();
builder.Services.AddScoped<ClipboardService>(); builder.Services.AddScoped<ClipboardService>();
builder.Services.AddScoped<ModalService>(); builder.Services.AddScoped<ModalService>();
builder.Services.AddMoonCoreUi(configuration => builder.Services.AddMoonCoreUi(configuration =>
{ {
configuration.ToastJavascriptPrefix = "moonlight.toasts"; configuration.ToastJavascriptPrefix = "moonlight.toasts";
@@ -69,13 +72,13 @@ public class CoreFeature : MoonlightFeature
configuration.ClipboardJavascriptPrefix = "moonlight.clipboard"; configuration.ClipboardJavascriptPrefix = "moonlight.clipboard";
configuration.FileDownloadJavascriptPrefix = "moonlight.utils"; configuration.FileDownloadJavascriptPrefix = "moonlight.utils";
}); });
// Add external services and blazor/asp.net stuff // Add external services and blazor/asp.net stuff
builder.Services.AddRazorPages(); builder.Services.AddRazorPages();
builder.Services.AddHttpContextAccessor(); builder.Services.AddHttpContextAccessor();
builder.Services.AddControllers(); builder.Services.AddControllers();
builder.Services.AddBlazorTable(); builder.Services.AddBlazorTable();
// Configure blazor pipeline in detail // Configure blazor pipeline in detail
builder.Services.AddServerSideBlazor().AddHubOptions(options => builder.Services.AddServerSideBlazor().AddHubOptions(options =>
{ {
@@ -85,15 +88,15 @@ public class CoreFeature : MoonlightFeature
// Setup authentication if required // Setup authentication if required
if (config.Authentication.UseDefaultAuthentication) if (config.Authentication.UseDefaultAuthentication)
builder.Services.AddScoped<IAuthenticationProvider, DefaultAuthenticationProvider>(); builder.Services.AddScoped<IAuthenticationProvider, DefaultAuthenticationProvider>();
// Setup http upload limit // Setup http upload limit
context.Builder.WebHost.ConfigureKestrel(options => context.Builder.WebHost.ConfigureKestrel(options =>
{ {
options.Limits.MaxRequestBodySize = ByteSizeValue.FromMegaBytes(config.Http.UploadLimit).Bytes; options.Limits.MaxRequestBodySize = ByteSizeValue.FromMegaBytes(config.Http.UploadLimit).Bytes;
}); });
// Assets // Assets
// - Javascript // - Javascript
context.AddAsset("Core", "js/bootstrap.js"); context.AddAsset("Core", "js/bootstrap.js");
context.AddAsset("Core", "js/moonlight.js"); context.AddAsset("Core", "js/moonlight.js");
@@ -101,24 +104,58 @@ public class CoreFeature : MoonlightFeature
context.AddAsset("Core", "js/toaster.js"); context.AddAsset("Core", "js/toaster.js");
context.AddAsset("Core", "js/sidebar.js"); context.AddAsset("Core", "js/sidebar.js");
context.AddAsset("Core", "js/alerter.js"); context.AddAsset("Core", "js/alerter.js");
// - Css // - Css
context.AddAsset("Core", "css/blazor.css"); context.AddAsset("Core", "css/blazor.css");
context.AddAsset("Core", "css/boxicons.css"); context.AddAsset("Core", "css/boxicons.css");
context.AddAsset("Core", "css/sweetalert2dark.css"); context.AddAsset("Core", "css/sweetalert2dark.css");
context.AddAsset("Core", "css/utils.css"); context.AddAsset("Core", "css/utils.css");
// Api
if (config.Development.EnableApiReference)
{
builder.Services.AddSwaggerGen(async options =>
{
foreach (var definition in await context.Plugins.GetImplementations<IApiDefinition>())
{
options.SwaggerDoc(
definition.GetId(),
new OpenApiInfo()
{
Title = definition.GetName(),
Version = definition.GetVersion()
}
);
}
options.SwaggerGeneratorOptions.DocInclusionPredicate = (document, description) =>
{
foreach (var attribute in description.CustomAttributes())
{
if (attribute is ApiDocumentAttribute documentAttribute)
return document == documentAttribute.Name;
}
return false;
};
});
}
return Task.CompletedTask; return Task.CompletedTask;
} }
public override async Task OnInitialized(InitContext context) public override async Task OnInitialized(InitContext context)
{ {
var app = context.Application; var app = context.Application;
// Config
var configService = app.Services.GetRequiredService<ConfigService<CoreConfiguration>>();
var config = configService.Get();
// Allow MoonlightService to access the app // Allow MoonlightService to access the app
var moonlightService = app.Services.GetRequiredService<MoonlightService>(); var moonlightService = app.Services.GetRequiredService<MoonlightService>();
moonlightService.Application = app; moonlightService.Application = app;
// Define permissions // Define permissions
var permissionService = app.Services.GetRequiredService<PermissionService>(); var permissionService = app.Services.GetRequiredService<PermissionService>();
@@ -127,25 +164,31 @@ public class CoreFeature : MoonlightFeature
Name = "See Admin Page", Name = "See Admin Page",
Description = "Allows access to the admin page and the connected stats (server and user count)" Description = "Allows access to the admin page and the connected stats (server and user count)"
}); });
await permissionService.Register(1000, new() await permissionService.Register(1000, new()
{ {
Name = "Manage users", Name = "Manage users",
Description = "Allows access to users and their sessions" Description = "Allows access to users and their sessions"
}); });
await permissionService.Register(9000, new() await permissionService.Register(9000, new()
{ {
Name = "View exceptions", Name = "View exceptions",
Description = "Allows to see the raw message of exceptions when thrown in a view" Description = "Allows to see the raw message of exceptions when thrown in a view"
}); });
await permissionService.Register(9998, new()
{
Name = "Manage admin api access",
Description = "Allows access to manage api keys and their permissions"
});
await permissionService.Register(9999, new() await permissionService.Register(9999, new()
{ {
Name = "Manage system", Name = "Manage system",
Description = "Allows access to the core system if moonlight and all configuration files" Description = "Allows access to the core system if moonlight and all configuration files"
}); });
// //
app.UseStaticFiles(); app.UseStaticFiles();
app.UseRouting(); app.UseRouting();
@@ -153,7 +196,7 @@ public class CoreFeature : MoonlightFeature
app.MapFallbackToPage("/_Host"); app.MapFallbackToPage("/_Host");
app.MapControllers(); app.MapControllers();
app.UseWebSockets(); app.UseWebSockets();
// Plugins // Plugins
var pluginService = app.Services.GetRequiredService<PluginService>(); var pluginService = app.Services.GetRequiredService<PluginService>();
@@ -162,26 +205,26 @@ public class CoreFeature : MoonlightFeature
await pluginService.RegisterImplementation<IDiagnoseAction>(new PluginsDiagnoseAction()); await pluginService.RegisterImplementation<IDiagnoseAction>(new PluginsDiagnoseAction());
await pluginService.RegisterImplementation<IDiagnoseAction>(new FeatureDiagnoseAction()); await pluginService.RegisterImplementation<IDiagnoseAction>(new FeatureDiagnoseAction());
await pluginService.RegisterImplementation<IDiagnoseAction>(new LogDiagnoseAction()); await pluginService.RegisterImplementation<IDiagnoseAction>(new LogDiagnoseAction());
// UI // UI
await pluginService.RegisterImplementation<IAdminDashboardColumn>(new UserCount()); await pluginService.RegisterImplementation<IAdminDashboardColumn>(new UserCount());
await pluginService.RegisterImplementation<IUserDashboardComponent>(new GreetingMessages()); await pluginService.RegisterImplementation<IUserDashboardComponent>(new GreetingMessages());
// Startup job services // Startup job services
var startupJobService = app.Services.GetRequiredService<StartupJobService>(); var startupJobService = app.Services.GetRequiredService<StartupJobService>();
await startupJobService.AddJob("Default user creation", TimeSpan.FromSeconds(3), async provider => await startupJobService.AddJob("Default user creation", TimeSpan.FromSeconds(3), async provider =>
{ {
using var scope = provider.CreateScope(); using var scope = provider.CreateScope();
var configService = scope.ServiceProvider.GetRequiredService<ConfigService<CoreConfiguration>>(); var configService = scope.ServiceProvider.GetRequiredService<ConfigService<CoreConfiguration>>();
var userRepo = scope.ServiceProvider.GetRequiredService<Repository<User>>(); var userRepo = scope.ServiceProvider.GetRequiredService<Repository<User>>();
var authenticationProvider = scope.ServiceProvider.GetRequiredService<IAuthenticationProvider>(); var authenticationProvider = scope.ServiceProvider.GetRequiredService<IAuthenticationProvider>();
if(!configService.Get().Authentication.UseDefaultAuthentication) if (!configService.Get().Authentication.UseDefaultAuthentication)
return; return;
if(userRepo.Get().Any()) if (userRepo.Get().Any())
return; return;
// Define credentials // Define credentials
@@ -202,43 +245,52 @@ public class CoreFeature : MoonlightFeature
var user = userRepo.Get().First(x => x.Username == username); var user = userRepo.Get().First(x => x.Username == username);
user.Permissions = 9999; user.Permissions = 9999;
userRepo.Update(user); userRepo.Update(user);
Logger.Info($"Default login: Email: '{email}' Password: '{password}'"); Logger.Info($"Default login: Email: '{email}' Password: '{password}'");
}); });
// Api
if (config.Development.EnableApiReference)
{
app.MapSwagger("/api/core/reference/openapi/{documentName}");
await pluginService.RegisterImplementation<IApiDefinition>(new InternalApiDefinition());
}
} }
public override Task OnUiInitialized(UiInitContext context) public override Task OnUiInitialized(UiInitContext context)
{ {
context.EnablePages<CoreFeature>(); context.EnablePages<CoreFeature>();
// User pages // User pages
context.AddSidebarItem("Dashboard", "bxs-dashboard", "/", needsExactMatch: true, index: int.MinValue); context.AddSidebarItem("Dashboard", "bxs-dashboard", "/", needsExactMatch: true, index: int.MinValue);
// Admin pages // Admin pages
context.AddSidebarItem("Dashboard", "bxs-dashboard", "/admin", needsExactMatch: true, isAdmin: true, index: int.MinValue); context.AddSidebarItem("Dashboard", "bxs-dashboard", "/admin", needsExactMatch: true, isAdmin: true,
index: int.MinValue);
context.AddSidebarItem("Users", "bxs-group", "/admin/users", needsExactMatch: false, isAdmin: true); context.AddSidebarItem("Users", "bxs-group", "/admin/users", needsExactMatch: false, isAdmin: true);
context.AddSidebarItem("System", "bxs-component", "/admin/sys", needsExactMatch: false, isAdmin: true); context.AddSidebarItem("System", "bxs-component", "/admin/sys", needsExactMatch: false, isAdmin: true);
return Task.CompletedTask; return Task.CompletedTask;
} }
public override async Task OnSessionInitialized(SessionInitContext context) public override async Task OnSessionInitialized(SessionInitContext context)
{ {
var lazyLoader = context.LazyLoader; var lazyLoader = context.LazyLoader;
// - Authentication // - Authentication
var cookieService = context.ServiceProvider.GetRequiredService<CookieService>(); var cookieService = context.ServiceProvider.GetRequiredService<CookieService>();
var identityService = context.ServiceProvider.GetRequiredService<IdentityService>(); var identityService = context.ServiceProvider.GetRequiredService<IdentityService>();
await lazyLoader.SetText("Authenticating"); await lazyLoader.SetText("Authenticating");
var token = await cookieService.GetValue("token"); var token = await cookieService.GetValue("token");
await identityService.Authenticate(token); await identityService.Authenticate(token);
// - Session // - Session
await lazyLoader.SetText("Starting session"); await lazyLoader.SetText("Starting session");
var scopedStorageService = context.ServiceProvider.GetRequiredService<ScopedStorageService>(); var scopedStorageService = context.ServiceProvider.GetRequiredService<ScopedStorageService>();
var sessionService = context.ServiceProvider.GetRequiredService<SessionService>(); var sessionService = context.ServiceProvider.GetRequiredService<SessionService>();
var navigationManager = context.ServiceProvider.GetRequiredService<NavigationManager>(); var navigationManager = context.ServiceProvider.GetRequiredService<NavigationManager>();
var alertService = context.ServiceProvider.GetRequiredService<AlertService>(); var alertService = context.ServiceProvider.GetRequiredService<AlertService>();
@@ -253,15 +305,12 @@ public class CoreFeature : MoonlightFeature
}; };
// Setup updating // Setup updating
navigationManager.LocationChanged += (_, _) => navigationManager.LocationChanged += (_, _) => { session.UpdatedAt = DateTime.UtcNow; };
{
session.UpdatedAt = DateTime.UtcNow;
};
// Save session and session service to view storage // Save session and session service to view storage
scopedStorageService.Set("Session", session); scopedStorageService.Set("Session", session);
scopedStorageService.Set("SessionService", sessionService); scopedStorageService.Set("SessionService", sessionService);
// Register session // Register session
await sessionService.Add(session); await sessionService.Add(session);
} }

View File

@@ -0,0 +1,53 @@
using Microsoft.AspNetCore.Mvc;
using MoonCore.Services;
using Moonlight.Core.Attributes;
using Moonlight.Core.Configuration;
using Moonlight.Core.Models;
using Newtonsoft.Json;
namespace Moonlight.Core.Http.Controllers;
[ApiController]
[ApiDocument("internal")]
[Route("/api/core/reference")]
public class ApiReferenceController : Controller
{
private readonly ConfigService<CoreConfiguration> ConfigService;
public ApiReferenceController(ConfigService<CoreConfiguration> configService)
{
ConfigService = configService;
}
[HttpGet]
public async Task<ActionResult> Get([FromQuery] string document)
{
if (!ConfigService.Get().Development.EnableApiReference)
return BadRequest("Api reference is disabled");
var options = new ScalarOptions();
var optionsJson = JsonConvert.SerializeObject(options, Formatting.Indented);
var html = "<!doctype html>\n" +
"<html>\n" +
"<head>\n" +
"<title>Moonlight Api Reference</title>\n" +
"<meta charset=\"utf-8\" />\n" +
"<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n" +
"</head>\n" +
"<body>\n" +
$"<script id=\"api-reference\" data-url=\"/api/core/reference/openapi/{document}\"></script>\n" +
"<script>\n" +
"var configuration =\n" +
$"{optionsJson}\n" +
"\n" +
"document.getElementById('api-reference').dataset.configuration =\n" +
"JSON.stringify(configuration)\n" +
"</script>\n" +
"<script src=\"https://cdn.jsdelivr.net/npm/@scalar/api-reference\"></script>\n" +
"</body>\n" +
"</html>";
return Content(html, "text/html");
}
}

View File

@@ -1,9 +1,11 @@
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using MoonCore.Helpers; using MoonCore.Helpers;
using Moonlight.Core.Attributes;
namespace Moonlight.Core.Http.Controllers; namespace Moonlight.Core.Http.Controllers;
[ApiController] [ApiController]
[ApiDocument("internal")]
[Route("api/core/asset")] [Route("api/core/asset")]
public class AssetController : Controller public class AssetController : Controller
{ {

View File

@@ -4,6 +4,7 @@ using Microsoft.AspNetCore.Mvc;
using MoonCore.Abstractions; using MoonCore.Abstractions;
using MoonCore.Helpers; using MoonCore.Helpers;
using MoonCore.Services; using MoonCore.Services;
using Moonlight.Core.Attributes;
using Moonlight.Core.Configuration; using Moonlight.Core.Configuration;
using Moonlight.Core.Database.Entities; using Moonlight.Core.Database.Entities;
using Moonlight.Core.Services; using Moonlight.Core.Services;
@@ -11,6 +12,7 @@ using Moonlight.Core.Services;
namespace Moonlight.Core.Http.Controllers; namespace Moonlight.Core.Http.Controllers;
[ApiController] [ApiController]
[ApiDocument("internal")]
[Route("api/core/avatar")] [Route("api/core/avatar")]
public class AvatarController : Controller public class AvatarController : Controller
{ {

View File

@@ -0,0 +1,14 @@
using Moonlight.Core.Interfaces;
namespace Moonlight.Core.Implementations.ApiDefinition;
public class InternalApiDefinition : IApiDefinition
{
public string GetId() => "internal";
public string GetName() => "Internal API";
public string GetVersion() => "v2";
public string[] GetPermissions() => [];
}

View File

@@ -0,0 +1,9 @@
namespace Moonlight.Core.Interfaces;
public interface IApiDefinition
{
public string GetId();
public string GetName();
public string GetVersion();
public string[] GetPermissions();
}

View File

@@ -1,4 +1,5 @@
using System.Reflection; using System.Reflection;
using Moonlight.Core.Services;
namespace Moonlight.Core.Models.Abstractions.Feature; namespace Moonlight.Core.Models.Abstractions.Feature;
@@ -7,6 +8,7 @@ public class PreInitContext
public WebApplicationBuilder Builder { get; set; } public WebApplicationBuilder Builder { get; set; }
public List<Assembly> DiAssemblies { get; set; } = new(); public List<Assembly> DiAssemblies { get; set; } = new();
public Dictionary<string, List<string>> Assets { get; set; } = new(); public Dictionary<string, List<string>> Assets { get; set; } = new();
public PluginService Plugins { get; set; }
public void EnableDependencyInjection<T>() public void EnableDependencyInjection<T>()
{ {

View File

@@ -0,0 +1,43 @@
namespace Moonlight.Core.Models;
// From https://github.com/scalar/scalar/blob/main/packages/scalar.aspnetcore/ScalarOptions.cs
public class ScalarOptions
{
public string Theme { get; set; } = "purple";
public bool? DarkMode { get; set; }
public bool? HideDownloadButton { get; set; }
public bool? ShowSideBar { get; set; }
public bool? WithDefaultFonts { get; set; }
public string? Layout { get; set; }
public string? CustomCss { get; set; }
public string? SearchHotkey { get; set; }
public Dictionary<string, string>? Metadata { get; set; }
public ScalarAuthenticationOptions? Authentication { get; set; }
}
public class ScalarAuthenticationOptions
{
public string? PreferredSecurityScheme { get; set; }
public ScalarAuthenticationApiKey? ApiKey { get; set; }
}
public class ScalarAuthenticationoAuth2
{
public string? ClientId { get; set; }
public List<string>? Scopes { get; set; }
}
public class ScalarAuthenticationApiKey
{
public string? Token { get; set; }
}

View File

@@ -55,11 +55,12 @@ public class FeatureService
return Task.CompletedTask; return Task.CompletedTask;
} }
public async Task PreInit(WebApplicationBuilder builder) public async Task PreInit(WebApplicationBuilder builder, PluginService pluginService)
{ {
Logger.Info("Pre-initializing features"); Logger.Info("Pre-initializing features");
PreInitContext.Builder = builder; PreInitContext.Builder = builder;
PreInitContext.Plugins = pluginService;
foreach (var feature in Features) foreach (var feature in Features)
{ {

View File

@@ -105,7 +105,7 @@ builder.Services.AddSingleton(configService);
builder.Services.AddSingleton(pluginService); builder.Services.AddSingleton(pluginService);
// Feature hook // Feature hook
await featureService.PreInit(builder); await featureService.PreInit(builder, pluginService);
// Plugin hook // Plugin hook
await pluginService.PreInitialize(builder); await pluginService.PreInitialize(builder);