145 lines
4.3 KiB
C#
145 lines
4.3 KiB
C#
using System.Diagnostics.CodeAnalysis;
|
|
using System.Net.Http.Json;
|
|
using System.Text.Json;
|
|
using Microsoft.Extensions.Logging;
|
|
using Microsoft.Net.Http.Headers;
|
|
using MoonlightServers.Api.Infrastructure.Database.Entities;
|
|
using MoonlightServers.DaemonShared.Http;
|
|
using MoonlightServers.DaemonShared.Http.Daemon;
|
|
|
|
namespace MoonlightServers.Api.Admin.Nodes;
|
|
|
|
public class NodeService
|
|
{
|
|
private readonly IHttpClientFactory ClientFactory;
|
|
private readonly ILogger<NodeService> Logger;
|
|
public NodeService(IHttpClientFactory clientFactory, ILogger<NodeService> logger)
|
|
{
|
|
ClientFactory = clientFactory;
|
|
Logger = logger;
|
|
}
|
|
|
|
public async Task<NodeHealthStatus> GetHealthAsync(Node node)
|
|
{
|
|
var client = ClientFactory.CreateClient();
|
|
|
|
var request = CreateBaseRequest(node, HttpMethod.Get, "api/health");
|
|
|
|
try
|
|
{
|
|
var response = await client.SendAsync(request);
|
|
|
|
if (!response.IsSuccessStatusCode)
|
|
return new NodeHealthStatus((int)response.StatusCode, null);
|
|
|
|
try
|
|
{
|
|
var health = await response
|
|
.Content
|
|
.ReadFromJsonAsync<HealthDto>(SerializationContext.Default.Options);
|
|
|
|
return new NodeHealthStatus((int)response.StatusCode, health);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Logger.LogTrace(e, "An unhandled error occured while processing health response of node {id}", node.Id);
|
|
|
|
return new NodeHealthStatus((int)response.StatusCode, null);
|
|
}
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Logger.LogTrace(e, "An error occured while fetching health status of node {id}", node.Id);
|
|
return new NodeHealthStatus(0, null);
|
|
}
|
|
}
|
|
|
|
public async Task<SystemStatisticsDto> GetStatisticsAsync(Node node)
|
|
{
|
|
var client = ClientFactory.CreateClient();
|
|
|
|
var request = CreateBaseRequest(node, HttpMethod.Get, "api/system/statistics");
|
|
|
|
var response = await client.SendAsync(request);
|
|
|
|
await EnsureSuccessAsync(response);
|
|
|
|
return (await response.Content.ReadFromJsonAsync<SystemStatisticsDto>(SerializationContext.Default.Options))!;
|
|
}
|
|
|
|
private static HttpRequestMessage CreateBaseRequest(
|
|
Node node,
|
|
[StringSyntax(StringSyntaxAttribute.Uri)]
|
|
HttpMethod method,
|
|
string endpoint
|
|
)
|
|
{
|
|
var request = new HttpRequestMessage();
|
|
|
|
request.Headers.Add(HeaderNames.Authorization, node.Token);
|
|
request.RequestUri = new Uri(new Uri(node.HttpEndpointUrl), endpoint);
|
|
request.Method = method;
|
|
|
|
return request;
|
|
}
|
|
|
|
private async Task EnsureSuccessAsync(HttpResponseMessage message)
|
|
{
|
|
if (message.IsSuccessStatusCode)
|
|
return;
|
|
|
|
try
|
|
{
|
|
var problemDetails = await message.Content.ReadFromJsonAsync<ProblemDetails>(
|
|
SerializationContext.Default.Options
|
|
);
|
|
|
|
if (problemDetails == null)
|
|
{
|
|
// If we cant handle problem details, we handle it natively
|
|
message.EnsureSuccessStatusCode();
|
|
return;
|
|
}
|
|
|
|
// Parse into exception
|
|
throw new NodeException(
|
|
problemDetails.Type,
|
|
problemDetails.Title,
|
|
problemDetails.Status,
|
|
problemDetails.Detail,
|
|
problemDetails.Errors
|
|
);
|
|
}
|
|
catch (JsonException)
|
|
{
|
|
// If we cant handle problem details, we handle it natively
|
|
message.EnsureSuccessStatusCode();
|
|
}
|
|
}
|
|
}
|
|
|
|
public record NodeHealthStatus(int StatusCode, HealthDto? Dto);
|
|
|
|
public class NodeException : Exception
|
|
{
|
|
public string Type { get; }
|
|
public string Title { get; }
|
|
public int Status { get; }
|
|
public string? Detail { get; }
|
|
public Dictionary<string, string[]>? Errors { get; }
|
|
|
|
public NodeException(
|
|
string type,
|
|
string title,
|
|
int status,
|
|
string? detail = null,
|
|
Dictionary<string, string[]>? errors = null)
|
|
: base(detail ?? title)
|
|
{
|
|
Type = type;
|
|
Title = title;
|
|
Status = status;
|
|
Detail = detail;
|
|
Errors = errors;
|
|
}
|
|
} |