1
0
mirror of https://github.com/Artemis-RGB/Artemis synced 2025-12-13 05:48:35 +00:00

Webserver - Removed '/api/' part from URLs

Webserver - Added the ability to register custom EmbedIO modules
Webserver - Ensure custom controllers/modules are always cleaned up on plugin feature disable
This commit is contained in:
Robert 2021-04-06 19:19:19 +02:00
parent e32b181d9f
commit ed704a165c
7 changed files with 102 additions and 20 deletions

View File

@ -55,7 +55,8 @@ namespace Artemis.Core.Services
DataModelJsonPluginEndPoint<T> AddDataModelJsonEndPoint<T>(Module<T> module, string endPointName) where T : DataModel;
/// <summary>
/// Adds a new endpoint that directly maps received JSON to the data model of the provided <paramref name="profileModule" />.
/// Adds a new endpoint that directly maps received JSON to the data model of the provided
/// <paramref name="profileModule" />.
/// </summary>
/// <typeparam name="T">The data model type of the module</typeparam>
/// <param name="profileModule">The module whose datamodel to apply the received JSON to</param>
@ -64,7 +65,8 @@ namespace Artemis.Core.Services
DataModelJsonPluginEndPoint<T> AddDataModelJsonEndPoint<T>(ProfileModule<T> profileModule, string endPointName) where T : DataModel;
/// <summary>
/// Adds a new endpoint that directly maps received JSON to the data model of the provided <paramref name="dataModelExpansion" />.
/// Adds a new endpoint that directly maps received JSON to the data model of the provided
/// <paramref name="dataModelExpansion" />.
/// </summary>
/// <typeparam name="T">The data model type of the module</typeparam>
/// <param name="dataModelExpansion">The data model expansion whose datamodel to apply the received JSON to</param>
@ -114,7 +116,7 @@ namespace Artemis.Core.Services
/// 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>() where T : WebApiController;
void AddController<T>(PluginFeature feature) where T : WebApiController;
/// <summary>
/// Removes an existing Web API controller and restarts the web server
@ -122,6 +124,18 @@ namespace Artemis.Core.Services
/// <typeparam name="T">The type of Web API controller to remove</typeparam>
void RemoveController<T>() where T : WebApiController;
/// <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;
/// <summary>
/// Occurs when the web server has been created and is about to start. This is the ideal place to add your own modules.
/// </summary>

View File

@ -48,13 +48,13 @@ namespace Artemis.Core.Services
protected override async Task OnRequestAsync(IHttpContext context)
{
if (context.Route.SubPath == null)
throw HttpException.NotFound();
return;
// Split the sub path
string[] pathParts = context.Route.SubPath.Substring(1).Split('/');
// Expect a plugin ID and an endpoint
if (pathParts == null || pathParts.Length != 2)
throw HttpException.BadRequest("Path must contain a plugin ID and endpoint and nothing else.");
if (pathParts.Length != 2)
return;
// Find a matching plugin
if (!_pluginEndPoints.TryGetValue(pathParts[0], out Dictionary<string, PluginEndPoint>? endPoints))
@ -78,7 +78,7 @@ namespace Artemis.Core.Services
}
/// <inheritdoc />
public override bool IsFinalHandler => true;
public override bool IsFinalHandler => false;
internal string? ServerUrl { get; set; }

View File

