Merge pull request #388 from Moonlight-Panel/v2_ImproveConsoleStreaming

Improved console streaming
This commit is contained in:
Masu Baumgartner
2024-04-17 15:43:32 +02:00
committed by GitHub
9 changed files with 106 additions and 72 deletions

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="MaterialThemeProjectNewConfig">
<option name="metadata">
<MTProjectMetadataState>
<option name="migrated" value="true" />
<option name="pristineConfig" value="false" />
<option name="userId" value="52a374ed:18c1029d858:-8000" />
<option name="version" value="8.13.2" />
</MTProjectMetadataState>
</option>
</component>
</project>

View File

@@ -55,9 +55,9 @@ public class AvatarController : Controller
if (!IdentityService.IsLoggedIn) if (!IdentityService.IsLoggedIn)
return StatusCode(403); return StatusCode(403);
if (ConfigService.Get().Security.EnforceAvatarPrivacy && if (ConfigService.Get().Security.EnforceAvatarPrivacy && // Do we need to enforce privacy?
id != IdentityService.CurrentUser.Id && id != IdentityService.CurrentUser.Id && // is the user not viewing his own image?
IdentityService.CurrentUser.Permissions < 1000) IdentityService.CurrentUser.Permissions < 1000) // and not an admin?
{ {
return StatusCode(403); return StatusCode(403);
} }

View File

@@ -22,13 +22,16 @@ public class DiagnoseService
{ {
using var scope = ServiceProvider.CreateScope(); using var scope = ServiceProvider.CreateScope();
// Create in memory zip archive
using var dataStream = new MemoryStream(); using var dataStream = new MemoryStream();
var zipArchive = new ZipArchive(dataStream, ZipArchiveMode.Create, true); var zipArchive = new ZipArchive(dataStream, ZipArchiveMode.Create, true);
// Call every plugin to perform their modifications to the file
await PluginService.ExecuteFuncAsync<IDiagnoseAction>( await PluginService.ExecuteFuncAsync<IDiagnoseAction>(
async x => await x.GenerateReport(zipArchive, scope.ServiceProvider) async x => await x.GenerateReport(zipArchive, scope.ServiceProvider)
); );
// Add a timestamp
await zipArchive.AddText("exported_at.txt", Formatter.FormatDate(DateTime.UtcNow)); await zipArchive.AddText("exported_at.txt", Formatter.FormatDate(DateTime.UtcNow));
zipArchive.Dispose(); zipArchive.Dispose();

View File

@@ -5,7 +5,7 @@ using MoonCore.Helpers;
namespace Moonlight.Core.Services; namespace Moonlight.Core.Services;
/// <summary> /// <summary>
/// This class provides a event to execute code when a user leaves the page /// This class provides an event to execute code when a user leaves the page
/// </summary> /// </summary>
[Scoped] [Scoped]

View File

@@ -2,12 +2,11 @@
using MoonCore.Helpers; using MoonCore.Helpers;
using Moonlight.Features.Servers.Api.Packets; using Moonlight.Features.Servers.Api.Packets;
using Moonlight.Features.Servers.Entities; using Moonlight.Features.Servers.Entities;
using Moonlight.Features.Servers.Models.Abstractions;
using Moonlight.Features.Servers.Models.Enums; using Moonlight.Features.Servers.Models.Enums;
namespace Moonlight.Features.Servers.Helpers; namespace Moonlight.Features.Servers.Helpers;
public class ServerConsole public class ServerConsole : IDisposable
{ {
public SmartEventHandler<ServerState> OnStateChange { get; set; } = new(); public SmartEventHandler<ServerState> OnStateChange { get; set; } = new();
public SmartEventHandler<ServerStats> OnStatsChange { get; set; } = new(); public SmartEventHandler<ServerStats> OnStatsChange { get; set; } = new();
@@ -23,7 +22,7 @@ public class ServerConsole
private readonly Server Server; private readonly Server Server;
private ClientWebSocket WebSocket; private ClientWebSocket WebSocket;
private WsPacketConnection PacketConnection; private AdvancedWebsocketStream WebsocketStream;
private CancellationTokenSource Cancellation = new(); private CancellationTokenSource Cancellation = new();
@@ -50,11 +49,11 @@ public class ServerConsole
wsUrl = $"ws://{Server.Node.Fqdn}:{Server.Node.HttpPort}/servers/{Server.Id}/ws"; wsUrl = $"ws://{Server.Node.Fqdn}:{Server.Node.HttpPort}/servers/{Server.Id}/ws";
await WebSocket.ConnectAsync(new Uri(wsUrl), CancellationToken.None); await WebSocket.ConnectAsync(new Uri(wsUrl), CancellationToken.None);
PacketConnection = new WsPacketConnection(WebSocket); WebsocketStream = new AdvancedWebsocketStream(WebSocket);
await PacketConnection.RegisterPacket<string>("output"); WebsocketStream.RegisterPacket<string>(1);
await PacketConnection.RegisterPacket<ServerState>("state"); WebsocketStream.RegisterPacket<ServerState>(2);
await PacketConnection.RegisterPacket<ServerStats>("stats"); WebsocketStream.RegisterPacket<ServerStats>(3);
Task.Run(Worker); Task.Run(Worker);
} }
@@ -65,7 +64,7 @@ public class ServerConsole
{ {
try try
{ {
var packet = await PacketConnection.Receive(); var packet = await WebsocketStream.ReceivePacket();
if (packet == null) if (packet == null)
continue; continue;
@@ -100,9 +99,11 @@ public class ServerConsole
} }
catch (Exception e) catch (Exception e)
{ {
if (e is not WebSocketException) if (e is WebSocketException)
Logger.Warn($"Lost connection to daemon server websocket: {e.Message}");
else
{ {
Logger.Warn("Lost connection to daemon server websocket"); Logger.Warn("Server console ws disconnected because of application error:");
Logger.Warn(e); Logger.Warn(e);
} }
@@ -111,7 +112,7 @@ public class ServerConsole
} }
await OnDisconnected.Invoke(); await OnDisconnected.Invoke();
await PacketConnection.Close(); await WebsocketStream.Close();
} }
public async Task Close() public async Task Close()
@@ -119,8 +120,8 @@ public class ServerConsole
if(!Cancellation.IsCancellationRequested) if(!Cancellation.IsCancellationRequested)
Cancellation.Cancel(); Cancellation.Cancel();
if(PacketConnection != null) if(WebsocketStream != null)
await PacketConnection.Close(); await WebsocketStream.Close();
} }
private string[] GetMessageCache() private string[] GetMessageCache()
@@ -128,4 +129,14 @@ public class ServerConsole
lock (MessageCache) lock (MessageCache)
return MessageCache.ToArray(); return MessageCache.ToArray();
} }
public async void Dispose()
{
MessageCache.Clear();
if (WebSocket.State == WebSocketState.Open)
await WebsocketStream.Close();
WebSocket.Dispose();
}
} }

