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;