1
0
mirror of https://github.com/Artemis-RGB/Artemis synced 2025-12-12 13:28:33 +00:00

Web server - Ensure controllers and web modules provided by plugins are removed on disable

This commit is contained in:
Robert 2023-10-16 21:29:37 +02:00
parent 43e396bf6d
commit d73312c4f4
4 changed files with 109 additions and 50 deletions

View File

@ -95,35 +95,29 @@ public interface IWebServerService : IArtemisService
/// Adds a new Web API controller and restarts the web server
/// </summary>
/// <typeparam name="T">The type of Web API controller to remove</typeparam>
void AddController<T>(PluginFeature feature) where T : WebApiController;
WebApiControllerRegistration AddController<T>(PluginFeature feature) where T : WebApiController;
/// <summary>
/// Removes an existing Web API controller and restarts the web server
/// </summary>
/// <typeparam name="T">The type of Web API controller to remove</typeparam>
void RemoveController<T>() where T : WebApiController;
void RemoveController(WebApiControllerRegistration registration);
/// <summary>
/// Adds a new EmbedIO module and restarts the web server
/// </summary>
void AddModule(PluginFeature feature, Func<IWebModule> create);
WebModuleRegistration AddModule(PluginFeature feature, Func<IWebModule> create);
/// <summary>
/// Removes a EmbedIO module and restarts the web server
/// </summary>
void RemoveModule(Func<IWebModule> create);
void RemoveModule(WebModuleRegistration create);
/// <summary>
/// Adds a new EmbedIO module and restarts the web server
/// </summary>
/// <typeparam name="T">The type of module to add</typeparam>
void AddModule<T>(PluginFeature feature) where T : IWebModule;
/// <summary>
/// Removes a EmbedIO module and restarts the web server
/// </summary>
/// <typeparam name="T">The type of module to remove</typeparam>
void RemoveModule<T>() where T : IWebModule;
WebModuleRegistration AddModule<T>(PluginFeature feature) where T : IWebModule;
/// <summary>
/// Occurs when the web server has been created and is about to start. This is the ideal place to add your own modules.

View File

@ -3,26 +3,55 @@ using EmbedIO.WebApi;
namespace Artemis.Core.Services;
internal class WebApiControllerRegistration<T> : WebApiControllerRegistration where T : WebApiController
/// <summary>
/// Represents a web API controller registration.
/// </summary>
/// <typeparam name="T">The type of the web API controller.</typeparam>
public class WebApiControllerRegistration<T> : WebApiControllerRegistration where T : WebApiController
{
public WebApiControllerRegistration(PluginFeature feature) : base(feature, typeof(T))
internal WebApiControllerRegistration(IWebServerService webServerService, PluginFeature feature) : base(webServerService, feature, typeof(T))
{
Factory = () => feature.Plugin.Resolve<T>();
}
public Func<T> Factory { get; set; }
public override object UntypedFactory => Factory;
internal Func<T> Factory { get; set; }
internal override object UntypedFactory => Factory;
}
internal abstract class WebApiControllerRegistration
/// <summary>
/// Represents a web API controller registration.
/// </summary>
public abstract class WebApiControllerRegistration
{
protected WebApiControllerRegistration(PluginFeature feature, Type controllerType)
private readonly IWebServerService _webServerService;
/// <summary>
/// Creates a new instance of the <see cref="WebApiControllerRegistration"/> class.
/// </summary>
protected internal WebApiControllerRegistration(IWebServerService webServerService, PluginFeature feature, Type controllerType)
{
_webServerService = webServerService;
Feature = feature;
ControllerType = controllerType;
Feature.Disabled += FeatureOnDisabled;
}
public abstract object UntypedFactory { get; }
public Type ControllerType { get; set; }
private void FeatureOnDisabled(object? sender, EventArgs e)
{
_webServerService.RemoveController(this);
Feature.Disabled -= FeatureOnDisabled;
}
internal abstract object UntypedFactory { get; }
/// <summary>
/// Gets the type of the web API controller.
/// </summary>
public Type ControllerType { get; }
/// <summary>
/// Gets the plugin feature that provided the web API controller.
/// </summary>
public PluginFeature Feature { get; }
}

View File