View File

@@ -36,9 +36,9 @@ public class ServersControllers : Controller
var websocket = await HttpContext.WebSockets.AcceptWebSocketAsync(); var websocket = await HttpContext.WebSockets.AcceptWebSocketAsync();
// Build connection wrapper // Build connection wrapper
var wsPacketConnection = new WsPacketConnection(websocket); var websocketStream = new AdvancedWebsocketStream(websocket);
await wsPacketConnection.RegisterPacket<int>("amount"); websocketStream.RegisterPacket<int>(1);
await wsPacketConnection.RegisterPacket<ServerConfiguration>("serverConfiguration"); websocketStream.RegisterPacket<ServerConfiguration>(2);
// Read server data for the node // Read server data for the node
var node = (HttpContext.Items["Node"] as ServerNode)!; var node = (HttpContext.Items["Node"] as ServerNode)!;
@@ -62,13 +62,13 @@ public class ServersControllers : Controller
.ToArray(); .ToArray();
// Send the amount of configs the node will receive // Send the amount of configs the node will receive
await wsPacketConnection.Send(servers.Length); await websocketStream.SendPacket(servers.Length);
// Send the server configurations // Send the server configurations
foreach (var serverConfiguration in serverConfigurations) foreach (var serverConfiguration in serverConfigurations)
await wsPacketConnection.Send(serverConfiguration); await websocketStream.SendPacket(serverConfiguration);
await wsPacketConnection.WaitForClose(); await websocketStream.WaitForClose();
return Ok(); return Ok();
} }

View File

