diff --git a/Pilz.Net/Api/ApiResult.cs b/Pilz.Net/Api/ApiResult.cs index accfcb5..d5d1ed4 100644 --- a/Pilz.Net/Api/ApiResult.cs +++ b/Pilz.Net/Api/ApiResult.cs @@ -8,11 +8,11 @@ public record class ApiResult( { public static ApiResult Ok() => new(HttpStatusCode.OK); - public static ApiResult Ok(ApiMessage message) => new(HttpStatusCode.OK, message); + public static ApiResult Ok(ApiMessage? message) => new(HttpStatusCode.OK, message); public static ApiResult Created() => new(HttpStatusCode.Created); - public static ApiResult Created(ApiMessage message) => new(HttpStatusCode.Created, message); + public static ApiResult Created(ApiMessage? message) => new(HttpStatusCode.Created, message); public static ApiResult Unauthorized() => new(HttpStatusCode.Unauthorized); diff --git a/Pilz.Net/Api/ApiServer.cs b/Pilz.Net/Api/ApiServer.cs index 8a7ae23..860245d 100644 --- a/Pilz.Net/Api/ApiServer.cs +++ b/Pilz.Net/Api/ApiServer.cs @@ -20,7 +20,7 @@ public class ApiServer : IApiServer protected readonly Dictionary serializers = []; protected readonly Dictionary managers = []; protected HttpListener httpListener; - protected int restartAttempts = 0; + protected uint restartAttempts = 0; protected DateTime lastRestartAttempt; protected SemaphoreSlim? semaphore; protected bool doListen; @@ -36,6 +36,10 @@ public class ApiServer : IApiServer protected record PrivateMessageHandler(string Url, bool UseRegEx, Delegate Handler, PrivateParameterInfo[] Parameters, ApiMessageHandlerAttribute Attribute); protected record PrivateApiResult(ApiResult Original, object? ResultContent); + public int HandlersCount => handlers.Count; + public int ManagersCount => managers.Count; + public uint RestartAttempts => restartAttempts; + public string ApiUrl { get; } public uint ApiVersion { get; set; } = 1; public virtual bool EnableAuth { get; set; } @@ -112,11 +116,6 @@ public class ApiServer : IApiServer Receive(); } - public virtual void Stop() - { - Stop(true); - } - public virtual void Stop(bool graceful) { Log.Info("Stopping listener"); @@ -184,6 +183,15 @@ public class ApiServer : IApiServer semaphore.Release(); } + public virtual IEnumerable GetEndpoints() + { + return handlers.SelectMany(n => n.Attribute.Methods.Select(m => new + { + n.Attribute.Route, + Method = m, + })).OrderBy(n => n.Route).ThenBy(n => n.Method).Select(n => $"{n.Method} {n.Route}"); + } + public virtual void RegisterHandler(T instance) where T : class { // Initialize diff --git a/Pilz.Net/Api/IApiServer.cs b/Pilz.Net/Api/IApiServer.cs index 03c9eba..261e939 100644 --- a/Pilz.Net/Api/IApiServer.cs +++ b/Pilz.Net/Api/IApiServer.cs @@ -16,24 +16,18 @@ public interface IApiServer event DataManagerEventHandler? OnResetDataManager; IDataManager Manager { get; } - string ApiUrl { get; } - bool EnableAuth { get; set; } IApiMessageSerializer Serializer { get; } - ILogger Log { get; set; } - void Start(); - - void Stop(); - + void Stop() => Stop(true); void RegisterHandler(T instance) where T : class; - void RegisterHandler(Delegate handler); - void RegisterHandler(Delegate handler, bool throwOnError); - void RegisterHandler(Delegate handler, ApiMessageHandlerAttribute attribute, bool throwOnError); + IEnumerable GetEndpoints(); + void Stop(bool graceful); + void Restart(bool graceful); } diff --git a/Pilz.Net/Api/Messages/ServerCapabilitiesMessage.cs b/Pilz.Net/Api/Messages/ServerCapabilitiesMessage.cs new file mode 100644 index 0000000..e1fcd89 --- /dev/null +++ b/Pilz.Net/Api/Messages/ServerCapabilitiesMessage.cs @@ -0,0 +1,6 @@ +namespace Pilz.Net.Api.Messages; + +public class ServerCapabilitiesMessage : ApiMessage +{ + public List Endpoints { get; } = []; +} diff --git a/Pilz.Net/Api/Messages/ServerStatusMessage.cs b/Pilz.Net/Api/Messages/ServerStatusMessage.cs new file mode 100644 index 0000000..2a0cbb9 --- /dev/null +++ b/Pilz.Net/Api/Messages/ServerStatusMessage.cs @@ -0,0 +1,10 @@ +namespace Pilz.Net.Api.Messages; + +public class ServerStatusMessage : ApiMessage +{ + public AppVersion? AppVersion { get; set; } + public uint ApiVersion { get; set; } + public int EndpointsCount { get; set; } + public int DataManagersCount { get; set; } + public uint RestartAttempts { get; set; } +} diff --git a/Pilz.Net/Api/Server/ServerCapabilitiesHandler.cs b/Pilz.Net/Api/Server/ServerCapabilitiesHandler.cs new file mode 100644 index 0000000..be9598b --- /dev/null +++ b/Pilz.Net/Api/Server/ServerCapabilitiesHandler.cs @@ -0,0 +1,31 @@ +using Pilz.Extensions.Reflection; +using Pilz.Net.Api.Messages; +using System.Diagnostics; + +namespace Pilz.Net.Api.Server; + +public class ServerCapabilitiesHandler : IApiHandlerInitializer +{ + protected IApiServer? server; + + public string Route { get; set; } = "/capabilities"; + + public virtual void Initialize(IApiServer server) + { + this.server = server; + server.RegisterHandler(GetType().GetMethod(nameof(GetCapabilities))!.CreateDelegate(this), new(Route, "GET"), Debugger.IsAttached); + } + + public virtual ApiResult GetCapabilities(ApiRequestInfo req) + { + var msg = BuildMessage(req); + if (server is ServerCapabilitiesMessage message) + message.Endpoints.AddRange(server.GetEndpoints()); + return ApiResult.Ok(msg); + } + + protected virtual ApiMessage? BuildMessage(ApiRequestInfo req) + { + return new ServerCapabilitiesMessage(); + } +} diff --git a/Pilz.Net/Api/Server/ServerStatusHandler.cs b/Pilz.Net/Api/Server/ServerStatusHandler.cs new file mode 100644 index 0000000..b760c83 --- /dev/null +++ b/Pilz.Net/Api/Server/ServerStatusHandler.cs @@ -0,0 +1,41 @@ +using Pilz.Extensions; +using Pilz.Extensions.Reflection; +using Pilz.Net.Api.Messages; +using System.Diagnostics; +using System.Reflection; + +namespace Pilz.Net.Api.Server; + +public class ServerStatusHandler : IApiHandlerInitializer +{ + protected IApiServer? server; + + public string Route { get; set; } = "/status"; + + public virtual void Initialize(IApiServer server) + { + this.server = server; + server.RegisterHandler(GetType().GetMethod(nameof(GetStatus))!.CreateDelegate(this), new(Route, "GET"), Debugger.IsAttached); + } + + public virtual ApiResult GetStatus(ApiRequestInfo req) + { + var msg = BuildMessage(req); + + if (server is ApiServer apiServer && msg is ServerStatusMessage message) + { + message.AppVersion ??= Assembly.GetEntryAssembly()?.GetAppVersion(); + message.ApiVersion = apiServer.ApiVersion; + message.EndpointsCount = apiServer.HandlersCount; + message.DataManagersCount = apiServer.ManagersCount; + message.RestartAttempts = apiServer.RestartAttempts; + } + + return ApiResult.Ok(msg); + } + + protected virtual ApiMessage? BuildMessage(ApiRequestInfo req) + { + return new ServerStatusMessage(); + } +}