@ -3,25 +3,44 @@ using EmbedIO;
namespace Artemis.Core.Services;
internal class WebModuleRegistration
/// <summary>
/// Represents a registration for a web module.
/// </summary>
public class WebModuleRegistration
{
public WebModuleRegistration(PluginFeature feature, Type webModuleType)
private readonly IWebServerService _webServerService;
internal WebModuleRegistration(IWebServerService webServerService, PluginFeature feature, Type webModuleType)
{
_webServerService = webServerService;
Feature = feature ?? throw new ArgumentNullException(nameof(feature));
WebModuleType = webModuleType ?? throw new ArgumentNullException(nameof(webModuleType));
Feature.Disabled += FeatureOnDisabled;
}
public WebModuleRegistration(PluginFeature feature, Func<IWebModule> create)
internal WebModuleRegistration(IWebServerService webServerService, PluginFeature feature, Func<IWebModule> create)
{
_webServerService = webServerService;
Feature = feature ?? throw new ArgumentNullException(nameof(feature));
Create = create ?? throw new ArgumentNullException(nameof(create));
Feature.Disabled += FeatureOnDisabled;
}
/// <summary>
/// The plugin feature that provided the web module.
/// </summary>
public PluginFeature Feature { get; }
public Type? WebModuleType { get; }
public Func<IWebModule>? Create { get; }
public IWebModule CreateInstance()
/// <summary>
/// The type of the web module.
/// </summary>
public Type? WebModuleType { get; }
internal Func<IWebModule>? Create { get; }
internal IWebModule CreateInstance()
{
if (Create != null)
return Create();
@ -29,4 +48,10 @@ internal class WebModuleRegistration
return (IWebModule) Feature.Plugin.Resolve(WebModuleType);
throw new ArtemisCoreException("WebModuleRegistration doesn't have a create function nor a web module type :(");
}
private void FeatureOnDisabled(object? sender, EventArgs e)
{
_webServerService.RemoveModule(this);
Feature.Disabled -= FeatureOnDisabled;
}
}

View File

@ -39,9 +39,9 @@ internal class WebServerService : IWebServerService, IDisposable
PluginsModule = new PluginsModule("/plugins");
if (coreService.IsInitialized)
StartWebServer();
AutoStartWebServer();
else
coreService.Initialized += (_, _) => StartWebServer();
coreService.Initialized += (sender, args) => AutoStartWebServer();
}
public event EventHandler? WebServerStopped;
@ -138,7 +138,7 @@ internal class WebServerService : IWebServerService, IDisposable
// Add registered controllers to the API module
foreach (WebApiControllerRegistration registration in _controllers)
apiModule.RegisterController(registration.ControllerType, (Func<WebApiController>) registration.UntypedFactory);
// Listen for state changes.
server.StateChanged += (s, e) => _logger.Verbose("WebServer new state - {state}", e.NewState);
@ -173,6 +173,18 @@ internal class WebServerService : IWebServerService, IDisposable
OnWebServerStarted();
}
}
private void AutoStartWebServer()
{
try
{
StartWebServer();
}
catch (Exception exception)
{
_logger.Warning(exception, "Failed to initially start webserver");
}
}
#endregion
@ -237,10 +249,6 @@ internal class WebServerService : IWebServerService, IDisposable
return endPoint;
}
private void HandleDataModelRequest<T>(Module<T> module, T value) where T : DataModel, new()
{
}
public void RemovePluginEndPoint(PluginEndPoint endPoint)
{
PluginsModule.RemovePluginEndPoint(endPoint);
@ -250,15 +258,20 @@ internal class WebServerService : IWebServerService, IDisposable
#region Controller management
public void AddController<T>(PluginFeature feature) where T : WebApiController
public WebApiControllerRegistration AddController<T>(PluginFeature feature) where T : WebApiController
{
_controllers.Add(new WebApiControllerRegistration<T>(feature));
if (feature == null) throw new ArgumentNullException(nameof(feature));
WebApiControllerRegistration<T> registration = new(this, feature);
_controllers.Add(registration);
StartWebServer();
return registration;
}
public void RemoveController<T>() where T : WebApiController
public void RemoveController(WebApiControllerRegistration registration)
{
_controllers.RemoveAll(r => r.ControllerType == typeof(T));
_controllers.Remove(registration);
StartWebServer();
}
@ -266,33 +279,31 @@ internal class WebServerService : IWebServerService, IDisposable
#region Module management
public void AddModule(PluginFeature feature, Func<IWebModule> create)
public WebModuleRegistration AddModule(PluginFeature feature, Func<IWebModule> create)
{
if (feature == null) throw new ArgumentNullException(nameof(feature));
_modules.Add(new WebModuleRegistration(feature, create));
WebModuleRegistration registration = new(this, feature, create);
_modules.Add(registration);
StartWebServer();
return registration;
}
public void RemoveModule(Func<IWebModule> create)
{
_modules.RemoveAll(r => r.Create == create);
StartWebServer();
}
public void AddModule<T>(PluginFeature feature) where T : IWebModule
public WebModuleRegistration AddModule<T>(PluginFeature feature) where T : IWebModule
{
if (feature == null) throw new ArgumentNullException(nameof(feature));
if (_modules.Any(r => r.WebModuleType == typeof(T)))
return;
_modules.Add(new WebModuleRegistration(feature, typeof(T)));
WebModuleRegistration registration = new(this, feature, typeof(T));
_modules.Add(registration);
StartWebServer();
return registration;
}
public void RemoveModule<T>() where T : IWebModule
public void RemoveModule(WebModuleRegistration registration)
{
_modules.RemoveAll(r => r.WebModuleType == typeof(T));
_modules.Remove(registration);
StartWebServer();
}