@@ -113,13 +113,7 @@
<ServerNavigation Index="@GetIndex()" ServerId="@Id"/> <ServerNavigation Index="@GetIndex()" ServerId="@Id"/>
<div class="mt-3"> <div class="mt-3">
@if (IsInstalling) @if (IsConsoleDisconnected)
{
<div class="card card-body bg-black p-3">
<Terminal @ref="InstallTerminal" EnableClipboard="false"/>
</div>
}
else if (IsConsoleDisconnected)
{ {
<IconAlert Title="Connection to server lost" Color="danger" Icon="bx-error"> <IconAlert Title="Connection to server lost" Color="danger" Icon="bx-error">
We lost the connection to the server. Please refresh the page in order to retry. If this error persists please contact the support We lost the connection to the server. Please refresh the page in order to retry. If this error persists please contact the support
@@ -137,6 +131,12 @@
The node this server is on is still booting. Please refresh the page in order to retry. If this error persists please contact the support The node this server is on is still booting. Please refresh the page in order to retry. If this error persists please contact the support
</IconAlert> </IconAlert>
} }
else if (IsInstalling)
{
<div class="card card-body bg-black p-3">
<Terminal @ref="InstallTerminal" EnableClipboard="false"/>
</div>
}
else else
{ {
<CascadingValue Value="Server"> <CascadingValue Value="Server">
@@ -217,37 +217,7 @@
Console = new ServerConsole(Server); Console = new ServerConsole(Server);
// Configure // Configure
Console.OnStateChange += async state => Console.OnStateChange += async state => await HandleStateChange(state);
{
// General rerender to update the state text in the ui
// NOTE: Obsolete because of the update timer
//await InvokeAsync(StateHasChanged);
// Change from offline to installing
// This will trigger the initialisation of the install view
if (state == ServerState.Installing && !IsInstalling)
{
IsInstalling = true;
// After this call, we should have access to the install terminal reference
await InvokeAsync(StateHasChanged);
Console.OnNewMessage += OnInstallConsoleMessage;
}
// Change from installing to offline
// This will trigger the destruction of the install view
else if (state == ServerState.Offline && IsInstalling)
{
IsInstalling = false;
Console.OnNewMessage -= OnInstallConsoleMessage;
// After this call, the install terminal will disappear
await InvokeAsync(StateHasChanged);
await ToastService.Info("Server installation complete");
}
};
Console.OnDisconnected += async () => Console.OnDisconnected += async () =>
{ {
@@ -267,7 +237,7 @@
await InvokeAsync(StateHasChanged); await InvokeAsync(StateHasChanged);
return; return;
} }
// We want to check if its an connection error, if yes we want to show the user that its an error with the connection // We want to check if its an connection error, if yes we want to show the user that its an error with the connection
// If not we proceed with the throwing for the soft error handler. // If not we proceed with the throwing for the soft error handler.
@@ -288,6 +258,41 @@
} }
UpdateTimer = new Timer(async _ => { await InvokeAsync(StateHasChanged); }, null, TimeSpan.Zero, TimeSpan.FromSeconds(1)); UpdateTimer = new Timer(async _ => { await InvokeAsync(StateHasChanged); }, null, TimeSpan.Zero, TimeSpan.FromSeconds(1));
var state = await ServerService.GetState(Server);
await HandleStateChange(state);
}
private async Task HandleStateChange(ServerState state)
{
// General rerender to update the state text in the ui
// NOTE: Obsolete because of the update timer
//await InvokeAsync(StateHasChanged);
// Change from offline to installing
// This will trigger the initialisation of the install view
if (state == ServerState.Installing && !IsInstalling)
{
IsInstalling = true;
// After this call, we should have access to the install terminal reference
await InvokeAsync(StateHasChanged);
Console.OnNewMessage += OnInstallConsoleMessage;
}
// Change from installing to offline
// This will trigger the destruction of the install view
else if (state == ServerState.Offline && IsInstalling)
{
IsInstalling = false;
Console.OnNewMessage -= OnInstallConsoleMessage;
// After this call, the install terminal will disappear
await InvokeAsync(StateHasChanged);
await ToastService.Info("Server installation complete");
}
} }
private async Task OnInstallConsoleMessage(string message) private async Task OnInstallConsoleMessage(string message)
@@ -308,7 +313,10 @@
await UpdateTimer.DisposeAsync(); await UpdateTimer.DisposeAsync();
if (Console != null) if (Console != null)
{
await Console.Close(); await Console.Close();
Console.Dispose();
}
} }
private int GetIndex() private int GetIndex()
@@ -331,7 +339,7 @@
case "/network": case "/network":
return 2; return 2;
case "/schedules": case "/schedules":
return 7; return 7;

