diff --git a/src/Artemis.Core/Artemis.Core.csproj.DotSettings b/src/Artemis.Core/Artemis.Core.csproj.DotSettings index 5ac92d7ad..61728d988 100644 --- a/src/Artemis.Core/Artemis.Core.csproj.DotSettings +++ b/src/Artemis.Core/Artemis.Core.csproj.DotSettings @@ -65,6 +65,7 @@ True True True + True True True True diff --git a/src/Artemis.Core/Services/WebServer/Controllers/PluginsController.cs b/src/Artemis.Core/Services/WebServer/Controllers/PluginsController.cs new file mode 100644 index 000000000..7c4e8c29e --- /dev/null +++ b/src/Artemis.Core/Services/WebServer/Controllers/PluginsController.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using EmbedIO; +using EmbedIO.Routing; +using EmbedIO.WebApi; + +namespace Artemis.Core.Services +{ + internal class PluginsController : WebApiController + { + private readonly IWebServerService _webServerService; + + public PluginsController(IWebServerService webServerService) + { + _webServerService = webServerService; + } + + [Route(HttpVerbs.Get, "/plugins/endpoints")] + public IReadOnlyCollection GetPluginEndPoints() + { + return _webServerService.PluginsModule.PluginEndPoints; + } + + [Route(HttpVerbs.Get, "/plugins/endpoints/{plugin}/{endPoint}")] + public PluginEndPoint GetPluginEndPoint(Guid plugin, string endPoint) + { + PluginEndPoint? pluginEndPoint = _webServerService.PluginsModule.PluginEndPoints.FirstOrDefault(e => e.PluginFeature.Plugin.Guid == plugin && e.Name == endPoint); + if (pluginEndPoint == null) + throw HttpException.NotFound(); + + return pluginEndPoint; + } + } +} \ No newline at end of file diff --git a/src/Artemis.Core/Services/WebServer/EndPoints/JsonPluginEndPoint.cs b/src/Artemis.Core/Services/WebServer/EndPoints/JsonPluginEndPoint.cs index 5a8ebcd98..061bd984e 100644 --- a/src/Artemis.Core/Services/WebServer/EndPoints/JsonPluginEndPoint.cs +++ b/src/Artemis.Core/Services/WebServer/EndPoints/JsonPluginEndPoint.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using System.Threading.Tasks; using EmbedIO; using Newtonsoft.Json; @@ -19,12 +20,15 @@ namespace Artemis.Core.Services { _requestHandler = requestHandler; ThrowOnFail = true; + Accepts = MimeType.Json; } internal JsonPluginEndPoint(PluginFeature pluginFeature, string name, PluginsModule pluginsModule, Func responseRequestHandler) : base(pluginFeature, name, pluginsModule) { _responseRequestHandler = responseRequestHandler; ThrowOnFail = true; + Accepts = MimeType.Json; + Returns = MimeType.Json; } /// @@ -37,7 +41,7 @@ namespace Artemis.Core.Services #region Overrides of PluginEndPoint /// - internal override void ProcessRequest(IHttpContext context) + protected override async Task ProcessRequest(IHttpContext context) { if (context.Request.HttpVerb != HttpVerbs.Post) throw HttpException.MethodNotAllowed("This end point only accepts POST calls"); @@ -48,7 +52,7 @@ namespace Artemis.Core.Services object? response = null; try { - T deserialized = JsonConvert.DeserializeObject(reader.ReadToEnd()); + T deserialized = JsonConvert.DeserializeObject(await reader.ReadToEndAsync()); if (_requestHandler != null) { @@ -67,8 +71,8 @@ namespace Artemis.Core.Services throw; } - using TextWriter writer = context.OpenResponseText(); - writer.Write(JsonConvert.SerializeObject(response)); + await using TextWriter writer = context.OpenResponseText(); + await writer.WriteAsync(JsonConvert.SerializeObject(response)); } #endregion diff --git a/src/Artemis.Core/Services/WebServer/EndPoints/PluginEndPoint.cs b/src/Artemis.Core/Services/WebServer/EndPoints/PluginEndPoint.cs index e2250aa8b..c81c772ee 100644 --- a/src/Artemis.Core/Services/WebServer/EndPoints/PluginEndPoint.cs +++ b/src/Artemis.Core/Services/WebServer/EndPoints/PluginEndPoint.cs @@ -1,8 +1,13 @@ using System; +using System.Threading.Tasks; using EmbedIO; +using Newtonsoft.Json; namespace Artemis.Core.Services { + /// + /// Represents a base type for plugin end points to be targeted by the + /// public abstract class PluginEndPoint { private readonly PluginsModule _pluginsModule; @@ -16,11 +21,6 @@ namespace Artemis.Core.Services PluginFeature.Disabled += OnDisabled; } - /// - /// Gets the plugin the data model is associated with - /// - public PluginFeature PluginFeature { get; } - /// /// Gets the name of the end point /// @@ -29,9 +29,39 @@ namespace Artemis.Core.Services /// /// Gets the full URL of the end point /// - public string Url => $"{_pluginsModule.BaseRoute}{PluginFeature.Plugin.Guid}/{Name}"; + public string Url => $"{_pluginsModule.ServerUrl.TrimEnd('/')}{_pluginsModule.BaseRoute}{PluginFeature.Plugin.Guid}/{Name}"; - internal abstract void ProcessRequest(IHttpContext context); + /// + /// Gets the plugin the end point is associated with + /// + [JsonIgnore] + public PluginFeature PluginFeature { get; } + + /// + /// Gets the plugin info of the plugin the end point is associated with + /// + public PluginInfo PluginInfo => PluginFeature.Plugin.Info; + + /// + /// Gets the mime type of the input this end point accepts + /// + public string Accepts { get; protected set; } + + /// + /// Gets the mime type of the output this end point returns + /// + public string Returns { get; protected set; } + + /// + /// Called whenever the end point has to process a request + /// + /// The HTTP context of the request + protected abstract Task ProcessRequest(IHttpContext context); + + internal async Task InternalProcessRequest(IHttpContext context) + { + await ProcessRequest(context); + } private void OnDisabled(object? sender, EventArgs e) { diff --git a/src/Artemis.Core/Services/WebServer/EndPoints/RawPluginEndPoint.cs b/src/Artemis.Core/Services/WebServer/EndPoints/RawPluginEndPoint.cs index 49dc63614..1aaca2ac3 100644 --- a/src/Artemis.Core/Services/WebServer/EndPoints/RawPluginEndPoint.cs +++ b/src/Artemis.Core/Services/WebServer/EndPoints/RawPluginEndPoint.cs @@ -1,4 +1,5 @@ using System; +using System.Threading.Tasks; using EmbedIO; namespace Artemis.Core.Services @@ -13,7 +14,7 @@ namespace Artemis.Core.Services public class RawPluginEndPoint : PluginEndPoint { /// - internal RawPluginEndPoint(PluginFeature pluginFeature, string name, PluginsModule pluginsModule, Action requestHandler) : base(pluginFeature, name, pluginsModule) + internal RawPluginEndPoint(PluginFeature pluginFeature, string name, PluginsModule pluginsModule, Func requestHandler) : base(pluginFeature, name, pluginsModule) { RequestHandler = requestHandler; } @@ -21,14 +22,30 @@ namespace Artemis.Core.Services /// /// Gets or sets the handler used to handle incoming requests to this endpoint /// - public Action RequestHandler { get; } + public Func RequestHandler { get; } + + /// + /// Sets the mime type this plugin end point accepts + /// + public void SetAcceptType(string type) + { + Accepts = type; + } + + /// + /// Sets the mime type this plugin end point returns + /// + public void SetReturnType(string type) + { + Returns = type; + } #region Overrides of PluginEndPoint /// - internal override void ProcessRequest(IHttpContext context) + protected override async Task ProcessRequest(IHttpContext context) { - RequestHandler(context); + await RequestHandler(context); } #endregion diff --git a/src/Artemis.Core/Services/WebServer/EndPoints/StringPluginEndPoint.cs b/src/Artemis.Core/Services/WebServer/EndPoints/StringPluginEndPoint.cs index 20cb823c3..b5f65bb2d 100644 --- a/src/Artemis.Core/Services/WebServer/EndPoints/StringPluginEndPoint.cs +++ b/src/Artemis.Core/Services/WebServer/EndPoints/StringPluginEndPoint.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using System.Threading.Tasks; using EmbedIO; namespace Artemis.Core.Services @@ -16,38 +17,41 @@ namespace Artemis.Core.Services internal StringPluginEndPoint(PluginFeature pluginFeature, string name, PluginsModule pluginsModule, Action requestHandler) : base(pluginFeature, name, pluginsModule) { _requestHandler = requestHandler; + Accepts = MimeType.PlainText; } internal StringPluginEndPoint(PluginFeature pluginFeature, string name, PluginsModule pluginsModule, Func requestHandler) : base(pluginFeature, name, pluginsModule) { _responseRequestHandler = requestHandler; + Accepts = MimeType.PlainText; + Returns = MimeType.PlainText; } #region Overrides of PluginEndPoint /// - internal override void ProcessRequest(IHttpContext context) + protected override async Task ProcessRequest(IHttpContext context) { if (context.Request.HttpVerb != HttpVerbs.Post) throw HttpException.MethodNotAllowed("This end point only accepts POST calls"); - + context.Response.ContentType = MimeType.PlainText; using TextReader reader = context.OpenRequestText(); string? response; if (_requestHandler != null) { - _requestHandler(reader.ReadToEnd()); + _requestHandler(await reader.ReadToEndAsync()); return; } - - else if (_responseRequestHandler != null) - response = _responseRequestHandler(reader.ReadToEnd()); + + if (_responseRequestHandler != null) + response = _responseRequestHandler(await reader.ReadToEndAsync()); else throw new ArtemisCoreException("String plugin end point has no request handler"); - using TextWriter writer = context.OpenResponseText(); - writer.Write(response); + await using TextWriter writer = context.OpenResponseText(); + await writer.WriteAsync(response); } #endregion diff --git a/src/Artemis.Core/Services/WebServer/Interfaces/IWebServerService.cs b/src/Artemis.Core/Services/WebServer/Interfaces/IWebServerService.cs index 223f0b505..704a4a19c 100644 --- a/src/Artemis.Core/Services/WebServer/Interfaces/IWebServerService.cs +++ b/src/Artemis.Core/Services/WebServer/Interfaces/IWebServerService.cs @@ -1,4 +1,5 @@ using System; +using System.Threading.Tasks; using EmbedIO; using EmbedIO.WebApi; @@ -10,10 +11,15 @@ namespace Artemis.Core.Services public interface IWebServerService : IArtemisService { /// - /// Gets the currently active instance of the web server + /// Gets the current instance of the web server, replaced when occurs. /// WebServer? Server { get; } + /// + /// Gets the plugins module containing all plugin end points + /// + PluginsModule PluginsModule { get; } + /// /// Adds a new endpoint for the given plugin feature receiving an object of type /// Note: Object will be deserialized using JSON. @@ -67,7 +73,7 @@ namespace Artemis.Core.Services /// The name of the end point, must be unique /// /// The resulting end point - RawPluginEndPoint AddRawEndPoint(PluginFeature feature, string endPointName, Action requestHandler); + RawPluginEndPoint AddRawEndPoint(PluginFeature feature, string endPointName, Func requestHandler); /// /// Removes an existing endpoint @@ -88,8 +94,8 @@ namespace Artemis.Core.Services void RemoveController() where T : WebApiController; /// - /// Occurs when a new instance of the web server was been created + /// Occurs when the web server has been created and is about to start. This is the ideal place to add your own modules. /// - event EventHandler? WebServerCreated; + event EventHandler? WebServerStarting; } } \ No newline at end of file diff --git a/src/Artemis.Core/Services/WebServer/PluginsModule.cs b/src/Artemis.Core/Services/WebServer/PluginsModule.cs index c19af8add..78de97e66 100644 --- a/src/Artemis.Core/Services/WebServer/PluginsModule.cs +++ b/src/Artemis.Core/Services/WebServer/PluginsModule.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Text; using System.Threading.Tasks; using EmbedIO; @@ -7,18 +8,20 @@ using Newtonsoft.Json; namespace Artemis.Core.Services { - internal class PluginsModule : WebModuleBase + /// + /// Represents an EmbedIO web module used to process web requests and forward them to the right + /// . + /// + public class PluginsModule : WebModuleBase { private readonly Dictionary> _pluginEndPoints; - /// - public PluginsModule(string baseRoute) : base(baseRoute) + internal PluginsModule(string baseRoute) : base(baseRoute) { _pluginEndPoints = new Dictionary>(); - OnUnhandledException += HandleUnhandledExceptionJson; } - public void AddPluginEndPoint(PluginEndPoint registration) + internal void AddPluginEndPoint(PluginEndPoint registration) { string id = registration.PluginFeature.Plugin.Guid.ToString(); if (!_pluginEndPoints.TryGetValue(id, out Dictionary? registrations)) @@ -32,7 +35,7 @@ namespace Artemis.Core.Services registrations.Add(registration.Name, registration); } - public void RemovePluginEndPoint(PluginEndPoint registration) + internal void RemovePluginEndPoint(PluginEndPoint registration) { string id = registration.PluginFeature.Plugin.Guid.ToString(); if (!_pluginEndPoints.TryGetValue(id, out Dictionary? registrations)) @@ -42,15 +45,6 @@ namespace Artemis.Core.Services registrations.Remove(registration.Name); } - private async Task HandleUnhandledExceptionJson(IHttpContext context, Exception exception) - { - await context.SendStringAsync( - JsonConvert.SerializeObject(new ArtemisPluginException("The plugin failed to process the request", exception), Formatting.Indented), - MimeType.Json, - Encoding.UTF8 - ); - } - #region Overrides of WebModuleBase /// @@ -58,23 +52,23 @@ namespace Artemis.Core.Services { if (context.Route.SubPath == null) throw HttpException.NotFound(); - + // 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."); - + // Find a matching plugin if (!_pluginEndPoints.TryGetValue(pathParts[0], out Dictionary? endPoints)) throw HttpException.NotFound($"Found no plugin with ID {pathParts[0]}."); - + // Find a matching endpoint if (!endPoints.TryGetValue(pathParts[1], out PluginEndPoint? endPoint)) throw HttpException.NotFound($"Found no endpoint called {pathParts[1]} for plugin with ID {pathParts[0]}."); - + // It is up to the registration how the request is eventually handled, it might even set a response here - endPoint.ProcessRequest(context); + await endPoint.InternalProcessRequest(context); // No need to return ourselves, assume the request is fully handled by the end point context.SetHandled(); @@ -83,6 +77,13 @@ namespace Artemis.Core.Services /// public override bool IsFinalHandler => true; + internal string? ServerUrl { get; set; } + + /// + /// Gets a read only collection containing all current plugin end points + /// + public IReadOnlyCollection PluginEndPoints => new List(_pluginEndPoints.SelectMany(p => p.Value.Values)); + #endregion } } \ No newline at end of file diff --git a/src/Artemis.Core/Services/WebServer/WebServerService.cs b/src/Artemis.Core/Services/WebServer/WebServerService.cs index 13d7ed7fb..79f7959d7 100644 --- a/src/Artemis.Core/Services/WebServer/WebServerService.cs +++ b/src/Artemis.Core/Services/WebServer/WebServerService.cs @@ -16,7 +16,6 @@ namespace Artemis.Core.Services private readonly List _controllers; private readonly IKernel _kernel; private readonly ILogger _logger; - private readonly PluginsModule _pluginModule; private readonly PluginSetting _webServerPortSetting; public WebServerService(IKernel kernel, ILogger logger, ISettingsService settingsService) @@ -28,17 +27,182 @@ namespace Artemis.Core.Services _webServerPortSetting = settingsService.GetSetting("WebServer.Port", 9696); _webServerPortSetting.SettingChanged += WebServerPortSettingOnSettingChanged; - _pluginModule = new PluginsModule("/plugin"); + PluginsModule = new PluginsModule("/plugins"); + + StartWebServer(); + } + + public WebServer? Server { get; private set; } + public PluginsModule PluginsModule { get; } + + #region Web server managament + + private WebServer CreateWebServer() + { + Server?.Dispose(); + Server = null; + + string url = $"http://localhost:{_webServerPortSetting.Value}/"; + WebApiModule apiModule = new("/api/", JsonNetSerializer); + PluginsModule.ServerUrl = url; + WebServer server = new WebServer(o => o.WithUrlPrefix(url).WithMode(HttpListenerMode.EmbedIO)) + .WithLocalSessionManager() + .WithModule(apiModule) + .WithModule(PluginsModule) + .HandleHttpException((context, exception) => HandleHttpExceptionJson(context, exception)) + .HandleUnhandledException(JsonExceptionHandlerCallback); + + // Add built-in core controllers to the API module + apiModule.RegisterController(() => _kernel.Get()); + // Add registered controllers to the API module + foreach (WebApiControllerRegistration registration in _controllers) + apiModule.RegisterController(registration.ControllerType, (Func) registration.UntypedFactory); + + // Listen for state changes. + server.StateChanged += (s, e) => _logger.Verbose("WebServer new state - {state}", e.NewState); + + // Store the URL in a webserver.txt file so that remote applications can find it + File.WriteAllText(Path.Combine(Constants.DataFolder, "webserver.txt"), url); + + return server; + } + + private void StartWebServer() + { Server = CreateWebServer(); + OnWebServerStarting(); Server.Start(); } + #endregion + + #region Plugin endpoint management + + public JsonPluginEndPoint AddJsonEndPoint(PluginFeature feature, string endPointName, Action requestHandler) + { + if (feature == null) throw new ArgumentNullException(nameof(feature)); + if (endPointName == null) throw new ArgumentNullException(nameof(endPointName)); + if (requestHandler == null) throw new ArgumentNullException(nameof(requestHandler)); + JsonPluginEndPoint endPoint = new(feature, endPointName, PluginsModule, requestHandler); + PluginsModule.AddPluginEndPoint(endPoint); + return endPoint; + } + + public JsonPluginEndPoint AddResponsiveJsonEndPoint(PluginFeature feature, string endPointName, Func requestHandler) + { + if (feature == null) throw new ArgumentNullException(nameof(feature)); + if (endPointName == null) throw new ArgumentNullException(nameof(endPointName)); + if (requestHandler == null) throw new ArgumentNullException(nameof(requestHandler)); + JsonPluginEndPoint endPoint = new(feature, endPointName, PluginsModule, requestHandler); + PluginsModule.AddPluginEndPoint(endPoint); + return endPoint; + } + + public StringPluginEndPoint AddStringEndPoint(PluginFeature feature, string endPointName, Action requestHandler) + { + if (feature == null) throw new ArgumentNullException(nameof(feature)); + if (endPointName == null) throw new ArgumentNullException(nameof(endPointName)); + if (requestHandler == null) throw new ArgumentNullException(nameof(requestHandler)); + StringPluginEndPoint endPoint = new(feature, endPointName, PluginsModule, requestHandler); + PluginsModule.AddPluginEndPoint(endPoint); + return endPoint; + } + + public StringPluginEndPoint AddResponsiveStringEndPoint(PluginFeature feature, string endPointName, Func requestHandler) + { + if (feature == null) throw new ArgumentNullException(nameof(feature)); + if (endPointName == null) throw new ArgumentNullException(nameof(endPointName)); + if (requestHandler == null) throw new ArgumentNullException(nameof(requestHandler)); + StringPluginEndPoint endPoint = new(feature, endPointName, PluginsModule, requestHandler); + PluginsModule.AddPluginEndPoint(endPoint); + return endPoint; + } + + public RawPluginEndPoint AddRawEndPoint(PluginFeature feature, string endPointName, Func requestHandler) + { + if (feature == null) throw new ArgumentNullException(nameof(feature)); + if (endPointName == null) throw new ArgumentNullException(nameof(endPointName)); + if (requestHandler == null) throw new ArgumentNullException(nameof(requestHandler)); + RawPluginEndPoint endPoint = new(feature, endPointName, PluginsModule, requestHandler); + PluginsModule.AddPluginEndPoint(endPoint); + return endPoint; + } + + public void RemovePluginEndPoint(PluginEndPoint endPoint) + { + PluginsModule.RemovePluginEndPoint(endPoint); + } + + #endregion + + #region Controller management + + public void AddController() where T : WebApiController + { + _controllers.Add(new WebApiControllerRegistration(_kernel)); + StartWebServer(); + } + + public void RemoveController() where T : WebApiController + { + _controllers.RemoveAll(r => r.ControllerType == typeof(T)); + StartWebServer(); + } + + #endregion + + #region Handlers + + private async Task JsonExceptionHandlerCallback(IHttpContext context, Exception exception) + { + context.Response.ContentType = MimeType.Json; + await using TextWriter writer = context.OpenResponseText(); + + string response = JsonConvert.SerializeObject(new Dictionary() + { + {"StatusCode", context.Response.StatusCode}, + {"StackTrace", exception.StackTrace}, + {"Type", exception.GetType().FullName}, + {"Message", exception.Message}, + {"Data", exception.Data}, + {"InnerException", exception.InnerException}, + {"HelpLink", exception.HelpLink}, + {"Source", exception.Source}, + {"HResult", exception.HResult} + }); + await writer.WriteAsync(response); + } + + private async Task JsonNetSerializer(IHttpContext context, object? data) + { + context.Response.ContentType = MimeType.Json; + await using TextWriter writer = context.OpenResponseText(); + await writer.WriteAsync(JsonConvert.SerializeObject(data)); + } + + private async Task HandleHttpExceptionJson(IHttpContext context, IHttpException httpException) + { + await context.SendStringAsync(JsonConvert.SerializeObject(httpException, Formatting.Indented), MimeType.Json, Encoding.UTF8); + } + + #endregion + + #region Events + + public event EventHandler? WebServerStarting; + + protected virtual void OnWebServerStarting() + { + WebServerStarting?.Invoke(this, EventArgs.Empty); + } + + #endregion + #region Event handlers private void WebServerPortSettingOnSettingChanged(object? sender, EventArgs e) { - Server = CreateWebServer(); - Server.Start(); + StartWebServer(); } #endregion @@ -53,131 +217,5 @@ namespace Artemis.Core.Services } #endregion - - public WebServer? Server { get; private set; } - - #region Web server managament - - private WebServer CreateWebServer() - { - Server?.Dispose(); - Server = null; - - string url = $"http://localhost:{_webServerPortSetting.Value}/"; - WebApiModule apiModule = new("/api/"); - WebServer server = new WebServer(o => o.WithUrlPrefix(url).WithMode(HttpListenerMode.EmbedIO)) - .WithLocalSessionManager() - .WithModule(apiModule) - .WithModule(_pluginModule) - .HandleHttpException((context, exception) => HandleHttpExceptionJson(context, exception)); - - // Add controllers to the API module - foreach (WebApiControllerRegistration registration in _controllers) - apiModule.RegisterController(registration.ControllerType, (Func) registration.UntypedFactory); - - // Listen for state changes. - server.StateChanged += (s, e) => _logger.Verbose("WebServer new state - {state}", e.NewState); - - // Store the URL in a webserver.txt file so that remote applications can find it - File.WriteAllText(Path.Combine(Constants.DataFolder, "webserver.txt"), url); - OnWebServerCreated(); - - return server; - } - - private async Task HandleHttpExceptionJson(IHttpContext context, IHttpException httpException) - { - await context.SendStringAsync(JsonConvert.SerializeObject(httpException, Formatting.Indented), MimeType.Json, Encoding.UTF8); - } - - #endregion - - #region Plugin endpoint management - - public JsonPluginEndPoint AddJsonEndPoint(PluginFeature feature, string endPointName, Action requestHandler) - { - if (feature == null) throw new ArgumentNullException(nameof(feature)); - if (endPointName == null) throw new ArgumentNullException(nameof(endPointName)); - if (requestHandler == null) throw new ArgumentNullException(nameof(requestHandler)); - JsonPluginEndPoint endPoint = new(feature, endPointName, _pluginModule, requestHandler); - _pluginModule.AddPluginEndPoint(endPoint); - return endPoint; - } - - public JsonPluginEndPoint AddResponsiveJsonEndPoint(PluginFeature feature, string endPointName, Func requestHandler) - { - if (feature == null) throw new ArgumentNullException(nameof(feature)); - if (endPointName == null) throw new ArgumentNullException(nameof(endPointName)); - if (requestHandler == null) throw new ArgumentNullException(nameof(requestHandler)); - JsonPluginEndPoint endPoint = new(feature, endPointName, _pluginModule, requestHandler); - _pluginModule.AddPluginEndPoint(endPoint); - return endPoint; - } - - public StringPluginEndPoint AddStringEndPoint(PluginFeature feature, string endPointName, Action requestHandler) - { - if (feature == null) throw new ArgumentNullException(nameof(feature)); - if (endPointName == null) throw new ArgumentNullException(nameof(endPointName)); - if (requestHandler == null) throw new ArgumentNullException(nameof(requestHandler)); - StringPluginEndPoint endPoint = new(feature, endPointName, _pluginModule, requestHandler); - _pluginModule.AddPluginEndPoint(endPoint); - return endPoint; - } - - public StringPluginEndPoint AddResponsiveStringEndPoint(PluginFeature feature, string endPointName, Func requestHandler) - { - if (feature == null) throw new ArgumentNullException(nameof(feature)); - if (endPointName == null) throw new ArgumentNullException(nameof(endPointName)); - if (requestHandler == null) throw new ArgumentNullException(nameof(requestHandler)); - StringPluginEndPoint endPoint = new(feature, endPointName, _pluginModule, requestHandler); - _pluginModule.AddPluginEndPoint(endPoint); - return endPoint; - } - - public RawPluginEndPoint AddRawEndPoint(PluginFeature feature, string endPointName, Action requestHandler) - { - if (feature == null) throw new ArgumentNullException(nameof(feature)); - if (endPointName == null) throw new ArgumentNullException(nameof(endPointName)); - if (requestHandler == null) throw new ArgumentNullException(nameof(requestHandler)); - RawPluginEndPoint endPoint = new(feature, endPointName, _pluginModule, requestHandler); - _pluginModule.AddPluginEndPoint(endPoint); - return endPoint; - } - - public void RemovePluginEndPoint(PluginEndPoint endPoint) - { - _pluginModule.RemovePluginEndPoint(endPoint); - } - - #endregion - - #region Controller management - - public void AddController() where T : WebApiController - { - _controllers.Add(new WebApiControllerRegistration(_kernel)); - Server = CreateWebServer(); - Server.Start(); - } - - public void RemoveController() where T : WebApiController - { - _controllers.RemoveAll(r => r.ControllerType == typeof(T)); - Server = CreateWebServer(); - Server.Start(); - } - - #endregion - - #region Events - - public event EventHandler? WebServerCreated; - - protected virtual void OnWebServerCreated() - { - WebServerCreated?.Invoke(this, EventArgs.Empty); - } - - #endregion } } \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Settings/Tabs/General/GeneralSettingsTabView.xaml b/src/Artemis.UI/Screens/Settings/Tabs/General/GeneralSettingsTabView.xaml index ac964893d..f7c14eebf 100644 --- a/src/Artemis.UI/Screens/Settings/Tabs/General/GeneralSettingsTabView.xaml +++ b/src/Artemis.UI/Screens/Settings/Tabs/General/GeneralSettingsTabView.xaml @@ -171,7 +171,7 @@ - + @@ -195,6 +195,33 @@ + + Web server + + + + + + + + + + + + + Web server port + + Artemis runs a local web server that can be used to externally interact with the application. + This web server can only be accessed by applications running on your own computer, e.g. supported games. + + + + + + + + + Updating diff --git a/src/Artemis.UI/Screens/Settings/Tabs/General/GeneralSettingsTabViewModel.cs b/src/Artemis.UI/Screens/Settings/Tabs/General/GeneralSettingsTabViewModel.cs index 4a304194b..9318f27f8 100644 --- a/src/Artemis.UI/Screens/Settings/Tabs/General/GeneralSettingsTabViewModel.cs +++ b/src/Artemis.UI/Screens/Settings/Tabs/General/GeneralSettingsTabViewModel.cs @@ -79,6 +79,9 @@ namespace Artemis.UI.Screens.Settings.Tabs.General LayerBrushProviderId = "Artemis.Plugins.LayerBrushes.Color.ColorBrushProvider-92a9d6ba", BrushType = "ColorBrush" }); + + WebServerPortSetting = _settingsService.GetSetting("WebServer.Port", 9696); + WebServerPortSetting.AutoSave = true; } public BindableCollection LayerBrushDescriptors { get; } @@ -234,6 +237,8 @@ namespace Artemis.UI.Screens.Settings.Tabs.General } } + public PluginSetting WebServerPortSetting { get; } + public bool CanOfferUpdatesIfFound { get => _canOfferUpdatesIfFound;