Cleaned up diagnose system. Fixed smaller inconsistencies
This commit is contained in:
@@ -10,6 +10,7 @@ namespace Moonlight.ApiServer.Http.Controllers.Admin.Sys;
|
|||||||
|
|
||||||
[ApiController]
|
[ApiController]
|
||||||
[Route("api/admin/system/diagnose")]
|
[Route("api/admin/system/diagnose")]
|
||||||
|
[RequirePermission("admin.system.diagnose")]
|
||||||
public class DiagnoseController : Controller
|
public class DiagnoseController : Controller
|
||||||
{
|
{
|
||||||
private readonly DiagnoseService DiagnoseService;
|
private readonly DiagnoseService DiagnoseService;
|
||||||
@@ -20,20 +21,21 @@ public class DiagnoseController : Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
[RequirePermission("admin.system.diagnose")]
|
public async Task Diagnose([FromBody] GenerateDiagnoseRequest request)
|
||||||
public async Task<IActionResult> Diagnose([FromBody] string[]? requestedDiagnoseProviders = null)
|
|
||||||
{
|
{
|
||||||
var stream = new MemoryStream();
|
var stream = await DiagnoseService.GenerateDiagnose(request.Providers);
|
||||||
|
|
||||||
await DiagnoseService.GenerateDiagnose(stream, requestedDiagnoseProviders);
|
await Results.Stream(
|
||||||
|
stream,
|
||||||
return File(stream, "application/zip", "diagnose.zip");
|
contentType: "application/zip",
|
||||||
|
fileDownloadName: "diagnose.zip"
|
||||||
|
)
|
||||||
|
.ExecuteAsync(HttpContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("available")]
|
[HttpGet("providers")]
|
||||||
[RequirePermission("admin.system.diagnose")]
|
public async Task<DiagnoseProvideResponse[]> GetProviders()
|
||||||
public async Task<DiagnoseProvideResponse[]> GetAvailable()
|
|
||||||
{
|
{
|
||||||
return await DiagnoseService.GetAvailable();
|
return await DiagnoseService.GetProviders();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -24,14 +24,12 @@ public class CoreConfigDiagnoseProvider : IDiagnoseProvider
|
|||||||
|
|
||||||
public async Task ModifyZipArchive(ZipArchive archive)
|
public async Task ModifyZipArchive(ZipArchive archive)
|
||||||
{
|
{
|
||||||
|
|
||||||
var json = JsonSerializer.Serialize(Config);
|
var json = JsonSerializer.Serialize(Config);
|
||||||
var config = JsonSerializer.Deserialize<AppConfiguration>(json);
|
var config = JsonSerializer.Deserialize<AppConfiguration>(json);
|
||||||
|
|
||||||
if (config == null)
|
if (config == null)
|
||||||
{
|
{
|
||||||
await archive.AddText("core/config.txt", "Could not fetch config.");
|
await archive.AddText("core/config.txt", "Could not fetch config.");
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,7 +43,15 @@ public class CoreConfigDiagnoseProvider : IDiagnoseProvider
|
|||||||
|
|
||||||
config.Authentication.OAuth2.ClientId = CheckForNullOrEmpty(config.Authentication.OAuth2.ClientId);
|
config.Authentication.OAuth2.ClientId = CheckForNullOrEmpty(config.Authentication.OAuth2.ClientId);
|
||||||
|
|
||||||
await archive.AddText("core/config.txt",
|
await archive.AddText(
|
||||||
JsonSerializer.Serialize(config, new JsonSerializerOptions() { WriteIndented = true }));
|
"core/config.txt",
|
||||||
|
JsonSerializer.Serialize(
|
||||||
|
config,
|
||||||
|
new JsonSerializerOptions()
|
||||||
|
{
|
||||||
|
WriteIndented = true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,4 @@
|
|||||||
using System.IO.Compression;
|
using System.IO.Compression;
|
||||||
using System.Text;
|
|
||||||
using MoonCore.Helpers;
|
|
||||||
using Moonlight.ApiServer.Extensions;
|
using Moonlight.ApiServer.Extensions;
|
||||||
using Moonlight.ApiServer.Interfaces;
|
using Moonlight.ApiServer.Interfaces;
|
||||||
|
|
||||||
@@ -10,15 +8,15 @@ public class LogsDiagnoseProvider : IDiagnoseProvider
|
|||||||
{
|
{
|
||||||
public async Task ModifyZipArchive(ZipArchive archive)
|
public async Task ModifyZipArchive(ZipArchive archive)
|
||||||
{
|
{
|
||||||
|
var path = Path.Combine("storage", "logs", "latest.log");
|
||||||
|
|
||||||
var logs = await File.ReadAllTextAsync(PathBuilder.File("storage", "logs", "latest.log"));
|
if (!File.Exists(path))
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(logs))
|
|
||||||
{
|
{
|
||||||
await archive.AddText("logs.txt", "Could not read the logs");
|
await archive.AddText("logs.txt", "Logs file latest.log has not been found");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await archive.AddText("logs.txt", logs);
|
var logsContent = await File.ReadAllTextAsync(path);
|
||||||
|
await archive.AddText("logs.txt", logsContent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
using Moonlight.ApiServer.Interfaces;
|
using Moonlight.ApiServer.Interfaces;
|
||||||
using System.IO.Compression;
|
using System.IO.Compression;
|
||||||
using MoonCore.Attributes;
|
using MoonCore.Attributes;
|
||||||
|
using MoonCore.Exceptions;
|
||||||
using Moonlight.Shared.Http.Responses.Admin.Sys;
|
using Moonlight.Shared.Http.Responses.Admin.Sys;
|
||||||
|
|
||||||
namespace Moonlight.ApiServer.Services;
|
namespace Moonlight.ApiServer.Services;
|
||||||
@@ -9,13 +10,18 @@ namespace Moonlight.ApiServer.Services;
|
|||||||
public class DiagnoseService
|
public class DiagnoseService
|
||||||
{
|
{
|
||||||
private readonly IEnumerable<IDiagnoseProvider> DiagnoseProviders;
|
private readonly IEnumerable<IDiagnoseProvider> DiagnoseProviders;
|
||||||
|
private readonly ILogger<DiagnoseService> Logger;
|
||||||
|
|
||||||
public DiagnoseService(IEnumerable<IDiagnoseProvider> diagnoseProviders)
|
public DiagnoseService(
|
||||||
|
IEnumerable<IDiagnoseProvider> diagnoseProviders,
|
||||||
|
ILogger<DiagnoseService> logger
|
||||||
|
)
|
||||||
{
|
{
|
||||||
DiagnoseProviders = diagnoseProviders;
|
DiagnoseProviders = diagnoseProviders;
|
||||||
|
Logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<DiagnoseProvideResponse[]> GetAvailable()
|
public Task<DiagnoseProvideResponse[]> GetProviders()
|
||||||
{
|
{
|
||||||
var availableProviders = new List<DiagnoseProvideResponse>();
|
var availableProviders = new List<DiagnoseProvideResponse>();
|
||||||
|
|
||||||
@@ -26,7 +32,9 @@ public class DiagnoseService
|
|||||||
var type = diagnoseProvider.GetType().FullName;
|
var type = diagnoseProvider.GetType().FullName;
|
||||||
|
|
||||||
// The type name is null if the type is a generic type, unlikely, but still could happen
|
// The type name is null if the type is a generic type, unlikely, but still could happen
|
||||||
if (type != null)
|
if (type == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
availableProviders.Add(new DiagnoseProvideResponse()
|
availableProviders.Add(new DiagnoseProvideResponse()
|
||||||
{
|
{
|
||||||
Name = name,
|
Name = name,
|
||||||
@@ -34,47 +42,54 @@ public class DiagnoseService
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return availableProviders.ToArray();
|
return Task.FromResult(
|
||||||
|
availableProviders.ToArray()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task GenerateDiagnose(Stream outputStream, string[]? requestedProviders)
|
public async Task<MemoryStream> GenerateDiagnose(string[] requestedProviders)
|
||||||
{
|
{
|
||||||
IDiagnoseProvider[] providers;
|
IDiagnoseProvider[] providers;
|
||||||
|
|
||||||
if (requestedProviders != null && requestedProviders.Length > 0)
|
if (requestedProviders.Length == 0)
|
||||||
|
providers = DiagnoseProviders.ToArray();
|
||||||
|
else
|
||||||
{
|
{
|
||||||
var requesteDiagnoseProviders = new List<IDiagnoseProvider>();
|
var foundProviders = new List<IDiagnoseProvider>();
|
||||||
|
|
||||||
foreach (var requestedProvider in requestedProviders)
|
foreach (var requestedProvider in requestedProviders)
|
||||||
{
|
{
|
||||||
var provider = DiagnoseProviders.FirstOrDefault(x => x.GetType().FullName == requestedProvider);
|
var provider = DiagnoseProviders.FirstOrDefault(x => x.GetType().FullName == requestedProvider);
|
||||||
|
|
||||||
if (provider != null)
|
if (provider == null)
|
||||||
requesteDiagnoseProviders.Add(provider);
|
continue;
|
||||||
|
|
||||||
|
foundProviders.Add(provider);
|
||||||
}
|
}
|
||||||
|
|
||||||
providers = requesteDiagnoseProviders.ToArray();
|
providers = foundProviders.ToArray();
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
providers = DiagnoseProviders.ToArray();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
using (var zip = new ZipArchive(outputStream, ZipArchiveMode.Create, leaveOpen: true))
|
var outputStream = new MemoryStream();
|
||||||
{
|
var zipArchive = new ZipArchive(outputStream, ZipArchiveMode.Create, leaveOpen: true);
|
||||||
|
|
||||||
foreach (var provider in providers)
|
foreach (var provider in providers)
|
||||||
{
|
{
|
||||||
await provider.ModifyZipArchive(zip);
|
await provider.ModifyZipArchive(zipArchive);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
outputStream.Seek(0, SeekOrigin.Begin);
|
zipArchive.Dispose();
|
||||||
|
|
||||||
|
outputStream.Position = 0;
|
||||||
|
return outputStream;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
throw new Exception($"An unknown error occured while building the Diagnose: {ex.Message}");
|
Logger.LogError("An unhandled error occured while generated the diagnose file: {e}", e);
|
||||||
|
|
||||||
|
throw new HttpApiException("An unhandled error occured while generating the diagnose file", 500);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
@using MoonCore.Attributes
|
@using MoonCore.Attributes
|
||||||
@using MoonCore.Helpers
|
@using MoonCore.Helpers
|
||||||
|
@using Moonlight.Shared.Http.Requests.Admin.Sys
|
||||||
@using Moonlight.Shared.Http.Responses.Admin.Sys
|
@using Moonlight.Shared.Http.Responses.Admin.Sys
|
||||||
@using Moonlight.Shared.Misc
|
|
||||||
|
|
||||||
@attribute [RequirePermission("admin.system.diagnose")]
|
@attribute [RequirePermission("admin.system.diagnose")]
|
||||||
|
|
||||||
@@ -22,27 +22,43 @@
|
|||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<p>
|
<p>
|
||||||
If you're experiencing issues or need help via our Discord, you're in the right place here!
|
If you're experiencing issues or need help via our Discord, you're in the right place here!
|
||||||
By pressing the button below, Moonlight will run all available diagnostic checks and package the results into a
|
By pressing the button below, Moonlight will run all available diagnostic checks and package the results
|
||||||
|
into a
|
||||||
downloadable zip file.
|
downloadable zip file.
|
||||||
The report includes useful information about your system, plugins, and environment, making it easier to identify problems or share with support.
|
The report includes useful information about your system, plugins, and environment, making it easier to
|
||||||
|
identify problems or share with support.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<WButton OnClick="GenerateDiagnose" CssClasses="btn btn-primary my-5">Generate diagnose</WButton>
|
<WButton OnClick="GenerateDiagnose" CssClasses="btn btn-primary my-5">Generate diagnose</WButton>
|
||||||
|
|
||||||
<div>
|
<div class="text-sm">
|
||||||
<a class="text-primary cursor-pointer" @onclick:preventDefault @onclick="ToggleDropDown">Advanced <i class="icon-chevron-@(DropdownOpen ? "up" : "down")"></i></a>
|
<a class="text-primary cursor-pointer flex items-center" @onclick:preventDefault
|
||||||
|
@onclick="ToggleDropDown">
|
||||||
|
<span class="me-1.5">Advanced</span>
|
||||||
|
@if (DropdownOpen)
|
||||||
|
{
|
||||||
|
<i class="icon-chevron-up"></i>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<i class="icon-chevron-down"></i>
|
||||||
|
}
|
||||||
|
</a>
|
||||||
|
|
||||||
<div class="@(DropdownOpen ? "" : "hidden")">
|
<div class="@(DropdownOpen ? "" : "hidden")">
|
||||||
<LazyLoader Load="Load">
|
<LazyLoader Load="Load">
|
||||||
<div class="mb-2 pb-2 pt-4 border-b border-white/5 flex items-center gap-3">
|
<div class="mb-2 py-2 border-b border-gray-700 flex items-center gap-3">
|
||||||
<input class="rounded" @bind="SelectAll" type="checkbox" id="selectall_checkbox"/>
|
<input id="selectall_checkbox" @bind="SelectAll" type="checkbox" class="form-checkbox">
|
||||||
<label for="selectall_checkbox">Select all</label>
|
<label for="selectall_checkbox">Select all</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@foreach (var item in AvailableProviders)
|
@foreach (var item in AvailableProviders)
|
||||||
{
|
{
|
||||||
<div class="mt-1 flex gap-3 items-center">
|
<div class="mt-1 flex gap-3 items-center">
|
||||||
<input class="rounded" type="checkbox" id="@(item.Key.Type + "_checkbox")" @bind="@AvailableProviders[item.Key]" />
|
<input class="form-checkbox" type="checkbox" id="@(item.Key.Type + "_checkbox")"
|
||||||
<label for="@(item.Key.Type + "_checkbox")">@Formatter.ConvertCamelCaseToSpaces(item.Key.Name)</label>
|
@bind="@AvailableProviders[item.Key]"/>
|
||||||
|
<label
|
||||||
|
for="@(item.Key.Type + "_checkbox")">@Formatter.ConvertCamelCaseToSpaces(item.Key.Name)</label>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
</LazyLoader>
|
</LazyLoader>
|
||||||
@@ -54,54 +70,51 @@
|
|||||||
|
|
||||||
@code
|
@code
|
||||||
{
|
{
|
||||||
|
|
||||||
private async Task GenerateDiagnose(WButton _)
|
|
||||||
{
|
|
||||||
string[] payload = [];
|
|
||||||
|
|
||||||
if (!SelectAll)
|
|
||||||
{
|
|
||||||
// filter the providers which have been selected if not all providers have been selected
|
|
||||||
payload = AvailableProviders
|
|
||||||
.Where(x => x.Value)
|
|
||||||
.Select(x => x.Key)
|
|
||||||
.Select(x => x.Type)
|
|
||||||
.ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
var stream = await ApiClient.PostStream("api/admin/system/diagnose", payload);
|
|
||||||
|
|
||||||
await DownloadService.DownloadStream("diagnose.zip", stream);
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool DropdownOpen = false;
|
private bool DropdownOpen = false;
|
||||||
private bool AllSelected = true;
|
|
||||||
private Dictionary<DiagnoseProvideResponse, bool> AvailableProviders;
|
private Dictionary<DiagnoseProvideResponse, bool> AvailableProviders;
|
||||||
|
|
||||||
private async Task ToggleDropDown()
|
|
||||||
{
|
|
||||||
DropdownOpen = !DropdownOpen;
|
|
||||||
|
|
||||||
await InvokeAsync(StateHasChanged);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task Load(LazyLoader arg)
|
|
||||||
{
|
|
||||||
AvailableProviders = (await ApiClient.GetJson<DiagnoseProvideResponse[]>("api/admin/system/diagnose/available"))
|
|
||||||
.ToDictionary(x => x, _ => true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool SelectAll
|
private bool SelectAll
|
||||||
{
|
{
|
||||||
get => AvailableProviders.Values.All(v => v);
|
get => AvailableProviders.Values.All(v => v);
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
// flip every entry to the new value
|
foreach (var k in AvailableProviders.Keys)
|
||||||
var keys = AvailableProviders.Keys.ToList();
|
|
||||||
foreach (var k in keys)
|
|
||||||
{
|
|
||||||
AvailableProviders[k] = value;
|
AvailableProviders[k] = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task Load(LazyLoader arg)
|
||||||
|
{
|
||||||
|
var providers = await ApiClient.GetJson<DiagnoseProvideResponse[]>(
|
||||||
|
"api/admin/system/diagnose/providers"
|
||||||
|
);
|
||||||
|
|
||||||
|
AvailableProviders = providers
|
||||||
|
.ToDictionary(x => x, _ => true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task GenerateDiagnose(WButton _)
|
||||||
|
{
|
||||||
|
var request = new GenerateDiagnoseRequest();
|
||||||
|
|
||||||
|
if (!SelectAll)
|
||||||
|
{
|
||||||
|
// filter the providers which have been selected if not all providers have been selected
|
||||||
|
request.Providers = AvailableProviders
|
||||||
|
.Where(x => x.Value)
|
||||||
|
.Select(x => x.Key.Type)
|
||||||
|
.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
var stream = await ApiClient.PostStream("api/admin/system/diagnose", request);
|
||||||
|
|
||||||
|
await DownloadService.DownloadStream("diagnose.zip", stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private async Task ToggleDropDown()
|
||||||
|
{
|
||||||
|
DropdownOpen = !DropdownOpen;
|
||||||
|
await InvokeAsync(StateHasChanged);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
namespace Moonlight.Shared.Http.Requests.Admin.Sys;
|
||||||
|
|
||||||
|
public class GenerateDiagnoseRequest
|
||||||
|
{
|
||||||
|
public string[] Providers { get; set; } = [];
|
||||||
|
}
|
||||||
@@ -3,6 +3,5 @@ namespace Moonlight.Shared.Http.Responses.Admin.Sys;
|
|||||||
public class DiagnoseProvideResponse
|
public class DiagnoseProvideResponse
|
||||||
{
|
{
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
|
|
||||||
public string Type { get; set; }
|
public string Type { get; set; }
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user