Added node server sync and delete sync. Cleaned up codebase and extracted calls to apis to services

This commit is contained in:
2025-03-02 19:24:24 +01:00
parent ef7f866ded
commit 30390dab71
25 changed files with 751 additions and 282 deletions

View File

@@ -22,9 +22,17 @@ public partial class Server
await LogToConsole("Fetching installation configuration");
// Fetching remote configuration
// Fetching remote configuration and install config
var remoteService = ServiceProvider.GetRequiredService<RemoteService>();
var installData = await remoteService.GetServerInstallation(Configuration.Id);
var serverData = await remoteService.GetServer(Configuration.Id);
// We are updating the regular server config here as well
// as changes to variables and other settings wouldn't sync otherwise
// because they won't trigger a sync
var serverConfiguration = serverData.ToServerConfiguration();
UpdateConfiguration(serverConfiguration);
var dockerImageService = ServiceProvider.GetRequiredService<DockerImageService>();

View File

@@ -1,5 +1,7 @@
using Docker.DotNet;
using MoonlightServers.Daemon.Enums;
using MoonlightServers.Daemon.Extensions;
using MoonlightServers.Daemon.Services;
namespace MoonlightServers.Daemon.Abstractions;
@@ -11,6 +13,17 @@ public partial class Server
{
try
{
await LogToConsole("Fetching configuration");
var remoteService = ServiceProvider.GetRequiredService<RemoteService>();
var serverData = await remoteService.GetServer(Configuration.Id);
// We are updating the server config here
// as changes to variables and other settings wouldn't sync otherwise
// because they won't trigger a sync
var serverConfiguration = serverData.ToServerConfiguration();
UpdateConfiguration(serverConfiguration);
await ReCreate();
await LogToConsole("Starting container");

View File

@@ -49,4 +49,7 @@ public partial class Server
RuntimeContainerName = $"moonlight-runtime-{Configuration.Id}";
InstallationContainerName = $"moonlight-install-{Configuration.Id}";
}
public void UpdateConfiguration(ServerConfiguration configuration)
=> Configuration = configuration;
}

View File

