1
0
mirror of https://github.com/Artemis-RGB/Artemis synced 2025-12-12 21:38:38 +00:00

Finish implementing GenHTTP

This commit is contained in:
Robert 2025-02-13 21:35:35 +01:00
parent e09389c6db
commit eb4a6ceafb
12 changed files with 122 additions and 184 deletions

View File

@ -38,7 +38,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="DryIoc.dll" /> <PackageReference Include="DryIoc.dll" />
<PackageReference Include="GenHTTP.Core" /> <PackageReference Include="GenHTTP.Core" />
<PackageReference Include="GenHTTP.Modules.Controllers" /> <PackageReference Include="GenHTTP.Modules.Webservices" />
<PackageReference Include="HidSharp" /> <PackageReference Include="HidSharp" />
<PackageReference Include="HPPH.SkiaSharp" /> <PackageReference Include="HPPH.SkiaSharp" />
<PackageReference Include="Humanizer.Core" /> <PackageReference Include="Humanizer.Core" />

View File

@ -1,84 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
using Artemis.Core.Modules;
using GenHTTP.Api.Protocol;
namespace Artemis.Core.Services;
/// <summary>
/// Represents a plugin web endpoint receiving an object of type <typeparamref name="T" /> and returning any
/// <see cref="object" /> or <see langword="null" />.
/// <para>Note: Both will be deserialized and serialized respectively using JSON.</para>
/// </summary>
public class DataModelJsonPluginEndPoint<T> : PluginEndPoint where T : DataModel, new()
{
private readonly Module<T> _module;
private readonly Action<T, T> _update;
internal DataModelJsonPluginEndPoint(Module<T> module, string name, PluginsModule pluginsModule) : base(module, name, pluginsModule)
{
_module = module ?? throw new ArgumentNullException(nameof(module));
_update = CreateUpdateAction();
ThrowOnFail = true;
Accepts = ContentType.ApplicationJson;
}
/// <summary>
/// Whether or not the end point should throw an exception if deserializing the received JSON fails.
/// If set to <see langword="false" /> malformed JSON is silently ignored; if set to <see langword="true" /> malformed
/// JSON throws a <see cref="JsonException" />.
/// </summary>
public bool ThrowOnFail { get; set; }
/// <inheritdoc />
protected override async Task<IResponse> ProcessRequest(IRequest request)
{
if (request.Method != RequestMethod.Post && request.Method != RequestMethod.Put)
return request.Respond().Status(ResponseStatus.MethodNotAllowed).Build();
if (request.Content == null)
return request.Respond().Status(ResponseStatus.BadRequest).Build();
try
{
T? dataModel = await JsonSerializer.DeserializeAsync<T>(request.Content, WebServerService.JsonOptions);
if (dataModel != null)
_update(dataModel, _module.DataModel);
}
catch (JsonException)
{
if (ThrowOnFail)
throw;
}
return request.Respond().Status(ResponseStatus.NoContent).Build();
}
private Action<T, T> CreateUpdateAction()
{
ParameterExpression sourceParameter = Expression.Parameter(typeof(T), "source");
ParameterExpression targetParameter = Expression.Parameter(typeof(T), "target");
IEnumerable<BinaryExpression> assignments = typeof(T)
.GetProperties()
.Where(prop => prop.CanWrite && prop.GetSetMethod() != null &&
prop.GetSetMethod()!.IsPublic &&
!prop.IsDefined(typeof(JsonIgnoreAttribute), false) &&
!prop.PropertyType.IsAssignableTo(typeof(IDataModelEvent)))
.Select(prop =>
{
MemberExpression sourceProperty = Expression.Property(sourceParameter, prop);
MemberExpression targetProperty = Expression.Property(targetParameter, prop);
return Expression.Assign(targetProperty, sourceProperty);
});
BlockExpression body = Expression.Block(assignments);
return Expression.Lambda<Action<T, T>>(body, sourceParameter, targetParameter).Compile();
}
}

View File