View File

@@ -1,4 +1,3 @@
@using Moonlight.Features.Servers.Models.Abstractions
@using Moonlight.Features.Servers.Services @using Moonlight.Features.Servers.Services
@using Moonlight.Features.Servers.UI.Components @using Moonlight.Features.Servers.UI.Components
@using Moonlight.Features.Servers.Entities @using Moonlight.Features.Servers.Entities
@@ -6,7 +5,6 @@
@using Moonlight.Features.Servers.Api.Packets @using Moonlight.Features.Servers.Api.Packets
@using Moonlight.Features.Servers.Models.Enums @using Moonlight.Features.Servers.Models.Enums
@using MoonCore.Helpers @using MoonCore.Helpers
@using ApexCharts
@inject ServerService ServerService @inject ServerService ServerService
@@ -79,13 +77,14 @@
var text = ""; var text = "";
foreach (var line in ServerConsole.Messages.TakeLast(50)) foreach (var line in ServerConsole.Messages.TakeLast(50))
text += line + "\n\r"; {
var lineModified = line.Replace("\n", "\n\r");
text += lineModified + "\n\r";
}
await Terminal.Write(text); await Terminal.Write(text);
ServerConsole.OnNewMessage += OnMessage; ServerConsole.OnNewMessage += OnMessage;
ServerConsole.OnStatsChange += HandleStats; ServerConsole.OnStatsChange += HandleStats;
ServerConsole.OnStateChange += HandleState; ServerConsole.OnStateChange += HandleState;
} }
@@ -108,7 +107,7 @@
private async Task OnMessage(string message) private async Task OnMessage(string message)
{ {
await Terminal.WriteLine(message); await Terminal.Write(message + "\n\r");
} }
private async Task SendCommand() private async Task SendCommand()

View File

@@ -74,8 +74,7 @@
<Folder Include="Features\FileManager\Http\Requests\" /> <Folder Include="Features\FileManager\Http\Requests\" />
<Folder Include="Features\FileManager\Http\Resources\" /> <Folder Include="Features\FileManager\Http\Resources\" />
<Folder Include="Features\Servers\Http\Resources\" /> <Folder Include="Features\Servers\Http\Resources\" />
<Folder Include="storage\assetOverrides\x\y\" /> <Folder Include="storage\" />
<Folder Include="storage\logs\" />
<Folder Include="Styles\" /> <Folder Include="Styles\" />
<Folder Include="wwwroot\css\" /> <Folder Include="wwwroot\css\" />
</ItemGroup> </ItemGroup>
@@ -90,7 +89,7 @@
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="MoonCore" Version="1.1.9" /> <PackageReference Include="MoonCore" Version="1.2.7" />
<PackageReference Include="MoonCoreUI" Version="1.1.5" /> <PackageReference Include="MoonCoreUI" Version="1.1.5" />
<PackageReference Include="Otp.NET" Version="1.3.0" /> <PackageReference Include="Otp.NET" Version="1.3.0" />
<PackageReference Include="QRCoder" Version="1.4.3" /> <PackageReference Include="QRCoder" Version="1.4.3" />
@@ -114,6 +113,7 @@
<_ContentIncludedByDefault Remove="Features\FileManager\UI\Components\FileManager.razor" /> <_ContentIncludedByDefault Remove="Features\FileManager\UI\Components\FileManager.razor" />
<_ContentIncludedByDefault Remove="Features\FileManager\UI\Components\FileUploader.razor" /> <_ContentIncludedByDefault Remove="Features\FileManager\UI\Components\FileUploader.razor" />
<_ContentIncludedByDefault Remove="Features\FileManager\UI\Components\FileView.razor" /> <_ContentIncludedByDefault Remove="Features\FileManager\UI\Components\FileView.razor" />
<_ContentIncludedByDefault Remove="storage\configs\core.json" />
</ItemGroup> </ItemGroup>
</Project> </Project>