Merge pull request #241 from Moonlight-Panel/SmallFixes

Did some small fixes, added connection timeout check, improved ux
This commit is contained in:
Marcel Baumgartner 2023-08-02 03:07:47 +02:00 committed by GitHub
commit 26617d67f5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 170 additions and 87 deletions

View file

@ -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();

View file

@ -43,19 +43,22 @@ public class WingsFileAccess : FileAccess
$"api/servers/{Server.Uuid}/files/list-directory?directory={CurrentPath}"
);
var x = new List<FileData>();
var result = new List<FileData>();
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)

View file

@ -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<Server, MalwareScanResult[]> 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<Repository<Node>>();
ServerRepository = scope.ServiceProvider.GetRequiredService<Repository<Server>>();
NodeService = scope.ServiceProvider.GetRequiredService<NodeService>();
ServerService = scope.ServiceProvider.GetRequiredService<ServerService>();
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<Server[]> 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<Server, MalwareScanResult[]>();
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<MalwareScanResult[]> PerformScanOnServer(Server server, Container container)
private async Task<MalwareScanResult[]> PerformScanOnServer(Server server)
{
var results = new List<MalwareScanResult>();

View file

@ -9,8 +9,8 @@ namespace Moonlight.App.Services.Plugins;
public class PluginService
{
public List<MoonlightPlugin> Plugins { get; private set; }
public Dictionary<MoonlightPlugin, string> PluginFiles { get; private set; }
public readonly List<MoonlightPlugin> Plugins = new();
public readonly Dictionary<MoonlightPlugin, string> 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"))

View file

@ -11,22 +11,29 @@
@implements IDisposable
<div class="card bg-black rounded">
@if (ShowHeader)
{
<div class="card-header">
<span class="card-title">@(Header)</span>
</div>
}
<div class="card-body">
<MonacoEditor CssClass="h-100" @ref="Editor" Id="vseditor" ConstructionOptions="(x) => EditorOptions"/>
</div>
@if (!HideControls)
{
<div class="card-footer">
<div class="btn-group">
<WButton
Text="@(TranslationService.Translate("Save"))"
WorkingText="@(TranslationService.Translate("Saving"))"
<WButton
Text="@(TranslationService.Translate("Save"))"
WorkingText="@(TranslationService.Translate("Saving"))"
OnClick="Submit"></WButton>
<WButton
CssClasses="btn-danger"
Text="@(TranslationService.Translate("Cancel"))"
WorkingText="@(TranslationService.Translate("Canceling"))"
<WButton
CssClasses="btn-danger"
Text="@(TranslationService.Translate("Cancel"))"
WorkingText="@(TranslationService.Translate("Canceling"))"
OnClick="Cancel"></WButton>
</div>
</div>
@ -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]

View file

@ -17,7 +17,9 @@
Language="@EditorLanguage"
OnCancel="() => Cancel()"
OnSubmit="(_) => Save()"
HideControls="false">
HideControls="false"
ShowHeader="true"
Header="@(EditingFile.Name)">
</FileEditor>
}
else

View file

@ -35,28 +35,31 @@
<tbody class="fw-semibold text-gray-600">
<LazyLoader Load="Load">
<ContentBlock @ref="ContentBlock" AllowContentOverride="true">
<tr class="even">
<td class="w-10px">
</td>
<td>
<div class="d-flex align-items-center">
<span class="icon-wrapper">
<i class="bx bx-md bx-up-arrow-alt text-primary"></i>
</span>
<a href="#" @onclick:preventDefault @onclick="GoUp" class="ms-3 text-gray-800 text-hover-primary">
<TL>Go up</TL>
</a>
</div>
</td>
<td></td>
<td class="text-end">
<div class="d-flex justify-content-end">
<div class="ms-2">
@if (Access.CurrentPath != "/")
{
<tr class="even">
<td class="w-10px">
</td>
<td>
<div class="d-flex align-items-center">
<span class="icon-wrapper">
<i class="bx bx-md bx-up-arrow-alt text-primary"></i>
</span>
<a href="#" @onclick:preventDefault @onclick="GoUp" class="ms-3 text-gray-800 text-hover-primary">
<TL>Go up</TL>
</a>
</div>
</div>
</td>
</tr>
</td>
<td></td>
<td class="text-end">
<div class="d-flex justify-content-end">
<div class="ms-2">
</div>
</div>
</td>
</tr>
}
@foreach (var file in Data)
{
<tr class="even">

View file

@ -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)
{

View file

@ -38,6 +38,15 @@
}
else
{
<div class="mb-3">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="scanAllServers" @bind="MalwareScanService.ScanAllServers">
<label class="form-check-label" for="scanAllServers">
<TL>Scan all servers</TL>
</label>
</div>
</div>
<WButton Text="@(SmartTranslateService.Translate("Start scan"))"
CssClasses="btn-success"
OnClick="MalwareScanService.Start">

View file

@ -28,22 +28,22 @@
</div>
</div>
<div class="col-4 d-flex flex-column flex-end mb-1">
<div class="btn-group btn-group-sm">
<button class="w-100 nav-link btn btn-sm btn-success fw-bold px-4 me-1 @(Console.ServerState == ServerState.Offline ? "" : "disabled")" aria-selected="true" role="tab" @onclick="Start">
<div class="btn-group btn-group-md">
<button class="nav-link btn btn-md btn-success fw-bold px-4 me-1 @(Console.ServerState == ServerState.Offline ? "" : "disabled")" aria-selected="true" role="tab" @onclick="Start">
<TL>Start</TL>
</button>
<button class="w-100 nav-link btn btn-sm btn-primary fw-bold px-4 me-1 @(Console.ServerState == ServerState.Running ? "" : "disabled")" aria-selected="true" role="tab" @onclick="Restart">
<button class="nav-link btn btn-md btn-primary fw-bold px-4 me-1 @(Console.ServerState == ServerState.Running ? "" : "disabled")" aria-selected="true" role="tab" @onclick="Restart">
<TL>Restart</TL>
</button>
@if (Console.ServerState == ServerState.Stopping)
{
<button class="w-100 nav-link btn btn-sm btn-danger fw-bold px-4 me-1" aria-selected="true" role="tab" @onclick="Kill">
<button class="nav-link btn btn-md btn-danger fw-bold px-4 me-1" aria-selected="true" role="tab" @onclick="Kill">
<TL>Kill</TL>
</button>
}
else
{
<button class="w-100 nav-link btn btn-sm btn-danger fw-bold px-4 me-1 @(Console.ServerState == ServerState.Running || Console.ServerState == ServerState.Starting ? "" : "disabled")"
<button class="nav-link btn btn-md btn-danger fw-bold px-4 me-1 @(Console.ServerState == ServerState.Running || Console.ServerState == ServerState.Starting ? "" : "disabled")"
aria-selected="true" role="tab" @onclick="Stop">
<TL>Stop</TL>
</button>
@ -55,11 +55,11 @@
<div class="row mt-3">
<div class="card card-body">
<div class="row align-items-center">
<div class="col fs-5">
<div class="col fs-5 text-nowrap py-2">
<span class="fw-bold"><TL>Shared IP</TL>:</span>
<span class="ms-1 text-muted @(IdentityService.User.StreamerMode ? "blur" : "")">@($"{CurrentServer.Node.Fqdn}:{CurrentServer.MainAllocation?.Port ?? 0}")</span>
</div>
<div class="col fs-5">
<div class="col fs-5 py-2">
<span class="fw-bold"><TL>Server ID</TL>:</span>
<span class="ms-1 text-muted">
@if (IdentityService.User.Admin)
@ -72,7 +72,7 @@
}
</span>
</div>
<div class="col fs-5">
<div class="col fs-5 py-2">
<span class="fw-bold"><TL>Status</TL>:</span>
<span class="ms-1 text-muted">
@switch (Console.ServerState)
@ -106,15 +106,15 @@
}
</span>
</div>
<div class="col fs-5">
<div class="col fs-5 py-2">
<span class="fw-bold"><TL>Cpu</TL>:</span>
<span class="ms-1 text-muted">@(Math.Round(Console.Resource.CpuAbsolute / (CurrentServer.Cpu / 100f), 2))%</span>
</div>
<div class="col fs-5">
<div class="col fs-5 py-2">
<span class="fw-bold"><TL>Memory</TL>:</span>
<span class="ms-1 text-muted">@(Formatter.FormatSize(Console.Resource.MemoryBytes)) / @(Formatter.FormatSize(Console.Resource.MemoryLimitBytes))</span>
<span class="ms-1 text-muted">@(Formatter.FormatSize(Console.Resource.MemoryBytes)) / @Math.Round(CurrentServer.Memory / 1024f, 2) GB</span>
</div>
<div class="col fs-5">
<div class="col fs-5 py-2">
<span class="fw-bold"><TL>Disk</TL>:</span>
<span class="ms-1 text-muted">@(Formatter.FormatSize(Console.Resource.DiskBytes)) / @(Math.Round(CurrentServer.Disk / 1024f, 2)) GB</span>
</div>

View file

@ -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: {