diff --git a/Moonlight/App/Helpers/Files/WingsFileAccess.cs b/Moonlight/App/Helpers/Files/WingsFileAccess.cs index 47acdc9d..8e1b7093 100644 --- a/Moonlight/App/Helpers/Files/WingsFileAccess.cs +++ b/Moonlight/App/Helpers/Files/WingsFileAccess.cs @@ -3,6 +3,7 @@ using Moonlight.App.ApiClients.Wings; using Moonlight.App.ApiClients.Wings.Requests; using Moonlight.App.ApiClients.Wings.Resources; using Moonlight.App.Database.Entities; +using Moonlight.App.Helpers.Wings; using Moonlight.App.Services; using RestSharp; diff --git a/Moonlight/App/Helpers/Formatter.cs b/Moonlight/App/Helpers/Formatter.cs index d23d3b5e..99a8ab62 100644 --- a/Moonlight/App/Helpers/Formatter.cs +++ b/Moonlight/App/Helpers/Formatter.cs @@ -8,7 +8,14 @@ public static class Formatter { TimeSpan t = TimeSpan.FromMilliseconds(uptime); - return $"{t.Days}d {t.Hours}h {t.Minutes}m {t.Seconds}s"; + if (t.Days > 0) + { + return $"{t.Days}d {t.Hours}h {t.Minutes}m {t.Seconds}s"; + } + else + { + return $"{t.Hours}h {t.Minutes}m {t.Seconds}s"; + } } private static double Round(this double d, int decimals) diff --git a/Moonlight/App/Helpers/Wings/Data/ConsoleMessage.cs b/Moonlight/App/Helpers/Wings/Data/ConsoleMessage.cs new file mode 100644 index 00000000..87d86f6d --- /dev/null +++ b/Moonlight/App/Helpers/Wings/Data/ConsoleMessage.cs @@ -0,0 +1,7 @@ +namespace Moonlight.App.Helpers.Wings.Data; + +public class ConsoleMessage +{ + public string Content { get; set; } = ""; + public bool IsInternal { get; set; } = false; +} \ No newline at end of file diff --git a/Moonlight/App/Helpers/Wings/Data/ServerResource.cs b/Moonlight/App/Helpers/Wings/Data/ServerResource.cs new file mode 100644 index 00000000..2b7ec53f --- /dev/null +++ b/Moonlight/App/Helpers/Wings/Data/ServerResource.cs @@ -0,0 +1,36 @@ +using Newtonsoft.Json; + +namespace Moonlight.App.Helpers.Wings.Data; + +public class ServerResource +{ + [JsonProperty("memory_bytes")] + public long MemoryBytes { get; set; } + + [JsonProperty("memory_limit_bytes")] + public long MemoryLimitBytes { get; set; } + + [JsonProperty("cpu_absolute")] + public float CpuAbsolute { get; set; } + + [JsonProperty("network")] + public NetworkData Network { get; set; } + + [JsonProperty("uptime")] + public double Uptime { get; set; } + + [JsonProperty("state")] + public string State { get; set; } + + [JsonProperty("disk_bytes")] + public long DiskBytes { get; set; } + + public class NetworkData + { + [JsonProperty("rx_bytes")] + public long RxBytes { get; set; } + + [JsonProperty("tx_bytes")] + public long TxBytes { get; set; } + } +} \ No newline at end of file diff --git a/Moonlight/App/Helpers/Wings/Enums/ConsoleState.cs b/Moonlight/App/Helpers/Wings/Enums/ConsoleState.cs new file mode 100644 index 00000000..b2fce78e --- /dev/null +++ b/Moonlight/App/Helpers/Wings/Enums/ConsoleState.cs @@ -0,0 +1,8 @@ +namespace Moonlight.App.Helpers.Wings.Enums; + +public enum ConsoleState +{ + Disconnected, + Connecting, + Connected +} \ No newline at end of file diff --git a/Moonlight/App/Helpers/Wings/Enums/ServerState.cs b/Moonlight/App/Helpers/Wings/Enums/ServerState.cs new file mode 100644 index 00000000..1ced0020 --- /dev/null +++ b/Moonlight/App/Helpers/Wings/Enums/ServerState.cs @@ -0,0 +1,10 @@ +namespace Moonlight.App.Helpers.Wings.Enums; + +public enum ServerState +{ + Starting, + Running, + Stopping, + Offline, + Installing +} \ No newline at end of file diff --git a/Moonlight/App/Helpers/Wings/Events/BaseEvent.cs b/Moonlight/App/Helpers/Wings/Events/BaseEvent.cs new file mode 100644 index 00000000..9c0455bd --- /dev/null +++ b/Moonlight/App/Helpers/Wings/Events/BaseEvent.cs @@ -0,0 +1,7 @@ +namespace Moonlight.App.Helpers.Wings.Events; + +public class BaseEvent +{ + public string Event { get; set; } = ""; + public string[] Args { get; set; } = Array.Empty(); +} \ No newline at end of file diff --git a/Moonlight/App/Helpers/Wings/Events/SendTokenEvent.cs b/Moonlight/App/Helpers/Wings/Events/SendTokenEvent.cs new file mode 100644 index 00000000..660ae448 --- /dev/null +++ b/Moonlight/App/Helpers/Wings/Events/SendTokenEvent.cs @@ -0,0 +1,7 @@ +namespace Moonlight.App.Helpers.Wings.Events; + +public class SendTokenEvent +{ + public string Event { get; set; } = "auth"; + public List Args = new(); +} \ No newline at end of file diff --git a/Moonlight/App/Helpers/Wings/WingsConsole.cs b/Moonlight/App/Helpers/Wings/WingsConsole.cs new file mode 100644 index 00000000..bb6edfbb --- /dev/null +++ b/Moonlight/App/Helpers/Wings/WingsConsole.cs @@ -0,0 +1,377 @@ +using System.Net.WebSockets; +using System.Text; +using Logging.Net; +using Moonlight.App.Helpers.Wings.Data; +using Moonlight.App.Helpers.Wings.Enums; +using Moonlight.App.Helpers.Wings.Events; +using Newtonsoft.Json; +using ConsoleMessage = Moonlight.App.Helpers.Wings.Data.ConsoleMessage; + +namespace Moonlight.App.Helpers.Wings; + +public class WingsConsole : IDisposable +{ + private ClientWebSocket WebSocket; + public List Messages; + private Task? ConsoleTask; + + private string Socket = ""; + private string Origin = ""; + private string Token = ""; + + private bool Disconnecting; + + public ConsoleState ConsoleState { get; private set; } + public ServerState ServerState { get; private set; } + public ServerResource Resource { get; private set; } + + public EventHandler OnConsoleStateUpdated { get; set; } + public EventHandler OnServerStateUpdated { get; set; } + public EventHandler OnResourceUpdated { get; set; } + public EventHandler OnMessage { get; set; } + public Func> OnRequestNewToken { get; set; } + + public WingsConsole() + { + ConsoleState = ConsoleState.Disconnected; + ServerState = ServerState.Offline; + Messages = new(); + + Resource = new() + { + Network = new() + { + RxBytes = 0, + TxBytes = 0 + }, + State = "offline", + Uptime = 0, + CpuAbsolute = 0, + DiskBytes = 0, + MemoryBytes = 0, + MemoryLimitBytes = 0 + }; + } + + public Task Connect(string origin, string socket, string token) + { + Disconnecting = false; + WebSocket = new(); + ConsoleState = ConsoleState.Disconnected; + ServerState = ServerState.Offline; + Messages = new(); + + Resource = new() + { + Network = new() + { + RxBytes = 0, + TxBytes = 0 + }, + State = "offline", + Uptime = 0, + CpuAbsolute = 0, + DiskBytes = 0, + MemoryBytes = 0, + MemoryLimitBytes = 0 + }; + + Socket = socket; + Origin = origin; + Token = token; + + WebSocket.Options.SetRequestHeader("Origin", Origin); + WebSocket.Options.SetRequestHeader("Authorization", "Bearer " + Token); + + ConsoleTask = Task.Run(async () => + { + try + { + await Work(); + } + catch (Exception e) + { + Logger.Warn("Error connecting to wings console"); + Logger.Warn(e); + } + }); + + return Task.CompletedTask; + } + + private async Task Work() + { + await UpdateConsoleState(ConsoleState.Connecting); + + await WebSocket.ConnectAsync( + new Uri(Socket), + CancellationToken.None + ); + + if (WebSocket.State != WebSocketState.Connecting && WebSocket.State != WebSocketState.Open) + { + await SaveMessage("Unable to connect to websocket", true); + await UpdateConsoleState(ConsoleState.Disconnected); + return; + } + + await UpdateConsoleState(ConsoleState.Connected); + + await Send(new SendTokenEvent() + { + Args = { Token } + }); + + while (WebSocket.State == WebSocketState.Open) + { + try + { + var raw = await ReceiveRaw(); + + if(string.IsNullOrEmpty(raw)) + continue; + + var eventData = JsonConvert.DeserializeObject(raw); + + if (eventData == null) + { + await SaveMessage("Unable to parse event", true); + continue; + } + + switch (eventData.Event) + { + case "jwt error": + await WebSocket.CloseAsync(WebSocketCloseStatus.Empty, "Jwt error detected", + CancellationToken.None); + + await UpdateServerState(ServerState.Offline); + await UpdateConsoleState(ConsoleState.Disconnected); + + await SaveMessage("Received a jwt error", true); + break; + + case "token expired": + await WebSocket.CloseAsync(WebSocketCloseStatus.Empty, "Jwt error detected", + CancellationToken.None); + + await UpdateServerState(ServerState.Offline); + await UpdateConsoleState(ConsoleState.Disconnected); + + await SaveMessage("Token expired", true); + + break; + + case "token expiring": + await SaveMessage("Token will expire soon. Generating a new one", true); + + Token = await OnRequestNewToken.Invoke(this); + + await Send(new SendTokenEvent() + { + Args = { Token } + }); + break; + + case "auth success": + // Send intents + await SendRaw("{\"event\":\"send logs\",\"args\":[null]}"); + await SendRaw("{\"event\":\"send stats\",\"args\":[null]}"); + break; + + case "stats": + var stats = JsonConvert.DeserializeObject(eventData.Args[0]); + + if (stats == null) + break; + + var serverState = ParseServerState(stats.State); + + if (ServerState != serverState) + await UpdateServerState(serverState); + + await UpdateResource(stats); + break; + + case "status": + var serverStateParsed = ParseServerState(eventData.Args[0]); + + if (ServerState != serverStateParsed) + await UpdateServerState(serverStateParsed); + break; + + case "console output": + foreach (var line in eventData.Args) + { + await SaveMessage(line); + } + + break; + + case "install output": + foreach (var line in eventData.Args) + { + await SaveMessage(line); + } + + break; + + case "daemon message": + foreach (var line in eventData.Args) + { + await SaveMessage(line); + } + + break; + + case "install started": + await UpdateServerState(ServerState.Installing); + break; + + case "install completed": + await UpdateServerState(ServerState.Offline); + break; + } + } + catch (Exception e) + { + if (!Disconnecting) + { + Logger.Warn("Error while performing websocket actions"); + Logger.Warn(e); + + await SaveMessage("A unknown error occured while processing websocket", true); + } + } + } + } + + private Task UpdateConsoleState(ConsoleState consoleState) + { + ConsoleState = consoleState; + OnConsoleStateUpdated?.Invoke(this, consoleState); + + return Task.CompletedTask; + } + private Task UpdateServerState(ServerState serverState) + { + ServerState = serverState; + OnServerStateUpdated?.Invoke(this, serverState); + + return Task.CompletedTask; + } + private Task UpdateResource(ServerResource resource) + { + Resource = resource; + OnResourceUpdated?.Invoke(this, Resource); + + return Task.CompletedTask; + } + + private Task SaveMessage(string content, bool internalMessage = false) + { + var msg = new ConsoleMessage() + { + Content = content, + IsInternal = internalMessage + }; + + lock (Messages) + { + Messages.Add(msg); + } + + OnMessage?.Invoke(this, msg); + + return Task.CompletedTask; + } + + private ServerState ParseServerState(string raw) + { + switch (raw) + { + case "offline": + return ServerState.Offline; + case "starting": + return ServerState.Starting; + case "running": + return ServerState.Running; + case "stopping": + return ServerState.Stopping; + case "installing": + return ServerState.Installing; + default: + return ServerState.Offline; + } + } + + public async Task EnterCommand(string content) + { + if (ConsoleState == ConsoleState.Connected) + { + await SendRaw("{\"event\":\"send command\",\"args\":[\"" + content + "\"]}"); + } + } + + public async Task SetPowerState(string state) + { + if (ConsoleState == ConsoleState.Connected) + { + await SendRaw("{\"event\":\"set state\",\"args\":[\"" + state + "\"]}"); + } + } + + private async Task Send(object data) + { + await SendRaw(JsonConvert.SerializeObject(data)); + } + + private async Task SendRaw(string data) + { + if (WebSocket.State == WebSocketState.Open) + { + byte[] byteContentBuffer = Encoding.UTF8.GetBytes(data); + await WebSocket.SendAsync(new ArraySegment(byteContentBuffer), WebSocketMessageType.Text, true, + CancellationToken.None); + } + } + + private async Task ReceiveRaw() + { + ArraySegment receivedBytes = new ArraySegment(new byte[1024]); + WebSocketReceiveResult result = await WebSocket.ReceiveAsync(receivedBytes, CancellationToken.None); + return Encoding.UTF8.GetString(receivedBytes.Array!, 0, result.Count); + } + + public async Task Disconnect() + { + Disconnecting = true; + + if (WebSocket != null) + { + if (WebSocket.State == WebSocketState.Connecting || WebSocket.State == WebSocketState.Open) + await WebSocket.CloseAsync(WebSocketCloseStatus.Empty, null, CancellationToken.None); + + WebSocket.Dispose(); + } + + if(ConsoleTask != null && ConsoleTask.IsCompleted) + ConsoleTask.Dispose(); + } + + public void Dispose() + { + Disconnecting = true; + + if (WebSocket != null) + { + if (WebSocket.State == WebSocketState.Connecting || WebSocket.State == WebSocketState.Open) + WebSocket.CloseAsync(WebSocketCloseStatus.Empty, null, CancellationToken.None).Wait(); + + WebSocket.Dispose(); + } + + if(ConsoleTask != null && ConsoleTask.IsCompleted) + ConsoleTask.Dispose(); + } +} \ No newline at end of file diff --git a/Moonlight/App/Helpers/WingsConsoleHelper.cs b/Moonlight/App/Helpers/Wings/WingsConsoleHelper.cs similarity index 83% rename from Moonlight/App/Helpers/WingsConsoleHelper.cs rename to Moonlight/App/Helpers/Wings/WingsConsoleHelper.cs index 720a293b..af2fc7d5 100644 --- a/Moonlight/App/Helpers/WingsConsoleHelper.cs +++ b/Moonlight/App/Helpers/Wings/WingsConsoleHelper.cs @@ -7,37 +7,34 @@ using Moonlight.App.Database.Entities; using Moonlight.App.Repositories.Servers; using Moonlight.App.Services; -namespace Moonlight.App.Helpers; +namespace Moonlight.App.Helpers.Wings; public class WingsConsoleHelper { private readonly ServerRepository ServerRepository; - private readonly WingsJwtHelper WingsJwtHelper; private readonly string AppUrl; public WingsConsoleHelper( ServerRepository serverRepository, - ConfigService configService, - WingsJwtHelper wingsJwtHelper) + ConfigService configService) { ServerRepository = serverRepository; - WingsJwtHelper = wingsJwtHelper; AppUrl = configService.GetSection("Moonlight").GetValue("AppUrl"); } - public async Task ConnectWings(PteroConsole.NET.PteroConsole pteroConsole, Server server) + public async Task ConnectWings(WingsConsole console, Server server) { var serverData = ServerRepository .Get() .Include(x => x.Node) .First(x => x.Id == server.Id); - - var token = GenerateToken(serverData); + + var token = await GenerateToken(serverData); if (serverData.Node.Ssl) { - await pteroConsole.Connect( + await console.Connect( AppUrl, $"wss://{serverData.Node.Fqdn}:{serverData.Node.HttpPort}/api/servers/{serverData.Uuid}/ws", token @@ -45,7 +42,7 @@ public class WingsConsoleHelper } else { - await pteroConsole.Connect( + await console.Connect( AppUrl, $"ws://{serverData.Node.Fqdn}:{serverData.Node.HttpPort}/api/servers/{serverData.Uuid}/ws", token @@ -53,20 +50,20 @@ public class WingsConsoleHelper } } - public string GenerateToken(Server server) + public async Task GenerateToken(Server server) { var serverData = ServerRepository .Get() .Include(x => x.Node) .First(x => x.Id == server.Id); - + var userid = 1; var secret = serverData.Node.Token; using (MD5 md5 = MD5.Create()) { - var inputBytes = Encoding.ASCII.GetBytes(userid + serverData.Uuid.ToString()); + var inputBytes = Encoding.ASCII.GetBytes(userid + server.Uuid.ToString()); var outputBytes = md5.ComputeHash(inputBytes); var identifier = Convert.ToHexString(outputBytes).ToLower(); @@ -77,7 +74,7 @@ public class WingsConsoleHelper .WithAlgorithm(new HMACSHA256Algorithm()) .WithSecret(secret) .AddClaim("user_id", userid) - .AddClaim("server_uuid", serverData.Uuid.ToString()) + .AddClaim("server_uuid", server.Uuid.ToString()) .AddClaim("permissions", new[] { "*", diff --git a/Moonlight/App/Helpers/WingsJwtHelper.cs b/Moonlight/App/Helpers/Wings/WingsJwtHelper.cs similarity index 97% rename from Moonlight/App/Helpers/WingsJwtHelper.cs rename to Moonlight/App/Helpers/Wings/WingsJwtHelper.cs index 221abd1f..8e4af78a 100644 --- a/Moonlight/App/Helpers/WingsJwtHelper.cs +++ b/Moonlight/App/Helpers/Wings/WingsJwtHelper.cs @@ -4,7 +4,7 @@ using JWT.Algorithms; using JWT.Builder; using Moonlight.App.Services; -namespace Moonlight.App.Helpers; +namespace Moonlight.App.Helpers.Wings; public class WingsJwtHelper { diff --git a/Moonlight/App/Helpers/WingsServerConverter.cs b/Moonlight/App/Helpers/Wings/WingsServerConverter.cs similarity index 99% rename from Moonlight/App/Helpers/WingsServerConverter.cs rename to Moonlight/App/Helpers/Wings/WingsServerConverter.cs index c7375e3d..c0875ab1 100644 --- a/Moonlight/App/Helpers/WingsServerConverter.cs +++ b/Moonlight/App/Helpers/Wings/WingsServerConverter.cs @@ -5,7 +5,7 @@ using Moonlight.App.Http.Resources.Wings; using Moonlight.App.Repositories; using Moonlight.App.Repositories.Servers; -namespace Moonlight.App.Helpers; +namespace Moonlight.App.Helpers.Wings; public class WingsServerConverter { diff --git a/Moonlight/App/Http/Controllers/Api/Remote/ServersController.cs b/Moonlight/App/Http/Controllers/Api/Remote/ServersController.cs index 08f4ddc0..b7afee2e 100644 --- a/Moonlight/App/Http/Controllers/Api/Remote/ServersController.cs +++ b/Moonlight/App/Http/Controllers/Api/Remote/ServersController.cs @@ -2,6 +2,7 @@ using Microsoft.EntityFrameworkCore; using Moonlight.App.Events; using Moonlight.App.Helpers; +using Moonlight.App.Helpers.Wings; using Moonlight.App.Http.Resources.Wings; using Moonlight.App.Repositories; using Moonlight.App.Repositories.Servers; diff --git a/Moonlight/App/Services/ServerService.cs b/Moonlight/App/Services/ServerService.cs index fb9c4db0..ae0390b0 100644 --- a/Moonlight/App/Services/ServerService.cs +++ b/Moonlight/App/Services/ServerService.cs @@ -8,6 +8,7 @@ using Moonlight.App.Events; using Moonlight.App.Exceptions; using Moonlight.App.Helpers; using Moonlight.App.Helpers.Files; +using Moonlight.App.Helpers.Wings; using Moonlight.App.Models.Misc; using Moonlight.App.Repositories; using Moonlight.App.Repositories.Servers; diff --git a/Moonlight/Moonlight.csproj b/Moonlight/Moonlight.csproj index 64f9e048..d519c643 100644 --- a/Moonlight/Moonlight.csproj +++ b/Moonlight/Moonlight.csproj @@ -41,7 +41,6 @@ - diff --git a/Moonlight/Program.cs b/Moonlight/Program.cs index d9a46b8e..70466a08 100644 --- a/Moonlight/Program.cs +++ b/Moonlight/Program.cs @@ -9,6 +9,7 @@ using Moonlight.App.ApiClients.Wings; using Moonlight.App.Database; using Moonlight.App.Events; using Moonlight.App.Helpers; +using Moonlight.App.Helpers.Wings; using Moonlight.App.LogMigrator; using Moonlight.App.Repositories; using Moonlight.App.Repositories.Domains; diff --git a/Moonlight/Shared/Components/ServerControl/ServerBackups.razor b/Moonlight/Shared/Components/ServerControl/ServerBackups.razor index 7175a3c7..fc71c474 100644 --- a/Moonlight/Shared/Components/ServerControl/ServerBackups.razor +++ b/Moonlight/Shared/Components/ServerControl/ServerBackups.razor @@ -1,6 +1,4 @@ -@using PteroConsole.NET -@using Moonlight.App.Services -@using Task = System.Threading.Tasks.Task +@using Moonlight.App.Services @using Moonlight.App.Helpers @using Logging.Net @using BlazorContextMenu @@ -101,9 +99,6 @@ @code { - [CascadingParameter] - public PteroConsole Console { get; set; } - [CascadingParameter] public Server CurrentServer { get; set; } diff --git a/Moonlight/Shared/Components/ServerControl/ServerConsole.razor b/Moonlight/Shared/Components/ServerControl/ServerConsole.razor index 474efa29..454e6d87 100644 --- a/Moonlight/Shared/Components/ServerControl/ServerConsole.razor +++ b/Moonlight/Shared/Components/ServerControl/ServerConsole.razor @@ -1,11 +1,9 @@ -@using PteroConsole.NET -@using PteroConsole.NET.Enums -@using Task = System.Threading.Tasks.Task -@using Moonlight.App.Helpers +@using Moonlight.App.Helpers @using Moonlight.App.Repositories @using Moonlight.App.Services -@using Logging.Net @using Moonlight.App.Database.Entities +@using Moonlight.App.Helpers.Wings +@using Moonlight.App.Helpers.Wings.Data @using Moonlight.App.Services.Interop @using Moonlight.Shared.Components.Xterm @@ -37,7 +35,7 @@ @code { [CascadingParameter] - public PteroConsole Console { get; set; } + public WingsConsole Console { get; set; } [CascadingParameter] public Server CurrentServer { get; set; } @@ -51,21 +49,35 @@ Console.OnMessage += OnMessage; } - private async void OnMessage(object? sender, string e) + private async void OnMessage(object? sender, ConsoleMessage message) { if (Terminal != null) { - var s = e; + if (message.IsInternal) + { + await Terminal.WriteLine("\x1b[38;5;16;48;5;135m\x1b[39m\x1b[1m Moonlight \x1b[0m " + message.Content + "\x1b[0m"); + } + else + { + var s = message.Content; - s = s.Replace("Pterodactyl Daemon", "Moonlight Daemon"); - s = s.Replace("Checking server disk space usage, this could take a few seconds...", TranslationService.Translate("Checking disk space")); - s = s.Replace("Updating process configuration files...", TranslationService.Translate("Updating config files")); - s = s.Replace("Ensuring file permissions are set correctly, this could take a few seconds...", TranslationService.Translate("Checking file permissions")); - s = s.Replace("Pulling Docker container image, this could take a few minutes to complete...", TranslationService.Translate("Downloading server image")); - s = s.Replace("Finished pulling Docker container image", TranslationService.Translate("Downloaded server image")); - s = s.Replace("container@pterodactyl~", "server@moonlight >"); + if (s.Contains("Moonlight Daemon") || s.Contains("Pterodactyl Daemon")) + { + s = s.Replace("[39m", "\x1b[0m"); + s = s.Replace("[33m", "[38;5;16;48;5;135m\x1b[39m"); + } - await Terminal.WriteLine(s); + s = s.Replace("[Pterodactyl Daemon]:", " Moonlight "); + s = s.Replace("[Moonlight Daemon]:", " Moonlight "); + s = s.Replace("Checking server disk space usage, this could take a few seconds...", TranslationService.Translate("Checking disk space")); + s = s.Replace("Updating process configuration files...", TranslationService.Translate("Updating config files")); + s = s.Replace("Ensuring file permissions are set correctly, this could take a few seconds...", TranslationService.Translate("Checking file permissions")); + s = s.Replace("Pulling Docker container image, this could take a few minutes to complete...", TranslationService.Translate("Downloading server image")); + s = s.Replace("Finished pulling Docker container image", TranslationService.Translate("Downloaded server image")); + s = s.Replace("container@pterodactyl~", "server@moonlight >"); + + await Terminal.WriteLine(s); + } } } @@ -85,9 +97,9 @@ private void RunOnFirstRender() { - lock (Console.MessageCache) + lock (Console.Messages) { - foreach (var message in Console.MessageCache.TakeLast(30)) + foreach (var message in Console.Messages) { OnMessage(null, message); } diff --git a/Moonlight/Shared/Components/ServerControl/ServerFiles.razor b/Moonlight/Shared/Components/ServerControl/ServerFiles.razor index 6267a9b8..9ce54cd2 100644 --- a/Moonlight/Shared/Components/ServerControl/ServerFiles.razor +++ b/Moonlight/Shared/Components/ServerControl/ServerFiles.razor @@ -4,6 +4,7 @@ @using Moonlight.App.Helpers.Files @using Moonlight.App.Services @using Moonlight.App.ApiClients.Wings +@using Moonlight.App.Helpers.Wings @inject WingsApiHelper WingsApiHelper @inject WingsJwtHelper WingsJwtHelper diff --git a/Moonlight/Shared/Components/ServerControl/ServerNavigation.razor b/Moonlight/Shared/Components/ServerControl/ServerNavigation.razor index 4d10f7b2..912b3c3e 100644 --- a/Moonlight/Shared/Components/ServerControl/ServerNavigation.razor +++ b/Moonlight/Shared/Components/ServerControl/ServerNavigation.razor @@ -1,9 +1,8 @@ -@using PteroConsole.NET -@using PteroConsole.NET.Enums -@using Task = System.Threading.Tasks.Task -@using Moonlight.App.Services +@using Moonlight.App.Services @using Moonlight.App.Database.Entities @using Moonlight.App.Helpers +@using Moonlight.App.Helpers.Wings +@using Moonlight.App.Helpers.Wings.Enums @inject SmartTranslateService TranslationService @@ -77,32 +76,32 @@ case ServerState.Starting: Starting - (@(Formatter.FormatUptime(Console.ServerResource.Uptime))) + (@(Formatter.FormatUptime(Console.Resource.Uptime))) break; case ServerState.Stopping: Stopping - (@(Formatter.FormatUptime(Console.ServerResource.Uptime))) + (@(Formatter.FormatUptime(Console.Resource.Uptime))) break; case ServerState.Running: Online - (@(Formatter.FormatUptime(Console.ServerResource.Uptime))) + (@(Formatter.FormatUptime(Console.Resource.Uptime))) break; }
Cpu: - @(Math.Round(Console.ServerResource.CpuAbsolute, 2))% + @(Math.Round(Console.Resource.CpuAbsolute, 2))%
Memory: - @(Formatter.FormatSize(Console.ServerResource.MemoryBytes)) / @(Formatter.FormatSize(Console.ServerResource.MemoryLimitBytes)) + @(Formatter.FormatSize(Console.Resource.MemoryBytes)) / @(Formatter.FormatSize(Console.Resource.MemoryLimitBytes))
Disk: - @(Formatter.FormatSize(Console.ServerResource.DiskBytes)) / @(Math.Round(CurrentServer.Disk / 1024f, 2)) GB + @(Formatter.FormatSize(Console.Resource.DiskBytes)) / @(Math.Round(CurrentServer.Disk / 1024f, 2)) GB
@@ -178,7 +177,7 @@ public User User { get; set; } [CascadingParameter] - public PteroConsole Console { get; set; } + public WingsConsole Console { get; set; } [Parameter] public RenderFragment ChildContent { get; set; } @@ -190,8 +189,8 @@ protected override void OnInitialized() { - Console.OnServerStateUpdated += async (sender, state) => { await InvokeAsync(StateHasChanged); }; - Console.OnServerResourceUpdated += async (sender, x) => { await InvokeAsync(StateHasChanged); }; + Console.OnServerStateUpdated += async (_, _) => { await InvokeAsync(StateHasChanged); }; + Console.OnResourceUpdated += async (_, _) => { await InvokeAsync(StateHasChanged); }; } #region Power Actions diff --git a/Moonlight/Shared/Views/Server/Index.razor b/Moonlight/Shared/Views/Server/Index.razor index 63754a15..bfa80cad 100644 --- a/Moonlight/Shared/Views/Server/Index.razor +++ b/Moonlight/Shared/Views/Server/Index.razor @@ -1,13 +1,13 @@ @page "/server/{ServerUuid}/{Route?}" -@using PteroConsole.NET @using Task = System.Threading.Tasks.Task @using Moonlight.App.Repositories.Servers -@using PteroConsole.NET.Enums @using Microsoft.EntityFrameworkCore @using Logging.Net @using Moonlight.App.Database.Entities @using Moonlight.App.Events @using Moonlight.App.Helpers +@using Moonlight.App.Helpers.Wings +@using Moonlight.App.Helpers.Wings.Enums @using Moonlight.App.Repositories @using Moonlight.App.Services @using Moonlight.Shared.Components.Xterm @@ -44,7 +44,7 @@ { if (NodeOnline) { - if (Console.ConnectionState == ConnectionState.Connected) + if (Console.ConsoleState == ConsoleState.Connected) { if (Console.ServerState == ServerState.Installing) { @@ -179,7 +179,7 @@ [Parameter] public string? Route { get; set; } - private PteroConsole? Console; + private WingsConsole? Console; private Server? CurrentServer; private Node Node; private bool NodeOnline = false; @@ -193,11 +193,11 @@ { Console = new(); - Console.OnConnectionStateUpdated += (_, _) => { InvokeAsync(StateHasChanged); }; - Console.OnServerResourceUpdated += async (_, _) => { await InvokeAsync(StateHasChanged); }; - Console.OnServerStateUpdated += async (_, _) => { await InvokeAsync(StateHasChanged); }; + Console.OnConsoleStateUpdated += (_, _) => { InvokeAsync(StateHasChanged); }; + Console.OnResourceUpdated += (_, _) => { InvokeAsync(StateHasChanged); }; + Console.OnServerStateUpdated += (_, _) => { InvokeAsync(StateHasChanged); }; - Console.RequestToken += (_) => WingsConsoleHelper.GenerateToken(CurrentServer!); + Console.OnRequestNewToken += async _ => await WingsConsoleHelper.GenerateToken(CurrentServer!); Console.OnMessage += async (_, s) => { @@ -205,7 +205,10 @@ { if (InstallConsole != null) { - await InstallConsole.WriteLine(s); + if (s.IsInternal) + await InstallConsole.WriteLine("\x1b[38;5;16;48;5;135m\x1b[39m\x1b[1m Moonlight \x1b[0m " + s.Content + "\x1b[0m"); + else + await InstallConsole.WriteLine(s.Content); } } }; @@ -280,7 +283,7 @@ await lazyLoader.SetText("Connecting to console"); - await WingsConsoleHelper.ConnectWings(Console!, CurrentServer); + await ReconnectConsole(); await Event.On($"server.{CurrentServer.Uuid}.installComplete", this, server => { @@ -295,6 +298,12 @@ Logger.Debug("Server is null"); } } + + private async Task ReconnectConsole() + { + await Console!.Disconnect(); + await WingsConsoleHelper.ConnectWings(Console!, CurrentServer!); + } public async void Dispose() {