Merge pull request #241 from Moonlight-Panel/SmallFixes
Did some small fixes, added connection timeout check, improved ux
This commit is contained in:
commit
26617d67f5
|
@ -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();
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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>();
|
||||
|
||||
|
|
|
@ -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"))
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -17,7 +17,9 @@
|
|||
Language="@EditorLanguage"
|
||||
OnCancel="() => Cancel()"
|
||||
OnSubmit="(_) => Save()"
|
||||
HideControls="false">
|
||||
HideControls="false"
|
||||
ShowHeader="true"
|
||||
Header="@(EditingFile.Name)">
|
||||
</FileEditor>
|
||||
}
|
||||
else
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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>
|
||||
|
|
15
Moonlight/wwwroot/assets/js/moonlight.js
vendored
15
Moonlight/wwwroot/assets/js/moonlight.js
vendored
|
@ -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: {
|
||||
|
|
Loading…
Reference in a new issue