Files
Servers/MoonlightServers.Api/Admin/Nodes/NodeService.cs

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;
}
}