@ -17,19 +17,19 @@ public class JsonPluginEndPoint<T> : PluginEndPoint
private readonly Action<T>? _requestHandler; private readonly Action<T>? _requestHandler;
private readonly Func<T, object?>? _responseRequestHandler; private readonly Func<T, object?>? _responseRequestHandler;
internal JsonPluginEndPoint(PluginFeature pluginFeature, string name, PluginsModule pluginsModule, Action<T> requestHandler) : base(pluginFeature, name, pluginsModule) internal JsonPluginEndPoint(PluginFeature pluginFeature, string name, PluginsHandler pluginsHandler, Action<T> requestHandler) : base(pluginFeature, name, pluginsHandler)
{ {
_requestHandler = requestHandler; _requestHandler = requestHandler;
ThrowOnFail = true; ThrowOnFail = true;
Accepts = ContentType.ApplicationJson; Accepts = FlexibleContentType.Get(ContentType.ApplicationJson);
} }
internal JsonPluginEndPoint(PluginFeature pluginFeature, string name, PluginsModule pluginsModule, Func<T, object?> responseRequestHandler) : base(pluginFeature, name, pluginsModule) internal JsonPluginEndPoint(PluginFeature pluginFeature, string name, PluginsHandler pluginsHandler, Func<T, object?> responseRequestHandler) : base(pluginFeature, name, pluginsHandler)
{ {
_responseRequestHandler = responseRequestHandler; _responseRequestHandler = responseRequestHandler;
ThrowOnFail = true; ThrowOnFail = true;
Accepts = ContentType.ApplicationJson; Accepts = FlexibleContentType.Get(ContentType.ApplicationJson);
Returns = ContentType.ApplicationJson; Returns = FlexibleContentType.Get(ContentType.ApplicationJson);
} }
/// <summary> /// <summary>

View File