@@ -2,11 +2,35 @@ using Docker.DotNet.Models;
using Mono.Unix.Native;
using MoonCore.Helpers;
using MoonlightServers.Daemon.Models.Cache;
using MoonlightServers.DaemonShared.PanelSide.Http.Responses;
namespace MoonlightServers.Daemon.Extensions;
public static class ServerConfigurationExtensions
{
public static ServerConfiguration ToServerConfiguration(this ServerDataResponse response)
{
return new ServerConfiguration()
{
Id = response.Id,
StartupCommand = response.StartupCommand,
Allocations = response.Allocations.Select(y => new ServerConfiguration.AllocationConfiguration()
{
IpAddress = y.IpAddress,
Port = y.Port
}).ToArray(),
Variables = response.Variables,
OnlineDetection = response.OnlineDetection,
DockerImage = response.DockerImage,
UseVirtualDisk = response.UseVirtualDisk,
Bandwidth = response.Bandwidth,
Cpu = response.Cpu,
Disk = response.Disk,
Memory = response.Memory,
StopCommand = response.StopCommand
};
}
public static CreateContainerParameters ToRuntimeCreateParameters(this ServerConfiguration configuration,
string hostPath, string containerName)
{

View File

@@ -1,3 +1,4 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using MoonCore.Exceptions;
using MoonlightServers.Daemon.Enums;
@@ -5,6 +6,7 @@ using MoonlightServers.Daemon.Services;
namespace MoonlightServers.Daemon.Http.Controllers.Servers;
[Authorize]
[ApiController]
[Route("api/servers")]
public class ServerPowerController : Controller

View File

@@ -1,3 +1,4 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using MoonCore.Exceptions;
using MoonlightServers.Daemon.Services;
@@ -6,6 +7,7 @@ using MoonlightServers.DaemonShared.Enums;
namespace MoonlightServers.Daemon.Http.Controllers.Servers;
[Authorize]
[ApiController]
[Route("api/servers")]
public class ServersController : Controller
@@ -17,18 +19,32 @@ public class ServersController : Controller
ServerService = serverService;
}
[HttpPost("{serverId:int}/sync")]
public async Task Sync([FromRoute] int serverId)
{
await ServerService.Sync(serverId);
}
[HttpDelete("{serverId:int}")]
public async Task Delete([FromRoute] int serverId)
{
await ServerService.Delete(serverId);
}
[HttpGet("{serverId:int}/status")]
public async Task<ServerStatusResponse> GetStatus(int serverId)
public Task<ServerStatusResponse> GetStatus([FromRoute] int serverId)
{
var server = ServerService.GetServer(serverId);
if (server == null)
throw new HttpApiException("No server with this id found", 404);
return new ServerStatusResponse()
var result = new ServerStatusResponse()
{
State = (ServerState)server.State
};
return Task.FromResult(result);
}
[HttpGet("{serverId:int}/logs")]

View File

@@ -1,3 +1,4 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using MoonlightServers.Daemon.Helpers;
using MoonlightServers.DaemonShared.DaemonSide.Http.Responses.Statistics;
@@ -6,6 +7,7 @@ namespace MoonlightServers.Daemon.Http.Controllers.Statistics;
// This controller hosts endpoints for the statistics for the daemon application itself
[Authorize]
[ApiController]
[Route("api/statistics/application")]
public class StatisticsApplicationController : Controller

View File

@@ -1,3 +1,4 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using MoonlightServers.Daemon.Services;
using MoonlightServers.DaemonShared.DaemonSide.Http.Responses.Statistics;
@@ -6,6 +7,7 @@ namespace MoonlightServers.Daemon.Http.Controllers.Statistics;
// This controller hosts endpoints for the statistics for the docker environment
[Authorize]
[ApiController]
[Route("api/statistics/docker")]
public class StatisticsDockerController : Controller

View File

@@ -1,3 +1,4 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using MoonlightServers.Daemon.Helpers;
using MoonlightServers.DaemonShared.DaemonSide.Http.Responses.Statistics;
@@ -6,6 +7,7 @@ namespace MoonlightServers.Daemon.Http.Controllers.Statistics;
// This controller hosts endpoints for the statistics for host system the daemon runs on
[Authorize]
[ApiController]
[Route("api/statistics/host")]
public class StatisticsHostController : Controller

View File

@@ -1,10 +1,12 @@
using System.Diagnostics;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using MoonlightServers.Daemon.Services;
using MoonlightServers.DaemonShared.DaemonSide.Http.Responses.Sys;
namespace MoonlightServers.Daemon.Http.Controllers.Sys;
[Authorize]
[ApiController]
[Route("api/system/status")]
public class SystemStatusController : Controller

View File

@@ -31,6 +31,13 @@ public class RemoteService
);
}
public async Task<ServerDataResponse> GetServer(int serverId)
{
return await ApiClient.GetJson<ServerDataResponse>(
$"api/remote/servers/{serverId}"
);
}
public async Task<ServerInstallDataResponse> GetServerInstallation(int serverId)
{
return await ApiClient.GetJson<ServerInstallDataResponse>(

View File

@@ -1,8 +1,11 @@
using Docker.DotNet;
using Docker.DotNet.Models;
using MoonCore.Attributes;
using MoonCore.Exceptions;
using MoonCore.Models;
using MoonlightServers.Daemon.Abstractions;
using MoonlightServers.Daemon.Enums;
using MoonlightServers.Daemon.Extensions;
using MoonlightServers.Daemon.Models.Cache;
using MoonlightServers.DaemonShared.PanelSide.Http.Responses;
@@ -16,11 +19,15 @@ public class ServerService : IHostedLifecycleService
private readonly RemoteService RemoteService;
private readonly IServiceProvider ServiceProvider;
private readonly ILoggerFactory LoggerFactory;
private bool IsInitialized = false;
private CancellationTokenSource Cancellation = new();
private bool IsInitialized = false;
public ServerService(RemoteService remoteService, ILogger<ServerService> logger, IServiceProvider serviceProvider,
ILoggerFactory loggerFactory)
public ServerService(
RemoteService remoteService,
ILogger<ServerService> logger,
IServiceProvider serviceProvider,
ILoggerFactory loggerFactory
)
{
RemoteService = remoteService;
Logger = logger;
@@ -35,8 +42,8 @@ public class ServerService : IHostedLifecycleService
Logger.LogWarning("Ignoring initialize call: Already initialized");
return;
}
IsInitialized = true;
else
IsInitialized = true;
// Loading models and converting them
Logger.LogInformation("Fetching servers from panel");
@@ -45,25 +52,9 @@ public class ServerService : IHostedLifecycleService
await RemoteService.GetServers(page, pageSize)
);
var configurations = servers.Select(x => new ServerConfiguration()
{
Id = x.Id,
StartupCommand = x.StartupCommand,
Allocations = x.Allocations.Select(y => new ServerConfiguration.AllocationConfiguration()
{
IpAddress = y.IpAddress,
Port = y.Port
}).ToArray(),
Variables = x.Variables,
OnlineDetection = x.OnlineDetection,
DockerImage = x.DockerImage,
UseVirtualDisk = x.UseVirtualDisk,
Bandwidth = x.Bandwidth,
Cpu = x.Cpu,
Disk = x.Disk,
Memory = x.Memory,
StopCommand = x.StopCommand
}).ToArray();
var configurations = servers
.Select(x => x.ToServerConfiguration())
.ToArray();
Logger.LogInformation("Initializing {count} servers", servers.Length);
@@ -91,7 +82,7 @@ public class ServerService : IHostedLifecycleService
await Cancellation.CancelAsync();
}
private async Task AttachToDockerEvents()
private Task AttachToDockerEvents()
{
var dockerClient = ServiceProvider.GetRequiredService<DockerClient>();
@@ -123,11 +114,11 @@ public class ServerService : IHostedLifecycleService
await server.NotifyRuntimeContainerDied();
return;
}
// Check if it's an installation container
lock (Servers)
server = Servers.FirstOrDefault(x => x.InstallationContainerId == message.ID);
if (server != null)
{
await server.NotifyInstallationContainerDied();
@@ -144,9 +135,11 @@ public class ServerService : IHostedLifecycleService
}
}
});
return Task.CompletedTask;
}
private async Task InitializeServerRange(ServerConfiguration[] serverConfigurations)
public async Task InitializeServerRange(ServerConfiguration[] serverConfigurations)
{
var dockerClient = ServiceProvider.GetRequiredService<DockerClient>();
@@ -173,7 +166,7 @@ public class ServerService : IHostedLifecycleService
await InitializeServer(configuration, existingContainers);
}
private async Task InitializeServer(
public async Task<Server> InitializeServer(
ServerConfiguration serverConfiguration,
IList<ContainerListResponse> existingContainers
)
@@ -190,6 +183,70 @@ public class ServerService : IHostedLifecycleService
lock (Servers)
Servers.Add(server);
return server;
}
public async Task Sync(int serverId)
{
var serverData = await RemoteService.GetServer(serverId);
var serverConfiguration = serverData.ToServerConfiguration();
var server = GetServer(serverId);
if (server == null)
await InitializeServer(serverConfiguration, []);
else
server.UpdateConfiguration(serverConfiguration);
}
public async Task Delete(int serverId)
{
var server = GetServer(serverId);
// If a server with this id doesn't exist we can just exit
if(server == null)
return;
if (server.State == ServerState.Installing)
throw new HttpApiException("Unable to delete a server while it is installing", 400);
#region Callbacks
var deleteCompletion = new TaskCompletionSource();
async Task HandleStateChange(ServerState state)
{
if (state == ServerState.Offline)
await DeleteServer();
}
async Task DeleteServer()
{
await server.CancelTasks();
await server.RemoveInstallationVolume();
await server.RemoveRuntimeVolume();
deleteCompletion.SetResult();
lock (Servers)
Servers.Remove(server);
}
#endregion
// If the server is still online, we are killing it and then
// waiting for the callback to trigger notifying us that the server is now offline
// so we can delete it. The request will pause until then using the deleteCompletion task
if (server.State != ServerState.Offline)
{
server.OnStateChanged += HandleStateChange;
await server.Kill();
await deleteCompletion.Task;
}
else
await DeleteServer();
}
public Server? GetServer(int id)
@@ -197,7 +254,7 @@ public class ServerService : IHostedLifecycleService
lock (Servers)
return Servers.FirstOrDefault(x => x.Id == id);
}
#region Lifecycle
public Task StartAsync(CancellationToken cancellationToken)

