From 072adb5bb1cd95bf6d5de4b4b34e48f9e8dd56ce Mon Sep 17 00:00:00 2001 From: Masu Baumgartner <68913099+Masu-Baumgartner@users.noreply.github.com> Date: Thu, 14 Nov 2024 22:30:02 +0100 Subject: [PATCH] Working on module/plugin system --- .../ClientPlugins/ClientPluginsController.cs | 37 --------- Moonlight.ApiServer/Models/ModuleModel.cs | 12 --- Moonlight.ApiServer/Models/PluginMeta.cs | 12 +++ .../Moonlight.ApiServer.csproj | 1 + Moonlight.ApiServer/Services/ModuleService.cs | 74 ------------------ Moonlight.ApiServer/Services/PluginService.cs | 75 +++++++++++++++++++ Moonlight.ApiServer/Startup.cs | 39 ++-------- 7 files changed, 94 insertions(+), 156 deletions(-) delete mode 100644 Moonlight.ApiServer/Http/Controllers/ClientPlugins/ClientPluginsController.cs delete mode 100644 Moonlight.ApiServer/Models/ModuleModel.cs create mode 100644 Moonlight.ApiServer/Models/PluginMeta.cs delete mode 100644 Moonlight.ApiServer/Services/ModuleService.cs create mode 100644 Moonlight.ApiServer/Services/PluginService.cs diff --git a/Moonlight.ApiServer/Http/Controllers/ClientPlugins/ClientPluginsController.cs b/Moonlight.ApiServer/Http/Controllers/ClientPlugins/ClientPluginsController.cs deleted file mode 100644 index 76223904..00000000 --- a/Moonlight.ApiServer/Http/Controllers/ClientPlugins/ClientPluginsController.cs +++ /dev/null @@ -1,37 +0,0 @@ -using Microsoft.AspNetCore.Mvc; -using Moonlight.ApiServer.Services; -using Moonlight.Shared.Http.Responses.ClientPlugins; - -namespace Moonlight.ApiServer.Http.Controllers.ClientPlugins; - -[ApiController] -[Route("api/clientPlugins")] -public class ClientPluginsController : Controller -{ - private readonly ModuleService ModuleService; - - public ClientPluginsController(ModuleService moduleService) - { - ModuleService = moduleService; - } - - [HttpGet] - public async Task Get() - { - var dlls = ModuleService.Modules - .Where(x => x.Modules.ContainsKey("client")) - .SelectMany(x => - x.Modules - .FirstOrDefault(c => c.Key == "client") - .Value - .Select(y => x.Name + "." + y) - ) - .ToArray(); - - return new() - { - CacheKey = "unset", - Dlls = dlls - }; - } -} \ No newline at end of file diff --git a/Moonlight.ApiServer/Models/ModuleModel.cs b/Moonlight.ApiServer/Models/ModuleModel.cs deleted file mode 100644 index c4e3fde2..00000000 --- a/Moonlight.ApiServer/Models/ModuleModel.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace Moonlight.ApiServer.Models; - -public class ModuleModel -{ - public string Name { get; set; } - public string Author { get; set; } - public string Version { get; set; } - public string? DonateUrl { get; set; } - public string? UpdateUrl { get; set; } - - public Dictionary> Modules { get; set; } = new(); -} \ No newline at end of file diff --git a/Moonlight.ApiServer/Models/PluginMeta.cs b/Moonlight.ApiServer/Models/PluginMeta.cs new file mode 100644 index 00000000..9e371d97 --- /dev/null +++ b/Moonlight.ApiServer/Models/PluginMeta.cs @@ -0,0 +1,12 @@ +namespace Moonlight.ApiServer.Models; + +public class PluginMeta +{ + public string Id { get; set; } + public string Name { get; set; } + public string Author { get; set; } + public string? DonationUrl { get; set; } + public string? UpdateUrl { get; set; } + + public Dictionary Binaries { get; set; } = new(); +} \ No newline at end of file diff --git a/Moonlight.ApiServer/Moonlight.ApiServer.csproj b/Moonlight.ApiServer/Moonlight.ApiServer.csproj index 69caecfb..d3ad7e70 100644 --- a/Moonlight.ApiServer/Moonlight.ApiServer.csproj +++ b/Moonlight.ApiServer/Moonlight.ApiServer.csproj @@ -38,6 +38,7 @@ + diff --git a/Moonlight.ApiServer/Services/ModuleService.cs b/Moonlight.ApiServer/Services/ModuleService.cs deleted file mode 100644 index 4a95a0fa..00000000 --- a/Moonlight.ApiServer/Services/ModuleService.cs +++ /dev/null @@ -1,74 +0,0 @@ -using System.Text.Json; -using MoonCore.Helpers; -using Moonlight.ApiServer.Models; - -namespace Moonlight.ApiServer.Services; - -public class ModuleService -{ - private readonly ILogger Logger; - private readonly Dictionary ModuleMeta = new(); - private static string ModulePath = PathBuilder.Dir("storage", "modules"); - - public ModuleModel[] Modules => ModuleMeta.Values.ToArray(); - - public ModuleService(ILogger logger) - { - Logger = logger; - } - - public void Load() - { - Logger.LogInformation("Loading modules"); - - Directory.CreateDirectory(ModulePath); - - foreach (var moduleDirectory in Directory.EnumerateDirectories(ModulePath)) - { - var metaPath = PathBuilder.File(moduleDirectory, "meta.json"); - - if (!File.Exists(metaPath)) - { - Logger.LogWarning("No meta.json found in {folder}", moduleDirectory); - continue; - } - - ModuleModel moduleModel; - - try - { - var json = File.ReadAllText(metaPath); - moduleModel = JsonSerializer.Deserialize(json)!; - } - catch (Exception e) - { - Logger.LogError("An error occured while loading meta.json in {folder}: {e}", moduleDirectory, e); - continue; - } - - ModuleMeta.Add(moduleDirectory, moduleModel); - } - - Logger.LogInformation("Loaded {count} modules", ModuleMeta.Count); - } - - public string[] GetModuleDlls(string moduleName, string section) - { - if (ModuleMeta.All(x => x.Value.Name != moduleName)) - throw new ArgumentException($"No module with the name '{moduleName}' found"); - - var moduleKvp = ModuleMeta - .FirstOrDefault(x => x.Value.Name == moduleName); - - var module = moduleKvp.Value; - - if (!module.Modules.ContainsKey(section)) - return []; - - var modulePaths = module.Modules[section].Select( - dllName => PathBuilder.File(ModulePath, moduleKvp.Key, "bin", section, dllName) - ).Where(File.Exists).ToArray(); - - return modulePaths; - } -} \ No newline at end of file diff --git a/Moonlight.ApiServer/Services/PluginService.cs b/Moonlight.ApiServer/Services/PluginService.cs new file mode 100644 index 00000000..28216c78 --- /dev/null +++ b/Moonlight.ApiServer/Services/PluginService.cs @@ -0,0 +1,75 @@ +using System.Text.Json; +using MoonCore.Helpers; +using Moonlight.ApiServer.Models; + +namespace Moonlight.ApiServer.Services; + +public class PluginService +{ + private readonly Dictionary Plugins = new(); + private readonly ILogger Logger; + + private static string PluginsPath = PathBuilder.Dir("storage", "plugins"); + + public PluginService(ILogger logger) + { + Logger = logger; + } + + public async Task Load() + { + Logger.LogInformation("Loading plugins..."); + + foreach (var pluginPath in Directory.EnumerateDirectories(PluginsPath)) + { + var metaPath = PathBuilder.File(PluginsPath, "meta.json"); + + if (!File.Exists(metaPath)) + { + Logger.LogWarning("Ignoring folder '{name}'. No meta.json found", pluginPath); + continue; + } + + var metaContent = await File.ReadAllTextAsync(metaPath); + PluginMeta meta; + + try + { + meta = JsonSerializer.Deserialize(metaContent) + ?? throw new JsonException(); + } + catch (Exception e) + { + Logger.LogCritical("Unable to deserialize meta.json: {e}", e); + continue; + } + + Plugins.Add(pluginPath, meta); + } + } + + public Task> GetBinariesBySection(string section) + { + var bins = Plugins + .Values + .Where(x => x.Binaries.ContainsKey(section)) + .ToDictionary(x => x.Id, x => x.Binaries[section]); + + return Task.FromResult(bins); + } + + public Task GetBinaryStream(string plugin, string section, string fileName) + { + if (Plugins.All(x => x.Value.Id != plugin)) + return Task.FromResult(null); + + var pluginData = Plugins.First(x => x.Value.Id == plugin); + var binaryPath = PathBuilder.File(pluginData.Key, section, fileName); + + if (!File.Exists(binaryPath)) + return Task.FromResult(null); + + var fs = File.OpenRead(binaryPath); + return Task.FromResult(fs); + } +} \ No newline at end of file diff --git a/Moonlight.ApiServer/Startup.cs b/Moonlight.ApiServer/Startup.cs index 62e2140a..b4a87a4d 100644 --- a/Moonlight.ApiServer/Startup.cs +++ b/Moonlight.ApiServer/Startup.cs @@ -69,33 +69,7 @@ public static class Startup var startupLogger = startupLoggerFactory.CreateLogger("Startup"); - // Load plugin/modules - var moduleService = new ModuleService(startupLoggerFactory.CreateLogger()); - moduleService.Load(); - - // Load api server module assemblies - var apiServerDlls = moduleService.Modules.SelectMany( - x => moduleService.GetModuleDlls(x.Name, "apiServer") - ); - - var apiServerModuleContext = new AssemblyLoadContext(null); - - foreach (var apiServerDll in apiServerDlls) - { - try - { - apiServerModuleContext.LoadFromStream(File.OpenRead( - apiServerDll - )); - } - catch (Exception e) - { - startupLogger.LogCritical("Unable to load dll {name} into context: {e}", apiServerDll, e); - throw; - } - } - - var moduleAssemblies = apiServerModuleContext.Assemblies.ToArray(); + //TODO: Load plugin // Configure startup interfaces var startupServiceCollection = new ServiceCollection(); @@ -123,7 +97,7 @@ public static class Startup if(additionalAssemblies != null) configuration.AddAssemblies(additionalAssemblies); - configuration.AddAssemblies(moduleAssemblies); + //configuration.AddAssemblies(moduleAssemblies); }); @@ -164,10 +138,9 @@ public static class Startup var controllerBuilder = builder.Services.AddControllers(); // Add current assemblies to the application part - foreach (var moduleAssembly in moduleAssemblies) - controllerBuilder.AddApplicationPart(moduleAssembly); - - builder.Services.AddSingleton(moduleService); + //foreach (var moduleAssembly in moduleAssemblies) + // controllerBuilder.AddApplicationPart(moduleAssembly); + builder.Services.AddSingleton(config); builder.Services.AutoAddServices(typeof(Startup).Assembly); builder.Services.AddHttpClient(); @@ -185,7 +158,7 @@ public static class Startup if(additionalAssemblies != null) configuration.AddAssemblies(additionalAssemblies); - configuration.AddAssemblies(moduleAssemblies); + //configuration.AddAssemblies(moduleAssemblies); }); var app = builder.Build();