@ -10,15 +10,15 @@ using StringContent = GenHTTP.Modules.IO.Strings.StringContent;
namespace Artemis.Core.Services; namespace Artemis.Core.Services;
/// <summary> /// <summary>
/// Represents a base type for plugin end points to be targeted by the <see cref="PluginsModule" /> /// Represents a base type for plugin end points to be targeted by the <see cref="PluginsHandler" />
/// </summary> /// </summary>
public abstract class PluginEndPoint public abstract class PluginEndPoint
{ {
private readonly PluginsModule _pluginsModule; private readonly PluginsHandler _pluginsHandler;
internal PluginEndPoint(PluginFeature pluginFeature, string name, PluginsModule pluginsModule) internal PluginEndPoint(PluginFeature pluginFeature, string name, PluginsHandler pluginsHandler)
{ {
_pluginsModule = pluginsModule; _pluginsHandler = pluginsHandler;
PluginFeature = pluginFeature; PluginFeature = pluginFeature;
Name = name; Name = name;
@ -33,7 +33,7 @@ public abstract class PluginEndPoint
/// <summary> /// <summary>
/// Gets the full URL of the end point /// Gets the full URL of the end point
/// </summary> /// </summary>
public string Url => $"{_pluginsModule.ServerUrl?.TrimEnd('/')}{_pluginsModule.BaseRoute}{PluginFeature.Plugin.Guid}/{Name}"; public string Url => $"{_pluginsHandler.ServerUrl}{_pluginsHandler.BaseRoute}/{PluginFeature.Plugin.Guid}/{Name}";
/// <summary> /// <summary>
/// Gets the plugin the end point is associated with /// Gets the plugin the end point is associated with
@ -46,15 +46,15 @@ public abstract class PluginEndPoint
/// </summary> /// </summary>
public PluginInfo PluginInfo => PluginFeature.Plugin.Info; public PluginInfo PluginInfo => PluginFeature.Plugin.Info;
/// <summary> /// <summary><summary>
/// Gets the mime type of the input this end point accepts /// Gets the mime type of the input this end point accepts
/// </summary> /// </summary>
public ContentType Accepts { get; protected set; } public FlexibleContentType Accepts { get; protected set; }
/// <summary> /// <summary>
/// Gets the mime type of the output this end point returns /// Gets the mime type of the output this end point returns
/// </summary> /// </summary>
public ContentType Returns { get; protected set; } public FlexibleContentType Returns { get; protected set; }
/// <summary> /// <summary>
/// Occurs whenever a request threw an unhandled exception /// Occurs whenever a request threw an unhandled exception
@ -107,6 +107,13 @@ public abstract class PluginEndPoint
try try
{ {
OnProcessingRequest(context); OnProcessingRequest(context);
if (!Equals(context.ContentType, Accepts))
{
OnRequestException(new Exception("Unsupported media type"));
return context.Respond().Status(ResponseStatus.UnsupportedMediaType).Build();
}
IResponse response = await ProcessRequest(context); IResponse response = await ProcessRequest(context);
OnProcessedRequest(context); OnProcessedRequest(context);
return response; return response;
@ -125,6 +132,6 @@ public abstract class PluginEndPoint
private void OnDisabled(object? sender, EventArgs e) private void OnDisabled(object? sender, EventArgs e)
{ {
PluginFeature.Disabled -= OnDisabled; PluginFeature.Disabled -= OnDisabled;
_pluginsModule.RemovePluginEndPoint(this); _pluginsHandler.RemovePluginEndPoint(this);
} }
} }

View File

@ -14,7 +14,7 @@ namespace Artemis.Core.Services;
public class RawPluginEndPoint : PluginEndPoint public class RawPluginEndPoint : PluginEndPoint
{ {
/// <inheritdoc /> /// <inheritdoc />
internal RawPluginEndPoint(PluginFeature pluginFeature, string name, PluginsModule pluginsModule, Func<IRequest, Task<IResponse>> requestHandler) : base(pluginFeature, name, pluginsModule) internal RawPluginEndPoint(PluginFeature pluginFeature, string name, PluginsHandler pluginsHandler, Func<IRequest, Task<IResponse>> requestHandler) : base(pluginFeature, name, pluginsHandler)
{ {
RequestHandler = requestHandler; RequestHandler = requestHandler;
} }
@ -29,7 +29,7 @@ public class RawPluginEndPoint : PluginEndPoint
/// </summary> /// </summary>
public void SetAcceptType(ContentType type) public void SetAcceptType(ContentType type)
{ {
Accepts = type; Accepts = FlexibleContentType.Get(type);
} }
/// <summary> /// <summary>
@ -37,7 +37,7 @@ public class RawPluginEndPoint : PluginEndPoint
/// </summary> /// </summary>
public void SetReturnType(ContentType type) public void SetReturnType(ContentType type)
{ {
Returns = type; Returns = FlexibleContentType.Get(type);
} }
#region Overrides of PluginEndPoint #region Overrides of PluginEndPoint

View File

@ -16,17 +16,17 @@ public class StringPluginEndPoint : PluginEndPoint
private readonly Action<string>? _requestHandler; private readonly Action<string>? _requestHandler;
private readonly Func<string, string?>? _responseRequestHandler; private readonly Func<string, string?>? _responseRequestHandler;
internal StringPluginEndPoint(PluginFeature pluginFeature, string name, PluginsModule pluginsModule, Action<string> requestHandler) : base(pluginFeature, name, pluginsModule) internal StringPluginEndPoint(PluginFeature pluginFeature, string name, PluginsHandler pluginsHandler, Action<string> requestHandler) : base(pluginFeature, name, pluginsHandler)
{ {
_requestHandler = requestHandler; _requestHandler = requestHandler;
Accepts = ContentType.TextPlain; Accepts = FlexibleContentType.Get(ContentType.TextPlain);
} }
internal StringPluginEndPoint(PluginFeature pluginFeature, string name, PluginsModule pluginsModule, Func<string, string?> requestHandler) : base(pluginFeature, name, pluginsModule) internal StringPluginEndPoint(PluginFeature pluginFeature, string name, PluginsHandler pluginsHandler, Func<string, string?> requestHandler) : base(pluginFeature, name, pluginsHandler)
{ {
_responseRequestHandler = requestHandler; _responseRequestHandler = requestHandler;
Accepts = ContentType.TextPlain; Accepts = FlexibleContentType.Get(ContentType.TextPlain);
Returns = ContentType.TextPlain; Returns = FlexibleContentType.Get(ContentType.TextPlain);
} }
#region Overrides of PluginEndPoint #region Overrides of PluginEndPoint

View File

@ -19,7 +19,7 @@ public interface IWebServerService : IArtemisService
/// <summary> /// <summary>
/// Gets the plugins module containing all plugin end points /// Gets the plugins module containing all plugin end points
/// </summary> /// </summary>
PluginsModule PluginsModule { get; } PluginsHandler PluginsHandler { get; }
/// <summary> /// <summary>
/// Adds a new endpoint for the given plugin feature receiving an object of type <typeparamref name="T" /> /// Adds a new endpoint for the given plugin feature receiving an object of type <typeparamref name="T" />
@ -44,16 +44,6 @@ public interface IWebServerService : IArtemisService
/// <returns>The resulting end point</returns> /// <returns>The resulting end point</returns>
JsonPluginEndPoint<T> AddResponsiveJsonEndPoint<T>(PluginFeature feature, string endPointName, Func<T, object?> requestHandler); JsonPluginEndPoint<T> AddResponsiveJsonEndPoint<T>(PluginFeature feature, string endPointName, Func<T, object?> requestHandler);
/// <summary>
/// Adds a new endpoint that directly maps received JSON to the data model of the provided <paramref name="module" />.
/// </summary>
/// <typeparam name="T">The data model type of the module</typeparam>
/// <param name="module">The module whose datamodel to apply the received JSON to</param>
/// <param name="endPointName">The name of the end point, must be unique</param>
/// <returns>The resulting end point</returns>
[Obsolete("This way of updating is too unpredictable in combination with nested events, use AddJsonEndPoint<T> to update manually instead")]
DataModelJsonPluginEndPoint<T> AddDataModelJsonEndPoint<T>(Module<T> module, string endPointName) where T : DataModel, new();
/// <summary> /// <summary>
/// Adds a new endpoint for the given plugin feature receiving an a <see cref="string" />. /// Adds a new endpoint for the given plugin feature receiving an a <see cref="string" />.
/// </summary> /// </summary>
@ -95,7 +85,7 @@ public interface IWebServerService : IArtemisService
/// <summary> /// <summary>
/// Adds a new Web API controller and restarts the web server /// Adds a new Web API controller and restarts the web server
/// </summary> /// </summary>
/// <typeparam name="T">The type of Web API controller to remove</typeparam> /// <typeparam name="T">The type of Web API controller to add</typeparam>
WebApiControllerRegistration AddController<T>(PluginFeature feature, string path) where T : class; WebApiControllerRegistration AddController<T>(PluginFeature feature, string path) where T : class;
/// <summary> /// <summary>

View File

@ -12,14 +12,14 @@ using GenHTTP.Modules.IO.Strings;
namespace Artemis.Core.Services; namespace Artemis.Core.Services;
/// <summary> /// <summary>
/// Represents an EmbedIO web module used to process web requests and forward them to the right /// Represents an GenHTTP handler used to process web requests and forward them to the right
/// <see cref="PluginEndPoint" />. /// <see cref="PluginEndPoint" />.
/// </summary> /// </summary>
public class PluginsModule : IHandler public class PluginsHandler : IHandler
{ {
private readonly Dictionary<string, Dictionary<string, PluginEndPoint>> _pluginEndPoints; private readonly Dictionary<string, Dictionary<string, PluginEndPoint>> _pluginEndPoints;
internal PluginsModule(string baseRoute) internal PluginsHandler(string baseRoute)
{ {
BaseRoute = baseRoute; BaseRoute = baseRoute;
_pluginEndPoints = new Dictionary<string, Dictionary<string, PluginEndPoint>>(comparer: StringComparer.InvariantCultureIgnoreCase); _pluginEndPoints = new Dictionary<string, Dictionary<string, PluginEndPoint>>(comparer: StringComparer.InvariantCultureIgnoreCase);
@ -63,20 +63,30 @@ public class PluginsModule : IHandler
/// <inheritdoc /> /// <inheritdoc />
public async ValueTask<IResponse?> HandleAsync(IRequest request) public async ValueTask<IResponse?> HandleAsync(IRequest request)
{ {
// Expect a plugin ID and an endpoint // Used to be part of the RemoteController but moved here to avoid the /remote/ prefix enforced by GenHTTP
if (request.Target.Path.Parts.Count != 2) if (request.Target.Current?.Value != "plugins")
return null;
request.Target.Advance();
string? pluginId = request.Target.Current?.Value;
if (pluginId == null)
return null; return null;
// Find a matching plugin, if none found let another handler have a go :) // Find a matching plugin, if none found let another handler have a go :)
if (!_pluginEndPoints.TryGetValue(request.Target.Path.Parts[0].Value, out Dictionary<string, PluginEndPoint>? endPoints)) if (!_pluginEndPoints.TryGetValue(pluginId, out Dictionary<string, PluginEndPoint>? endPoints))
return null; return null;
request.Target.Advance();
string? endPointName = request.Target.Current?.Value;
if (endPointName == null)
return null;
// Find a matching endpoint // Find a matching endpoint
if (!endPoints.TryGetValue(request.Target.Path.Parts[1].Value, out PluginEndPoint? endPoint)) if (!endPoints.TryGetValue(endPointName, out PluginEndPoint? endPoint))
{ {
return request.Respond() return request.Respond()
.Status(ResponseStatus.NotFound) .Status(ResponseStatus.NotFound)
.Content(new StringContent($"Found no endpoint called {request.Target.Path.Parts[1].Value} for plugin with ID {request.Target.Path.Parts[0].Value}.")) .Content(new StringContent($"Found no endpoint called {endPointName} for plugin with ID {pluginId}."))
.Type(ContentType.TextPlain) .Type(ContentType.TextPlain)
.Build(); .Build();
} }

View File

@ -0,0 +1,27 @@
using System.Threading.Tasks;
using GenHTTP.Api.Content;
using GenHTTP.Api.Protocol;
namespace Artemis.Core.Services;
/// <summary>
/// Represents an GenHTTP handler used to process web requests and forward them to the right
/// <see cref="PluginEndPoint" />.
/// </summary>
public class StatusHandler : IHandler
{
/// <inheritdoc />
public ValueTask PrepareAsync()
{
return ValueTask.CompletedTask;
}
/// <inheritdoc />
public ValueTask<IResponse?> HandleAsync(IRequest request)
{
// Used to be part of the RemoteController but moved here to avoid the /remote/ prefix enforced by GenHTTP
return request.Target.Current?.Value == "status"
? ValueTask.FromResult<IResponse?>(request.Respond().Status(ResponseStatus.NoContent).Build())
: ValueTask.FromResult<IResponse?>(null);
}
}

View File

@ -3,20 +3,21 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using System.Text;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Artemis.Core.Modules;
using GenHTTP.Api.Infrastructure; using GenHTTP.Api.Infrastructure;
using GenHTTP.Api.Protocol; using GenHTTP.Api.Protocol;
using GenHTTP.Engine.Internal; using GenHTTP.Engine.Internal;
using GenHTTP.Modules.Controllers; using GenHTTP.Modules.Conversion;
using GenHTTP.Modules.Conversion.Serializers;
using GenHTTP.Modules.ErrorHandling; using GenHTTP.Modules.ErrorHandling;
using GenHTTP.Modules.Layouting; using GenHTTP.Modules.Layouting;
using GenHTTP.Modules.Layouting.Provider; using GenHTTP.Modules.Layouting.Provider;
using GenHTTP.Modules.Practices;
using GenHTTP.Modules.Security; using GenHTTP.Modules.Security;
using GenHTTP.Modules.Webservices;
using Serilog; using Serilog;
namespace Artemis.Core.Services; namespace Artemis.Core.Services;
@ -29,7 +30,13 @@ internal class WebServerService : IWebServerService, IDisposable
private readonly PluginSetting<bool> _webServerEnabledSetting; private readonly PluginSetting<bool> _webServerEnabledSetting;
private readonly PluginSetting<int> _webServerPortSetting; private readonly PluginSetting<int> _webServerPortSetting;
private readonly SemaphoreSlim _webserverSemaphore = new(1, 1); private readonly SemaphoreSlim _webserverSemaphore = new(1, 1);
internal static readonly JsonSerializerOptions JsonOptions = new(CoreJson.GetJsonSerializerOptions()) {ReferenceHandler = ReferenceHandler.IgnoreCycles, WriteIndented = true};
internal static readonly JsonSerializerOptions JsonOptions = new(CoreJson.GetJsonSerializerOptions())
{
ReferenceHandler = ReferenceHandler.IgnoreCycles,
WriteIndented = true,
Converters = {new JsonStringEnumConverter(JsonNamingPolicy.CamelCase)}
};
public WebServerService(ILogger logger, ICoreService coreService, ISettingsService settingsService, IPluginManagementService pluginManagementService) public WebServerService(ILogger logger, ICoreService coreService, ISettingsService settingsService, IPluginManagementService pluginManagementService)
{ {
@ -43,7 +50,7 @@ internal class WebServerService : IWebServerService, IDisposable
_webServerPortSetting.SettingChanged += WebServerPortSettingOnSettingChanged; _webServerPortSetting.SettingChanged += WebServerPortSettingOnSettingChanged;
pluginManagementService.PluginFeatureDisabled += PluginManagementServiceOnPluginFeatureDisabled; pluginManagementService.PluginFeatureDisabled += PluginManagementServiceOnPluginFeatureDisabled;
PluginsModule = new PluginsModule("/plugins"); PluginsHandler = new PluginsHandler("plugins");
if (coreService.IsInitialized) if (coreService.IsInitialized)
AutoStartWebServer(); AutoStartWebServer();
else else
@ -86,7 +93,7 @@ internal class WebServerService : IWebServerService, IDisposable
mustRestart = true; mustRestart = true;
_controllers.RemoveAll(c => c.Feature == e.PluginFeature); _controllers.RemoveAll(c => c.Feature == e.PluginFeature);
} }
if (mustRestart) if (mustRestart)
_ = StartWebServer(); _ = StartWebServer();
} }
@ -99,13 +106,13 @@ internal class WebServerService : IWebServerService, IDisposable
} }
public IServer? Server { get; private set; } public IServer? Server { get; private set; }
public PluginsModule PluginsModule { get; } public PluginsHandler PluginsHandler { get; }
public event EventHandler? WebServerStarting; public event EventHandler? WebServerStarting;
#region Web server managament #region Web server managament
private async Task <IServer> CreateWebServer() private async Task<IServer> CreateWebServer()
{ {
if (Server != null) if (Server != null)
{ {
@ -114,27 +121,29 @@ internal class WebServerService : IWebServerService, IDisposable
Server = null; Server = null;
} }
PluginsModule.ServerUrl = $"http://localhost:{_webServerPortSetting.Value}/"; PluginsHandler.ServerUrl = $"http://localhost:{_webServerPortSetting.Value}/";
LayoutBuilder serverLayout = Layout.Create() LayoutBuilder serverLayout = Layout.Create()
.Add(PluginsModule) .Add(PluginsHandler)
.Add(ErrorHandler.Structured()) .Add(ErrorHandler.Structured())
.Add(CorsPolicy.Permissive()); .Add(CorsPolicy.Permissive());
// Add registered controllers to the API module // Add registered controllers to the API module as services.
// GenHTTP also has controllers but services are more flexible and match EmbedIO's approach more closely.
SerializationBuilder serialization = Serialization.Default(JsonOptions);
foreach (WebApiControllerRegistration registration in _controllers) foreach (WebApiControllerRegistration registration in _controllers)
{ {
serverLayout = serverLayout.Add(registration.Path, Controller.From(registration.Factory())); serverLayout = serverLayout.AddService(registration.Path, registration.Factory(), serializers: serialization);
} }
IServer server = Host.Create() IServer server = Host.Create()
.Handler(serverLayout.Build()) .Handler(serverLayout.Build())
.Bind(IPAddress.Loopback, (ushort) _webServerPortSetting.Value) .Bind(IPAddress.Loopback, (ushort) _webServerPortSetting.Value)
.Development() .Defaults()
.Build(); .Build();
// Store the URL in a webserver.txt file so that remote applications can find it // Store the URL in a webserver.txt file so that remote applications can find it
await File.WriteAllTextAsync(Path.Combine(Constants.DataFolder, "webserver.txt"), PluginsModule.ServerUrl); await File.WriteAllTextAsync(Path.Combine(Constants.DataFolder, "webserver.txt"), PluginsHandler.ServerUrl);
return server; return server;
} }
@ -169,6 +178,7 @@ internal class WebServerService : IWebServerService, IDisposable
_logger.Warning(e, "Failed to start webserver"); _logger.Warning(e, "Failed to start webserver");
throw; throw;
} }
OnWebServerStarted(); OnWebServerStarted();
} }
finally finally
@ -198,8 +208,8 @@ internal class WebServerService : IWebServerService, IDisposable
if (feature == null) throw new ArgumentNullException(nameof(feature)); if (feature == null) throw new ArgumentNullException(nameof(feature));
if (endPointName == null) throw new ArgumentNullException(nameof(endPointName)); if (endPointName == null) throw new ArgumentNullException(nameof(endPointName));
if (requestHandler == null) throw new ArgumentNullException(nameof(requestHandler)); if (requestHandler == null) throw new ArgumentNullException(nameof(requestHandler));
JsonPluginEndPoint<T> endPoint = new(feature, endPointName, PluginsModule, requestHandler); JsonPluginEndPoint<T> endPoint = new(feature, endPointName, PluginsHandler, requestHandler);
PluginsModule.AddPluginEndPoint(endPoint); PluginsHandler.AddPluginEndPoint(endPoint);
return endPoint; return endPoint;
} }
@ -208,8 +218,8 @@ internal class WebServerService : IWebServerService, IDisposable
if (feature == null) throw new ArgumentNullException(nameof(feature)); if (feature == null) throw new ArgumentNullException(nameof(feature));
if (endPointName == null) throw new ArgumentNullException(nameof(endPointName)); if (endPointName == null) throw new ArgumentNullException(nameof(endPointName));
if (requestHandler == null) throw new ArgumentNullException(nameof(requestHandler)); if (requestHandler == null) throw new ArgumentNullException(nameof(requestHandler));
JsonPluginEndPoint<T> endPoint = new(feature, endPointName, PluginsModule, requestHandler); JsonPluginEndPoint<T> endPoint = new(feature, endPointName, PluginsHandler, requestHandler);
PluginsModule.AddPluginEndPoint(endPoint); PluginsHandler.AddPluginEndPoint(endPoint);
return endPoint; return endPoint;
} }
@ -218,8 +228,8 @@ internal class WebServerService : IWebServerService, IDisposable
if (feature == null) throw new ArgumentNullException(nameof(feature)); if (feature == null) throw new ArgumentNullException(nameof(feature));
if (endPointName == null) throw new ArgumentNullException(nameof(endPointName)); if (endPointName == null) throw new ArgumentNullException(nameof(endPointName));
if (requestHandler == null) throw new ArgumentNullException(nameof(requestHandler)); if (requestHandler == null) throw new ArgumentNullException(nameof(requestHandler));
StringPluginEndPoint endPoint = new(feature, endPointName, PluginsModule, requestHandler); StringPluginEndPoint endPoint = new(feature, endPointName, PluginsHandler, requestHandler);
PluginsModule.AddPluginEndPoint(endPoint); PluginsHandler.AddPluginEndPoint(endPoint);
return endPoint; return endPoint;
} }
@ -228,8 +238,8 @@ internal class WebServerService : IWebServerService, IDisposable
if (feature == null) throw new ArgumentNullException(nameof(feature)); if (feature == null) throw new ArgumentNullException(nameof(feature));
if (endPointName == null) throw new ArgumentNullException(nameof(endPointName)); if (endPointName == null) throw new ArgumentNullException(nameof(endPointName));
if (requestHandler == null) throw new ArgumentNullException(nameof(requestHandler)); if (requestHandler == null) throw new ArgumentNullException(nameof(requestHandler));
StringPluginEndPoint endPoint = new(feature, endPointName, PluginsModule, requestHandler); StringPluginEndPoint endPoint = new(feature, endPointName, PluginsHandler, requestHandler);
PluginsModule.AddPluginEndPoint(endPoint); PluginsHandler.AddPluginEndPoint(endPoint);
return endPoint; return endPoint;
} }
@ -238,26 +248,16 @@ internal class WebServerService : IWebServerService, IDisposable
if (feature == null) throw new ArgumentNullException(nameof(feature)); if (feature == null) throw new ArgumentNullException(nameof(feature));
if (endPointName == null) throw new ArgumentNullException(nameof(endPointName)); if (endPointName == null) throw new ArgumentNullException(nameof(endPointName));
if (requestHandler == null) throw new ArgumentNullException(nameof(requestHandler)); if (requestHandler == null) throw new ArgumentNullException(nameof(requestHandler));
RawPluginEndPoint endPoint = new(feature, endPointName, PluginsModule, requestHandler); RawPluginEndPoint endPoint = new(feature, endPointName, PluginsHandler, requestHandler);
PluginsModule.AddPluginEndPoint(endPoint); PluginsHandler.AddPluginEndPoint(endPoint);
return endPoint;
}
[Obsolete("Use AddJsonEndPoint<T>(PluginFeature feature, string endPointName, Action<T> requestHandler) instead")]
public DataModelJsonPluginEndPoint<T> AddDataModelJsonEndPoint<T>(Module<T> module, string endPointName) where T : DataModel, new()
{
if (module == null) throw new ArgumentNullException(nameof(module));
if (endPointName == null) throw new ArgumentNullException(nameof(endPointName));
DataModelJsonPluginEndPoint<T> endPoint = new(module, endPointName, PluginsModule);
PluginsModule.AddPluginEndPoint(endPoint);
return endPoint; return endPoint;
} }
public void RemovePluginEndPoint(PluginEndPoint endPoint) public void RemovePluginEndPoint(PluginEndPoint endPoint)
{ {
PluginsModule.RemovePluginEndPoint(endPoint); PluginsHandler.RemovePluginEndPoint(endPoint);
} }
#endregion #endregion
#region Controller management #region Controller management