View File

@@ -1,5 +1,8 @@
using System.Text;
using System.Text.Json;
using Docker.DotNet;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using MoonCore.Configuration;
using MoonCore.EnvConfiguration;
using MoonCore.Extended.Extensions;
@@ -41,6 +44,7 @@ public class Startup
await RegisterAppConfiguration();
await RegisterLogging();
await RegisterBase();
await RegisterAuth();
await RegisterDocker();
await RegisterServers();
await RegisterSignalR();
@@ -49,6 +53,7 @@ public class Startup
await BuildWebApplication();
await UseBase();
await UseAuth();
await UseCors();
await UseBaseMiddleware();
@@ -289,4 +294,40 @@ public class Startup
}
#endregion
#region Authentication
private Task RegisterAuth()
{
WebApplicationBuilder.Services
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new()
{
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(
Configuration.Security.Token
)),
ValidateIssuerSigningKey = true,
ValidateLifetime = true,
ValidateAudience = false,
ValidateIssuer = false,
ClockSkew = TimeSpan.Zero
};
});
WebApplicationBuilder.Services.AddAuthorization();
return Task.CompletedTask;
}
private Task UseAuth()
{
WebApplication.UseAuthentication();
WebApplication.UseAuthorization();
return Task.CompletedTask;
}
#endregion
}