From 600bec3417825594e8e6c2bb0990d5aa6b518a34 Mon Sep 17 00:00:00 2001 From: Marcel Baumgartner Date: Wed, 2 Aug 2023 03:06:54 +0200 Subject: [PATCH] Did some small fixes, added connection timeout check, improved ux --- Moonlight/App/Configuration/ConfigV1.cs | 9 ++ .../App/Helpers/Files/WingsFileAccess.cs | 19 ++-- .../Services/Background/MalwareScanService.cs | 88 +++++++++++-------- .../App/Services/Plugins/PluginService.cs | 7 +- .../FileManagerPartials/FileEditor.razor | 29 ++++-- .../FileManagerPartials/FileManager.razor | 4 +- .../FileManagerPartials/FileView.razor | 45 +++++----- Moonlight/Shared/Layouts/MainLayout.razor | 8 ++ .../Shared/Views/Admin/Security/Malware.razor | 9 ++ .../Views/Server/ServerNavigation.razor | 24 ++--- Moonlight/wwwroot/assets/js/moonlight.js | 15 ++++ 11 files changed, 170 insertions(+), 87 deletions(-) diff --git a/Moonlight/App/Configuration/ConfigV1.cs b/Moonlight/App/Configuration/ConfigV1.cs index 147301d..11ce0d2 100644 --- a/Moonlight/App/Configuration/ConfigV1.cs +++ b/Moonlight/App/Configuration/ConfigV1.cs @@ -17,6 +17,15 @@ public class ConfigV1 [Description("The url moonlight is accesible with from the internet")] public string AppUrl { get; set; } = "http://your-moonlight-url-without-slash"; + [JsonProperty("EnableLatencyCheck")] + [Description( + "This will enable a latency check for connections to moonlight. Users with an too high latency will be warned that moonlight might be buggy for them")] + public bool EnableLatencyCheck { get; set; } = true; + + [JsonProperty("LatencyCheckThreshold")] + [Description("Specify the latency threshold which has to be reached in order to trigger the warning message")] + public int LatencyCheckThreshold { get; set; } = 500; + [JsonProperty("Auth")] public AuthData Auth { get; set; } = new(); [JsonProperty("Database")] public DatabaseData Database { get; set; } = new(); diff --git a/Moonlight/App/Helpers/Files/WingsFileAccess.cs b/Moonlight/App/Helpers/Files/WingsFileAccess.cs index 70bebf6..8070a50 100644 --- a/Moonlight/App/Helpers/Files/WingsFileAccess.cs +++ b/Moonlight/App/Helpers/Files/WingsFileAccess.cs @@ -43,19 +43,22 @@ public class WingsFileAccess : FileAccess $"api/servers/{Server.Uuid}/files/list-directory?directory={CurrentPath}" ); - var x = new List(); + var result = new List(); - foreach (var response in res) + foreach (var resGrouped in res.GroupBy(x => x.Directory)) { - x.Add(new() + foreach (var resItem in resGrouped.OrderBy(x => x.Name)) { - Name = response.Name, - Size = response.File ? response.Size : 0, - IsFile = response.File, - }); + result.Add(new() + { + Name = resItem.Name, + Size = resItem.File ? resItem.Size : 0, + IsFile = resItem.File, + }); + } } - return x.ToArray(); + return result.ToArray(); } public override Task Cd(string dir) diff --git a/Moonlight/App/Services/Background/MalwareScanService.cs b/Moonlight/App/Services/Background/MalwareScanService.cs index d80fd2c..989b4a8 100644 --- a/Moonlight/App/Services/Background/MalwareScanService.cs +++ b/Moonlight/App/Services/Background/MalwareScanService.cs @@ -19,6 +19,7 @@ public class MalwareScanService private readonly IServiceScopeFactory ServiceScopeFactory; public bool IsRunning { get; private set; } + public bool ScanAllServers { get; set; } public readonly Dictionary ScanResults; public string Status { get; private set; } = "N/A"; @@ -26,7 +27,6 @@ public class MalwareScanService { ServiceScopeFactory = serviceScopeFactory; Event = eventSystem; - ScanResults = new(); } @@ -42,6 +42,7 @@ public class MalwareScanService private async Task Run() { + // Clean results IsRunning = true; Status = "Clearing last results"; await Event.Emit("malwareScan.status", IsRunning); @@ -53,6 +54,55 @@ public class MalwareScanService await Event.Emit("malwareScan.result"); + // Load servers to scan + + using var scope = ServiceScopeFactory.CreateScope(); + + // Load services from di scope + NodeRepository = scope.ServiceProvider.GetRequiredService>(); + ServerRepository = scope.ServiceProvider.GetRequiredService>(); + NodeService = scope.ServiceProvider.GetRequiredService(); + ServerService = scope.ServiceProvider.GetRequiredService(); + + Status = "Fetching servers to scan"; + await Event.Emit("malwareScan.status", IsRunning); + + Server[] servers; + + if (ScanAllServers) + servers = ServerRepository.Get().ToArray(); + else + servers = await GetOnlineServers(); + + // Perform scan + + int i = 1; + foreach (var server in servers) + { + Status = $"[{i} / {servers.Length}] Scanning server {server.Name}"; + await Event.Emit("malwareScan.status", IsRunning); + + var results = await PerformScanOnServer(server); + + if (results.Any()) + { + lock (ScanResults) + { + ScanResults.Add(server, results); + } + + await Event.Emit("malwareScan.result"); + } + + i++; + } + + IsRunning = false; + await Event.Emit("malwareScan.status", IsRunning); + } + + private async Task GetOnlineServers() + { using var scope = ServiceScopeFactory.CreateScope(); // Load services from di scope @@ -102,43 +152,11 @@ public class MalwareScanService containerServerMapped.Add(server, container); } } - - // Perform scan - var resultsMapped = new Dictionary(); - foreach (var mapping in containerServerMapped) - { - Logger.Verbose($"Scanning server {mapping.Key.Name} for malware"); - - Status = $"Scanning server {mapping.Key.Name} for malware"; - await Event.Emit("malwareScan.status", IsRunning); - - var results = await PerformScanOnServer(mapping.Key, mapping.Value); - if (results.Any()) - { - resultsMapped.Add(mapping.Key, results); - Logger.Verbose($"{results.Length} findings on server {mapping.Key.Name}"); - } - } - - Logger.Verbose($"Scan complete. Detected {resultsMapped.Count} servers with findings"); - - IsRunning = false; - Status = $"Scan complete. Detected {resultsMapped.Count} servers with findings"; - await Event.Emit("malwareScan.status", IsRunning); - - lock (ScanResults) - { - foreach (var mapping in resultsMapped) - { - ScanResults.Add(mapping.Key, mapping.Value); - } - } - - await Event.Emit("malwareScan.result"); + return containerServerMapped.Keys.ToArray(); } - private async Task PerformScanOnServer(Server server, Container container) + private async Task PerformScanOnServer(Server server) { var results = new List(); diff --git a/Moonlight/App/Services/Plugins/PluginService.cs b/Moonlight/App/Services/Plugins/PluginService.cs index 893bd46..641f658 100644 --- a/Moonlight/App/Services/Plugins/PluginService.cs +++ b/Moonlight/App/Services/Plugins/PluginService.cs @@ -9,8 +9,8 @@ namespace Moonlight.App.Services.Plugins; public class PluginService { - public List Plugins { get; private set; } - public Dictionary PluginFiles { get; private set; } + public readonly List Plugins = new(); + public readonly Dictionary PluginFiles = new(); public PluginService() { @@ -19,6 +19,9 @@ public class PluginService public Task ReloadPlugins() { + PluginFiles.Clear(); + Plugins.Clear(); + // Try to update all plugins ending with .dll.cache foreach (var pluginFile in Directory.EnumerateFiles( PathBuilder.Dir(Directory.GetCurrentDirectory(), "storage", "plugins")) diff --git a/Moonlight/Shared/Components/FileManagerPartials/FileEditor.razor b/Moonlight/Shared/Components/FileManagerPartials/FileEditor.razor index 31d7eaa..4f06d10 100644 --- a/Moonlight/Shared/Components/FileManagerPartials/FileEditor.razor +++ b/Moonlight/Shared/Components/FileManagerPartials/FileEditor.razor @@ -11,22 +11,29 @@ @implements IDisposable
+ @if (ShowHeader) + { +
+ @(Header) +
+ } +
- + @if (!HideControls) { @@ -43,6 +50,12 @@ [Parameter] public bool HideControls { get; set; } = false; + + [Parameter] + public bool ShowHeader { get; set; } = false; + + [Parameter] + public string Header { get; set; } = "Header.changeme.txt"; // Events [Parameter] diff --git a/Moonlight/Shared/Components/FileManagerPartials/FileManager.razor b/Moonlight/Shared/Components/FileManagerPartials/FileManager.razor index 95b8b04..ddd8358 100644 --- a/Moonlight/Shared/Components/FileManagerPartials/FileManager.razor +++ b/Moonlight/Shared/Components/FileManagerPartials/FileManager.razor @@ -17,7 +17,9 @@ Language="@EditorLanguage" OnCancel="() => Cancel()" OnSubmit="(_) => Save()" - HideControls="false"> + HideControls="false" + ShowHeader="true" + Header="@(EditingFile.Name)"> } else diff --git a/Moonlight/Shared/Components/FileManagerPartials/FileView.razor b/Moonlight/Shared/Components/FileManagerPartials/FileView.razor index b1ef5e2..af60ab4 100644 --- a/Moonlight/Shared/Components/FileManagerPartials/FileView.razor +++ b/Moonlight/Shared/Components/FileManagerPartials/FileView.razor @@ -35,28 +35,31 @@ - - - - -
- - - - - Go up - -
- - - -
-
- + @if (Access.CurrentPath != "/") + { + + + + +
+ + + + + Go up +
-
- - + + + +
+
+ +
+
+ + + } @foreach (var file in Data) { diff --git a/Moonlight/Shared/Layouts/MainLayout.razor b/Moonlight/Shared/Layouts/MainLayout.razor index 6ea2985..85bfe81 100644 --- a/Moonlight/Shared/Layouts/MainLayout.razor +++ b/Moonlight/Shared/Layouts/MainLayout.razor @@ -22,6 +22,7 @@ @inject IpBanService IpBanService @inject DynamicBackgroundService DynamicBackgroundService @inject KeyListenerService KeyListenerService +@inject ConfigService ConfigService @{ var uri = new Uri(NavigationManager.Uri); @@ -245,6 +246,13 @@ RunDelayedMenu(1); RunDelayedMenu(3); RunDelayedMenu(5); + + if (ConfigService.Get().Moonlight.EnableLatencyCheck) + { + await JsRuntime.InvokeVoidAsync("moonlight.loading.checkConnection", + ConfigService.Get().Moonlight.AppUrl, + ConfigService.Get().Moonlight.LatencyCheckThreshold); + } } catch (Exception) { diff --git a/Moonlight/Shared/Views/Admin/Security/Malware.razor b/Moonlight/Shared/Views/Admin/Security/Malware.razor index 958b4f1..195ef0b 100644 --- a/Moonlight/Shared/Views/Admin/Security/Malware.razor +++ b/Moonlight/Shared/Views/Admin/Security/Malware.razor @@ -38,6 +38,15 @@ } else { +
+
+ + +
+
+ diff --git a/Moonlight/Shared/Views/Server/ServerNavigation.razor b/Moonlight/Shared/Views/Server/ServerNavigation.razor index ebb37a7..ce7bf59 100644 --- a/Moonlight/Shared/Views/Server/ServerNavigation.razor +++ b/Moonlight/Shared/Views/Server/ServerNavigation.razor @@ -28,22 +28,22 @@
-
- - @if (Console.ServerState == ServerState.Stopping) { - } else { - @@ -55,11 +55,11 @@
-
+
Shared IP: @($"{CurrentServer.Node.Fqdn}:{CurrentServer.MainAllocation?.Port ?? 0}")
-
+
Server ID: @if (IdentityService.User.Admin) @@ -72,7 +72,7 @@ }
-
+
Status: @switch (Console.ServerState) @@ -106,15 +106,15 @@ }
-
+
Cpu: @(Math.Round(Console.Resource.CpuAbsolute / (CurrentServer.Cpu / 100f), 2))%
-
+
Memory: - @(Formatter.FormatSize(Console.Resource.MemoryBytes)) / @(Formatter.FormatSize(Console.Resource.MemoryLimitBytes)) + @(Formatter.FormatSize(Console.Resource.MemoryBytes)) / @Math.Round(CurrentServer.Memory / 1024f, 2) GB
-
+
Disk: @(Formatter.FormatSize(Console.Resource.DiskBytes)) / @(Math.Round(CurrentServer.Disk / 1024f, 2)) GB
diff --git a/Moonlight/wwwroot/assets/js/moonlight.js b/Moonlight/wwwroot/assets/js/moonlight.js index cafd22f..5ab0ec2 100644 --- a/Moonlight/wwwroot/assets/js/moonlight.js +++ b/Moonlight/wwwroot/assets/js/moonlight.js @@ -313,6 +313,21 @@ 'editor.background': '#000000' } }); + }, + checkConnection: async function(url, threshold) { + const start = performance.now(); + + try + { + const response = await fetch(url, { mode: 'no-cors' }); + const latency = performance.now() - start; + + if (latency > threshold) + { + moonlight.toasts.warning(`High latency detected: ${latency}ms. Moonlight might feel laggy. Please check your internet connection`); + } + } + catch (error) {} } }, flashbang: {