View File

@ -7,8 +7,7 @@ using Artemis.UI.Shared.Routing;
using Artemis.UI.Shared.Services.MainWindow; using Artemis.UI.Shared.Services.MainWindow;
using Avalonia.Threading; using Avalonia.Threading;
using GenHTTP.Api.Protocol; using GenHTTP.Api.Protocol;
using GenHTTP.Modules.Controllers; using GenHTTP.Modules.Webservices;
using GenHTTP.Modules.Reflection;
namespace Artemis.UI.Controllers; namespace Artemis.UI.Controllers;
@ -25,18 +24,7 @@ public class RemoteController
_router = router; _router = router;
} }
public void Index() [ResourceMethod(RequestMethod.Post, "bring-to-foreground")]
{
// HTTP 204 No Content
}
[ControllerAction(RequestMethod.Get)]
public void Status()
{
// HTTP 204 No Content
}
[ControllerAction(RequestMethod.Post)]
public void BringToForeground(IRequest request) public void BringToForeground(IRequest request)
{ {
// Get the route from the request content stream // Get the route from the request content stream
@ -55,13 +43,13 @@ public class RemoteController
}); });
} }
[ControllerAction(RequestMethod.Post)] [ResourceMethod(RequestMethod.Post, "restart")]
public void Restart(List<string> args) public void Restart(List<string> args)
{ {
Utilities.Restart(_coreService.IsElevated, TimeSpan.FromMilliseconds(500), args.ToArray()); Utilities.Restart(_coreService.IsElevated, TimeSpan.FromMilliseconds(500), args.ToArray());
} }
[ControllerAction(RequestMethod.Post)] [ResourceMethod(RequestMethod.Post, "shutdown")]
public void Shutdown() public void Shutdown()
{ {
Utilities.Shutdown(); Utilities.Shutdown();

View File

@ -15,7 +15,7 @@
<PackageVersion Include="Avalonia.Skia.Lottie" Version="11.0.0" /> <PackageVersion Include="Avalonia.Skia.Lottie" Version="11.0.0" />
<PackageVersion Include="Avalonia.Win32" Version="11.2.3" /> <PackageVersion Include="Avalonia.Win32" Version="11.2.3" />
<PackageVersion Include="GenHTTP.Core" Version="9.6.2" /> <PackageVersion Include="GenHTTP.Core" Version="9.6.2" />
<PackageVersion Include="GenHTTP.Modules.Controllers" Version="9.6.2" /> <PackageVersion Include="GenHTTP.Modules.Webservices" Version="9.6.2" />
<PackageVersion Include="HPPH.SkiaSharp" Version="1.0.0" /> <PackageVersion Include="HPPH.SkiaSharp" Version="1.0.0" />
<PackageVersion Include="Microsoft.Win32.SystemEvents" Version="9.0.1" /> <PackageVersion Include="Microsoft.Win32.SystemEvents" Version="9.0.1" />
<PackageVersion Include="Avalonia.Xaml.Behaviors" Version="11.2.0.8" /> <PackageVersion Include="Avalonia.Xaml.Behaviors" Version="11.2.0.8" />