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
commit fd01787dfb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
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)
return StatusCode(403);
if (ConfigService.Get().Security.EnforceAvatarPrivacy &&
id != IdentityService.CurrentUser.Id &&
IdentityService.CurrentUser.Permissions < 1000)
if (ConfigService.Get().Security.EnforceAvatarPrivacy && // Do we need to enforce privacy?
id != IdentityService.CurrentUser.Id && // is the user not viewing his own image?
IdentityService.CurrentUser.Permissions < 1000) // and not an admin?
{
return StatusCode(403);
}

View file

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

View file

@ -5,7 +5,7 @@ using MoonCore.Helpers;
namespace Moonlight.Core.Services;
/// <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>
[Scoped]

View file

@ -2,12 +2,11 @@
using MoonCore.Helpers;
using Moonlight.Features.Servers.Api.Packets;
using Moonlight.Features.Servers.Entities;
using Moonlight.Features.Servers.Models.Abstractions;
using Moonlight.Features.Servers.Models.Enums;
namespace Moonlight.Features.Servers.Helpers;
public class ServerConsole
public class ServerConsole : IDisposable
{
public SmartEventHandler<ServerState> OnStateChange { get; set; } = new();
public SmartEventHandler<ServerStats> OnStatsChange { get; set; } = new();
@ -23,7 +22,7 @@ public class ServerConsole
private readonly Server Server;
private ClientWebSocket WebSocket;
private WsPacketConnection PacketConnection;
private AdvancedWebsocketStream WebsocketStream;
private CancellationTokenSource Cancellation = new();
@ -50,11 +49,11 @@ public class ServerConsole
wsUrl = $"ws://{Server.Node.Fqdn}:{Server.Node.HttpPort}/servers/{Server.Id}/ws";
await WebSocket.ConnectAsync(new Uri(wsUrl), CancellationToken.None);
PacketConnection = new WsPacketConnection(WebSocket);
WebsocketStream = new AdvancedWebsocketStream(WebSocket);
await PacketConnection.RegisterPacket<string>("output");
await PacketConnection.RegisterPacket<ServerState>("state");
await PacketConnection.RegisterPacket<ServerStats>("stats");
WebsocketStream.RegisterPacket<string>(1);
WebsocketStream.RegisterPacket<ServerState>(2);
WebsocketStream.RegisterPacket<ServerStats>(3);
Task.Run(Worker);
}
@ -65,7 +64,7 @@ public class ServerConsole
{
try
{
var packet = await PacketConnection.Receive();
var packet = await WebsocketStream.ReceivePacket();
if (packet == null)
continue;
@ -100,9 +99,11 @@ public class ServerConsole
}
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);
}
@ -111,7 +112,7 @@ public class ServerConsole
}
await OnDisconnected.Invoke();
await PacketConnection.Close();
await WebsocketStream.Close();
}
public async Task Close()
@ -119,8 +120,8 @@ public class ServerConsole
if(!Cancellation.IsCancellationRequested)
Cancellation.Cancel();
if(PacketConnection != null)
await PacketConnection.Close();
if(WebsocketStream != null)
await WebsocketStream.Close();
}
private string[] GetMessageCache()
@ -128,4 +129,14 @@ public class ServerConsole
lock (MessageCache)
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();
// Build connection wrapper
var wsPacketConnection = new WsPacketConnection(websocket);
await wsPacketConnection.RegisterPacket<int>("amount");
await wsPacketConnection.RegisterPacket<ServerConfiguration>("serverConfiguration");
var websocketStream = new AdvancedWebsocketStream(websocket);
websocketStream.RegisterPacket<int>(1);
websocketStream.RegisterPacket<ServerConfiguration>(2);
// Read server data for the node
var node = (HttpContext.Items["Node"] as ServerNode)!;
@ -62,13 +62,13 @@ public class ServersControllers : Controller
.ToArray();
// Send the amount of configs the node will receive
await wsPacketConnection.Send(servers.Length);
await websocketStream.SendPacket(servers.Length);
// Send the server configurations
foreach (var serverConfiguration in serverConfigurations)
await wsPacketConnection.Send(serverConfiguration);
await websocketStream.SendPacket(serverConfiguration);
await wsPacketConnection.WaitForClose();
await websocketStream.WaitForClose();
return Ok();
}

View file

@ -113,13 +113,7 @@
<ServerNavigation Index="@GetIndex()" ServerId="@Id"/>
<div class="mt-3">
@if (IsInstalling)
{
<div class="card card-body bg-black p-3">
<Terminal @ref="InstallTerminal" EnableClipboard="false"/>
</div>
}
else if (IsConsoleDisconnected)
@if (IsConsoleDisconnected)
{
<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
@ -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
</IconAlert>
}
else if (IsInstalling)
{
<div class="card card-body bg-black p-3">
<Terminal @ref="InstallTerminal" EnableClipboard="false"/>
</div>
}
else
{
<CascadingValue Value="Server">
@ -217,37 +217,7 @@
Console = new ServerConsole(Server);
// Configure
Console.OnStateChange += async 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.OnStateChange += async state => await HandleStateChange(state);
Console.OnDisconnected += async () =>
{
@ -267,7 +237,7 @@
await InvokeAsync(StateHasChanged);
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
// 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));
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)
@ -308,7 +313,10 @@
await UpdateTimer.DisposeAsync();
if (Console != null)
{
await Console.Close();
Console.Dispose();
}
}
private int GetIndex()
@ -331,7 +339,7 @@
case "/network":
return 2;
case "/schedules":
return 7;

View file

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

View file

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