Merge branch 'v2_ChangeArchitecture' into v2_ChangeArchitecture_AddDiagnose
This commit is contained in:
@@ -6,6 +6,7 @@ using MoonCore.Attributes;
|
||||
using MoonCore.Exceptions;
|
||||
using MoonCore.Helpers;
|
||||
using Moonlight.ApiServer.Configuration;
|
||||
using Moonlight.ApiServer.Models;
|
||||
using Moonlight.Shared.Misc;
|
||||
|
||||
namespace Moonlight.ApiServer.Services;
|
||||
@@ -14,18 +15,18 @@ namespace Moonlight.ApiServer.Services;
|
||||
public class FrontendService
|
||||
{
|
||||
private readonly AppConfiguration Configuration;
|
||||
private readonly PluginService PluginService;
|
||||
private readonly IWebHostEnvironment WebHostEnvironment;
|
||||
private readonly IEnumerable<FrontendConfigurationOption> ConfigurationOptions;
|
||||
|
||||
public FrontendService(
|
||||
AppConfiguration configuration,
|
||||
PluginService pluginService,
|
||||
IWebHostEnvironment webHostEnvironment
|
||||
IWebHostEnvironment webHostEnvironment,
|
||||
IEnumerable<FrontendConfigurationOption> configurationOptions
|
||||
)
|
||||
{
|
||||
Configuration = configuration;
|
||||
PluginService = pluginService;
|
||||
WebHostEnvironment = webHostEnvironment;
|
||||
ConfigurationOptions = configurationOptions;
|
||||
}
|
||||
|
||||
public async Task<FrontendConfiguration> GetConfiguration()
|
||||
@@ -48,33 +49,15 @@ public class FrontendService
|
||||
.Deserialize<Dictionary<string, string>>(variablesJson) ?? new();
|
||||
}
|
||||
|
||||
// Collect assemblies for the 'client' section
|
||||
configuration.Assemblies = PluginService
|
||||
.GetAssemblies("client")
|
||||
.Keys
|
||||
.ToArray();
|
||||
|
||||
// Collect scripts to execute
|
||||
configuration.Scripts = PluginService
|
||||
.LoadedPlugins
|
||||
.Keys
|
||||
configuration.Scripts = ConfigurationOptions
|
||||
.SelectMany(x => x.Scripts)
|
||||
.ToArray();
|
||||
|
||||
// Collect styles
|
||||
var styles = new List<string>();
|
||||
|
||||
styles.AddRange(
|
||||
PluginService
|
||||
.LoadedPlugins
|
||||
.Keys
|
||||
.SelectMany(x => x.Styles)
|
||||
);
|
||||
|
||||
// Add bundle css
|
||||
styles.Add("css/bundle.min.css");
|
||||
|
||||
configuration.Styles = styles.ToArray();
|
||||
configuration.Styles = ConfigurationOptions
|
||||
.SelectMany(x => x.Styles)
|
||||
.ToArray();
|
||||
|
||||
return configuration;
|
||||
}
|
||||
@@ -111,42 +94,12 @@ public class FrontendService
|
||||
// Add blazor files
|
||||
await ArchiveFsItem(zipArchive, blazorPath, blazorPath, "_framework/");
|
||||
|
||||
// Add bundle.css
|
||||
var bundleContent = await File.ReadAllBytesAsync(Path.Combine("storage", "tmp", "bundle.css"));
|
||||
await ArchiveBytes(zipArchive, "css/bundle.css", bundleContent);
|
||||
|
||||
// Add frontend.json
|
||||
var frontendConfig = await GetConfiguration();
|
||||
frontendConfig.HostEnvironment = "Static";
|
||||
var frontendJson = JsonSerializer.Serialize(frontendConfig);
|
||||
await ArchiveText(zipArchive, "frontend.json", frontendJson);
|
||||
|
||||
// Add plugin wwwroot files
|
||||
foreach (var pluginPath in PluginService.LoadedPlugins.Values)
|
||||
{
|
||||
var wwwRootPluginPath = Path.Combine(pluginPath, "wwwroot/");
|
||||
|
||||
if (!Directory.Exists(wwwRootPluginPath))
|
||||
continue;
|
||||
|
||||
await ArchiveFsItem(zipArchive, wwwRootPluginPath, wwwRootPluginPath);
|
||||
}
|
||||
|
||||
// Add plugin assemblies for client to the zip file
|
||||
var assembliesMap = PluginService.GetAssemblies("client");
|
||||
|
||||
foreach (var assemblyName in assembliesMap.Keys)
|
||||
{
|
||||
var path = assembliesMap[assemblyName];
|
||||
|
||||
await ArchiveFsItem(
|
||||
zipArchive,
|
||||
path,
|
||||
path,
|
||||
$"plugins/{assemblyName}"
|
||||
);
|
||||
}
|
||||
|
||||
// Finish zip archive and reset stream so the code calling this function can process it
|
||||
zipArchive.Dispose();
|
||||
await memoryStream.FlushAsync();
|
||||
|
||||
@@ -1,139 +0,0 @@
|
||||
using System.Text.Json;
|
||||
using Microsoft.Extensions.FileProviders;
|
||||
using MoonCore.Helpers;
|
||||
using Moonlight.ApiServer.Models;
|
||||
|
||||
namespace Moonlight.ApiServer.Services;
|
||||
|
||||
public class PluginService
|
||||
{
|
||||
private readonly ILogger<PluginService> Logger;
|
||||
private readonly string PluginRoot;
|
||||
|
||||
public readonly Dictionary<PluginManifest, string> LoadedPlugins = new();
|
||||
public IFileProvider WwwRootFileProvider;
|
||||
|
||||
public PluginService(ILogger<PluginService> logger)
|
||||
{
|
||||
Logger = logger;
|
||||
|
||||
PluginRoot = PathBuilder.Dir("storage", "plugins");
|
||||
}
|
||||
|
||||
public async Task Load()
|
||||
{
|
||||
var jsonOptions = new JsonSerializerOptions()
|
||||
{
|
||||
PropertyNameCaseInsensitive = true
|
||||
};
|
||||
|
||||
var pluginDirs = Directory.GetDirectories(PluginRoot);
|
||||
var pluginMap = new Dictionary<PluginManifest, string>();
|
||||
|
||||
#region Scan plugins/ directory for plugin.json files
|
||||
|
||||
foreach (var dir in pluginDirs)
|
||||
{
|
||||
var metaPath = PathBuilder.File(dir, "plugin.json");
|
||||
|
||||
if (!File.Exists(metaPath))
|
||||
{
|
||||
Logger.LogWarning("Skipped '{dir}' as it is missing a plugin.json", dir);
|
||||
continue;
|
||||
}
|
||||
|
||||
var json = await File.ReadAllTextAsync(metaPath);
|
||||
|
||||
try
|
||||
{
|
||||
var meta = JsonSerializer.Deserialize<PluginManifest>(json, jsonOptions);
|
||||
|
||||
if (meta == null)
|
||||
throw new JsonException("Unable to parse. Return value was null");
|
||||
|
||||
pluginMap.Add(meta, dir);
|
||||
}
|
||||
catch (JsonException e)
|
||||
{
|
||||
Logger.LogError("Unable to load plugin.json at '{path}': {e}", metaPath, e);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Depdenency check
|
||||
|
||||
foreach (var plugin in pluginMap.Keys)
|
||||
{
|
||||
var hasMissingDep = false;
|
||||
|
||||
foreach (var dependency in plugin.Dependencies)
|
||||
{
|
||||
if (pluginMap.Keys.All(x => x.Id != dependency))
|
||||
{
|
||||
hasMissingDep = true;
|
||||
Logger.LogWarning("Plugin '{name}' has missing dependency: {dep}", plugin.Name, dependency);
|
||||
}
|
||||
}
|
||||
|
||||
if (hasMissingDep)
|
||||
Logger.LogWarning("Unable to load '{name}' due to missing dependencies", plugin.Name);
|
||||
else
|
||||
LoadedPlugins.Add(plugin, pluginMap[plugin]);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Create wwwroot file provider
|
||||
|
||||
Logger.LogInformation("Creating wwwroot file provider");
|
||||
WwwRootFileProvider = CreateWwwRootProvider();
|
||||
|
||||
#endregion
|
||||
|
||||
Logger.LogInformation("Loaded {count} plugins", LoadedPlugins.Count);
|
||||
}
|
||||
|
||||
public Dictionary<string, string> GetAssemblies(string section)
|
||||
{
|
||||
var assemblyMap = new Dictionary<string, string>();
|
||||
|
||||
foreach (var loadedPlugin in LoadedPlugins.Keys)
|
||||
{
|
||||
// Skip all plugins which haven't defined any assemblies in that section
|
||||
if (!loadedPlugin.Assemblies.ContainsKey(section))
|
||||
continue;
|
||||
|
||||
var pluginPath = LoadedPlugins[loadedPlugin];
|
||||
|
||||
foreach (var assembly in loadedPlugin.Assemblies[section])
|
||||
{
|
||||
var assemblyFile = Path.GetFileName(assembly);
|
||||
assemblyMap[assemblyFile] = PathBuilder.File(pluginPath, assembly);
|
||||
}
|
||||
}
|
||||
|
||||
return assemblyMap;
|
||||
}
|
||||
|
||||
private IFileProvider CreateWwwRootProvider()
|
||||
{
|
||||
List<IFileProvider> wwwRootProviders = new();
|
||||
|
||||
foreach (var pluginFolder in LoadedPlugins.Values)
|
||||
{
|
||||
var wwwRootPath = Path.GetFullPath(
|
||||
PathBuilder.Dir(pluginFolder, "wwwroot")
|
||||
);
|
||||
|
||||
if(!Directory.Exists(wwwRootPath))
|
||||
continue;
|
||||
|
||||
wwwRootProviders.Add(
|
||||
new PhysicalFileProvider(wwwRootPath)
|
||||
);
|
||||
}
|
||||
|
||||
return new CompositeFileProvider(wwwRootProviders);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user