@ -6,9 +6,9 @@ namespace Artemis.Core.Services
{
internal class WebApiControllerRegistration<T> : WebApiControllerRegistration where T : WebApiController
{
public WebApiControllerRegistration(IKernel kernel) : base(typeof(T))
public WebApiControllerRegistration(PluginFeature feature) : base(feature, typeof(T))
{
Factory = () => kernel.Get<T>();
Factory = () => feature.Plugin.Kernel!.Get<T>();
}
public Func<T> Factory { get; set; }
@ -17,12 +17,14 @@ namespace Artemis.Core.Services
internal abstract class WebApiControllerRegistration
{
protected WebApiControllerRegistration(Type controllerType)
protected WebApiControllerRegistration(PluginFeature feature, Type controllerType)
{
Feature = feature;
ControllerType = controllerType;
}
public abstract object UntypedFactory { get; }
public Type ControllerType { get; set; }
public PluginFeature Feature { get; }
}
}

View File

@ -0,0 +1,20 @@
using System;
using EmbedIO;
using Ninject;
namespace Artemis.Core.Services
{
internal class WebModuleRegistration
{
public PluginFeature Feature { get; }
public Type WebModuleType { get; }
public WebModuleRegistration(PluginFeature feature, Type webModuleType)
{
Feature = feature;
WebModuleType = webModuleType;
}
public IWebModule CreateInstance() => (IWebModule) Feature.Plugin.Kernel!.Get(WebModuleType);
}
}

View File

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Artemis.Core.DataModelExpansions;
@ -8,7 +9,6 @@ using Artemis.Core.Modules;
using EmbedIO;
using EmbedIO.WebApi;
using Newtonsoft.Json;
using Ninject;
using Serilog;
namespace Artemis.Core.Services
@ -16,18 +16,19 @@ namespace Artemis.Core.Services
internal class WebServerService : IWebServerService, IDisposable
{
private readonly List<WebApiControllerRegistration> _controllers;
private readonly IKernel _kernel;
private readonly List<WebModuleRegistration> _modules;
private readonly ILogger _logger;
private readonly PluginSetting<int> _webServerPortSetting;
public WebServerService(IKernel kernel, ILogger logger, ISettingsService settingsService)
public WebServerService(ILogger logger, ISettingsService settingsService, IPluginManagementService pluginManagementService)
{
_kernel = kernel;
_logger = logger;
_controllers = new List<WebApiControllerRegistration>();
_modules = new List<WebModuleRegistration>();
_webServerPortSetting = settingsService.GetSetting("WebServer.Port", 9696);
_webServerPortSetting.SettingChanged += WebServerPortSettingOnSettingChanged;
pluginManagementService.PluginFeatureDisabled += PluginManagementServiceOnPluginFeatureDisabled;
PluginsModule = new PluginsModule("/plugins");
StartWebServer();
@ -43,12 +44,18 @@ namespace Artemis.Core.Services
Server?.Dispose();
Server = null;
WebApiModule apiModule = new("/api/", JsonNetSerializer);
WebApiModule apiModule = new("/", JsonNetSerializer);
PluginsModule.ServerUrl = $"http://localhost:{_webServerPortSetting.Value}/";
WebServer server = new WebServer(o => o.WithUrlPrefix($"http://*:{_webServerPortSetting.Value}/").WithMode(HttpListenerMode.EmbedIO))
.WithLocalSessionManager()
.WithModule(PluginsModule);
// Add registered modules
foreach (var webModule in _modules)
server = server.WithModule(webModule.CreateInstance());
server = server
.WithModule(apiModule)
.WithModule(PluginsModule)
.HandleHttpException((context, exception) => HandleHttpExceptionJson(context, exception))
.HandleUnhandledException(JsonExceptionHandlerCallback);
@ -166,9 +173,9 @@ namespace Artemis.Core.Services
#region Controller management
public void AddController<T>() where T : WebApiController
public void AddController<T>(PluginFeature feature) where T : WebApiController
{
_controllers.Add(new WebApiControllerRegistration<T>(_kernel));
_controllers.Add(new WebApiControllerRegistration<T>(feature));
StartWebServer();
}
@ -180,6 +187,26 @@ namespace Artemis.Core.Services
#endregion
#region Module management
public void 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)));
StartWebServer();
}
public void RemoveModule<T>() where T : IWebModule
{
_modules.RemoveAll(r => r.WebModuleType == typeof(T));
StartWebServer();
}
#endregion
#region Handlers
private async Task JsonExceptionHandlerCallback(IHttpContext context, Exception exception)
@ -235,6 +262,25 @@ namespace Artemis.Core.Services
StartWebServer();
}
private void PluginManagementServiceOnPluginFeatureDisabled(object? sender, PluginFeatureEventArgs e)
{
bool mustRestart = false;
if (_controllers.Any(c => c.Feature == e.PluginFeature))
{
mustRestart = true;
_controllers.RemoveAll(c => c.Feature == e.PluginFeature);
}
if (_modules.Any(m => m.Feature == e.PluginFeature))
{
mustRestart = true;
_modules.RemoveAll(m => m.Feature == e.PluginFeature);
}
if (mustRestart)
StartWebServer();
}
#endregion
#region IDisposable

View File

@ -64,7 +64,7 @@ namespace Artemis.UI
string url = await File.ReadAllTextAsync(Path.Combine(Constants.DataFolder, "webserver.txt"));
using HttpClient client = new();
await client.PostAsync(url + "api/remote/bring-to-foreground", null!);
await client.PostAsync(url + "remote/bring-to-foreground", null!);
}
private void UtilitiesOnRestartRequested(object sender, RestartEventArgs e)

View File

@ -106,7 +106,7 @@ namespace Artemis.UI.Services
public void RegisterControllers()
{
_webServerService.AddController<RemoteController>();
_webServerService.AddController<RemoteController>(Constants.CorePlugin.Features.First().Instance!);
}
/// <inheritdoc />