[backend/plugins] Allow plugins to instantiate cron tasks (ISH-422)
This commit is contained in:
parent
c309fb00c9
commit
a73587d142
3 changed files with 29 additions and 29 deletions
|
@ -7,27 +7,25 @@ namespace Iceshrimp.Backend.Core.Helpers;
|
||||||
|
|
||||||
using LoadedPlugin = (Plugin descriptor, IPlugin instance);
|
using LoadedPlugin = (Plugin descriptor, IPlugin instance);
|
||||||
|
|
||||||
public class PluginLoader
|
public abstract class PluginLoader
|
||||||
{
|
{
|
||||||
// Increment whenever a breaking plugin API change is made
|
// Increment whenever a breaking plugin API change is made
|
||||||
private const int ApiVersion = 1;
|
private const int ApiVersion = 1;
|
||||||
|
|
||||||
public PluginLoader()
|
private static readonly string PathRoot = Environment.GetEnvironmentVariable("ICESHRIMP_PLUGIN_DIR") ??
|
||||||
{
|
Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!,
|
||||||
var path = Environment.GetEnvironmentVariable("ICESHRIMP_PLUGIN_DIR");
|
"plugins");
|
||||||
path ??= Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!, "plugins");
|
|
||||||
_path = Path.Combine(path, $"v{ApiVersion}");
|
|
||||||
}
|
|
||||||
|
|
||||||
private readonly string _path;
|
private static readonly string DllPath = Path.Combine(PathRoot, $"v{ApiVersion}");
|
||||||
private List<LoadedPlugin> _loaded = [];
|
|
||||||
private IEnumerable<IPlugin> Plugins => _loaded.Select(p => p.instance);
|
|
||||||
public IEnumerable<Assembly> Assemblies => _loaded.Select(p => p.descriptor.Assembly);
|
|
||||||
|
|
||||||
public async Task LoadPlugins()
|
private static List<LoadedPlugin> _loaded = [];
|
||||||
|
private static IEnumerable<IPlugin> Plugins => _loaded.Select(p => p.instance);
|
||||||
|
public static IEnumerable<Assembly> Assemblies => _loaded.Select(p => p.descriptor.Assembly);
|
||||||
|
|
||||||
|
public static async Task LoadPlugins()
|
||||||
{
|
{
|
||||||
if (!Directory.Exists(_path)) return;
|
if (!Directory.Exists(DllPath)) return;
|
||||||
var dlls = Directory.EnumerateFiles(_path, "*.dll").ToList();
|
var dlls = Directory.EnumerateFiles(DllPath, "*.dll").ToList();
|
||||||
var catalogs = dlls
|
var catalogs = dlls
|
||||||
.Select(p => new AssemblyPluginCatalog(p, type => type.Implements<IPlugin>()))
|
.Select(p => new AssemblyPluginCatalog(p, type => type.Implements<IPlugin>()))
|
||||||
.Cast<IPluginCatalog>()
|
.Cast<IPluginCatalog>()
|
||||||
|
@ -44,28 +42,28 @@ public class PluginLoader
|
||||||
await Plugins.Select(i => i.Initialize()).AwaitAllNoConcurrencyAsync();
|
await Plugins.Select(i => i.Initialize()).AwaitAllNoConcurrencyAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RunBuilderHooks(WebApplicationBuilder builder)
|
public static void RunBuilderHooks(WebApplicationBuilder builder)
|
||||||
{
|
{
|
||||||
foreach (var plugin in Plugins) plugin.BuilderHook(builder);
|
foreach (var plugin in Plugins) plugin.BuilderHook(builder);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RunAppHooks(WebApplication app)
|
public static void RunAppHooks(WebApplication app)
|
||||||
{
|
{
|
||||||
foreach (var plugin in Plugins) plugin.AppHook(app);
|
foreach (var plugin in Plugins) plugin.AppHook(app);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void PrintPluginInformation(WebApplication app)
|
public static void PrintPluginInformation(WebApplication app)
|
||||||
{
|
{
|
||||||
var logger = app.Services.GetRequiredService<ILogger<PluginLoader>>();
|
var logger = app.Services.GetRequiredService<ILogger<PluginLoader>>();
|
||||||
if (_loaded.Count == 0)
|
if (_loaded.Count == 0)
|
||||||
{
|
{
|
||||||
logger.LogInformation("Found {count} plugins in {dllPath}.", _loaded.Count, _path);
|
logger.LogInformation("Found {count} plugins in {dllPath}.", _loaded.Count, DllPath);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var plugins = _loaded.Select(plugin => $"{plugin.instance.Name} v{plugin.instance.Version} " +
|
var plugins = _loaded.Select(plugin => $"{plugin.instance.Name} v{plugin.instance.Version} " +
|
||||||
$"({Path.GetFileName(plugin.descriptor.Assembly.Location)})");
|
$"({Path.GetFileName(plugin.descriptor.Assembly.Location)})");
|
||||||
logger.LogInformation("Loaded {count} plugins from {dllPath}: \n* {files}", _loaded.Count, _path,
|
logger.LogInformation("Loaded {count} plugins from {dllPath}: \n* {files}", _loaded.Count, DllPath,
|
||||||
string.Join("\n* ", plugins));
|
string.Join("\n* ", plugins));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
using System.Reflection;
|
||||||
using Iceshrimp.Backend.Core.Helpers;
|
using Iceshrimp.Backend.Core.Helpers;
|
||||||
|
|
||||||
namespace Iceshrimp.Backend.Core.Services;
|
namespace Iceshrimp.Backend.Core.Services;
|
||||||
|
@ -6,7 +7,9 @@ public class CronService(IServiceScopeFactory serviceScopeFactory) : BackgroundS
|
||||||
{
|
{
|
||||||
protected override Task ExecuteAsync(CancellationToken token)
|
protected override Task ExecuteAsync(CancellationToken token)
|
||||||
{
|
{
|
||||||
var tasks = AssemblyHelpers.GetImplementationsOfInterface(typeof(ICronTask))
|
var tasks = PluginLoader
|
||||||
|
.Assemblies.Prepend(Assembly.GetExecutingAssembly())
|
||||||
|
.SelectMany(assembly => AssemblyHelpers.GetImplementationsOfInterface(typeof(ICronTask), assembly))
|
||||||
.Select(p => Activator.CreateInstance(p) as ICronTask)
|
.Select(p => Activator.CreateInstance(p) as ICronTask)
|
||||||
.Where(p => p != null)
|
.Where(p => p != null)
|
||||||
.Cast<ICronTask>();
|
.Cast<ICronTask>();
|
||||||
|
|
|
@ -10,8 +10,7 @@ var builder = WebApplication.CreateBuilder(args);
|
||||||
builder.Configuration.Sources.Clear();
|
builder.Configuration.Sources.Clear();
|
||||||
builder.Configuration.AddCustomConfiguration();
|
builder.Configuration.AddCustomConfiguration();
|
||||||
|
|
||||||
var pluginLoader = new PluginLoader();
|
await PluginLoader.LoadPlugins();
|
||||||
await pluginLoader.LoadPlugins();
|
|
||||||
|
|
||||||
builder.Services.AddControllers()
|
builder.Services.AddControllers()
|
||||||
.AddNewtonsoftJson() //TODO: remove once dotNetRdf switches to System.Text.Json (or we switch to LinkedData.NET)
|
.AddNewtonsoftJson() //TODO: remove once dotNetRdf switches to System.Text.Json (or we switch to LinkedData.NET)
|
||||||
|
@ -20,7 +19,7 @@ builder.Services.AddControllers()
|
||||||
.AddModelBindingProviders()
|
.AddModelBindingProviders()
|
||||||
.AddValueProviderFactories()
|
.AddValueProviderFactories()
|
||||||
.AddApiBehaviorOptions()
|
.AddApiBehaviorOptions()
|
||||||
.AddPlugins(pluginLoader.Assemblies);
|
.AddPlugins(PluginLoader.Assemblies);
|
||||||
|
|
||||||
builder.Services.AddSwaggerGenWithOptions();
|
builder.Services.AddSwaggerGenWithOptions();
|
||||||
builder.Services.AddLogging(logging => logging.AddCustomConsoleFormatter());
|
builder.Services.AddLogging(logging => logging.AddCustomConsoleFormatter());
|
||||||
|
@ -46,7 +45,7 @@ builder.Services.ConfigureServices(builder.Configuration);
|
||||||
builder.WebHost.ConfigureKestrel(builder.Configuration);
|
builder.WebHost.ConfigureKestrel(builder.Configuration);
|
||||||
builder.WebHost.UseStaticWebAssets();
|
builder.WebHost.UseStaticWebAssets();
|
||||||
|
|
||||||
pluginLoader.RunBuilderHooks(builder);
|
PluginLoader.RunBuilderHooks(builder);
|
||||||
|
|
||||||
var app = builder.Build();
|
var app = builder.Build();
|
||||||
var config = await app.Initialize(args);
|
var config = await app.Initialize(args);
|
||||||
|
@ -76,8 +75,8 @@ app.MapHub<StreamingHub>("/hubs/streaming");
|
||||||
app.MapRazorPages();
|
app.MapRazorPages();
|
||||||
app.MapFrontendRoutes("/Shared/FrontendSPA");
|
app.MapFrontendRoutes("/Shared/FrontendSPA");
|
||||||
|
|
||||||
pluginLoader.RunAppHooks(app);
|
PluginLoader.RunAppHooks(app);
|
||||||
pluginLoader.PrintPluginInformation(app);
|
PluginLoader.PrintPluginInformation(app);
|
||||||
|
|
||||||
// If running under IIS, this collection is read only
|
// If running under IIS, this collection is read only
|
||||||
if (!app.Urls.IsReadOnly)
|
if (!app.Urls.IsReadOnly)
|
||||||
|
|
Loading…
Add table
Reference in a new issue