moved diagnose to own controller, added advanced diagnose building, ui for advanced still missing

This commit is contained in:
mxritzdev
2025-05-13 17:22:47 +02:00
parent 0743bad93c
commit 609a0297d5
6 changed files with 155 additions and 35 deletions

View File

@@ -0,0 +1,44 @@
using Microsoft.AspNetCore.Mvc;
using MoonCore.Attributes;
using Moonlight.ApiServer.Services;
using Moonlight.Shared.Http.Requests.Admin.Sys;
using Moonlight.Shared.Http.Responses.Admin.Sys;
using Moonlight.Shared.Misc;
namespace Moonlight.ApiServer.Http.Controllers.Admin.Sys;
[ApiController]
[Route("api/admin/system/diagnose")]
public class DiagnoseController : Controller
{
private readonly DiagnoseService DiagnoseService;
public DiagnoseController(DiagnoseService diagnoseService)
{
DiagnoseService = diagnoseService;
}
[HttpPost]
[RequirePermission("admin.system.diagnose")]
public async Task<IActionResult> Diagnose([FromBody] string[]? requestedDiagnoseProviders = null)
{
var stream = new MemoryStream();
await DiagnoseService.GenerateDiagnose(stream, requestedDiagnoseProviders);
return File(stream, "application/zip", "diagnose.zip");
}
[HttpGet("available")]
[RequirePermission("admin.system.diagnose")]
public async Task<SystemAvailableDiagnoseProviderResponse> GetAvailable()
{
var availableProviders = await DiagnoseService.GetAvailable();
return new SystemAvailableDiagnoseProviderResponse()
{
AvailableProviders = availableProviders
};
}
}

View File

@@ -18,13 +18,12 @@ public class SystemController : Controller
{
private readonly ApplicationService ApplicationService;
private readonly IEnumerable<IDiagnoseProvider> DiagnoseProviders;
private readonly DiagnoseService DiagnoseService;
public SystemController(ApplicationService applicationService, IEnumerable<IDiagnoseProvider> diagnoseProviders, DiagnoseService diagnoseService)
public SystemController(ApplicationService applicationService, IEnumerable<IDiagnoseProvider> diagnoseProviders)
{
ApplicationService = applicationService;
DiagnoseProviders = diagnoseProviders;
DiagnoseService = diagnoseService;
}
[HttpGet]
@@ -46,15 +45,4 @@ public class SystemController : Controller
{
await ApplicationService.Shutdown();
}
[HttpGet("diagnose")]
[RequirePermission("admin.system.diagnose")]
public async Task<IActionResult> Diagnose()
{
var stream = new MemoryStream();
await DiagnoseService.GenerateDiagnose(stream);
return File(stream, "application/zip", "diagnose.zip");
}
}

View File

@@ -3,13 +3,14 @@ using Moonlight.ApiServer.Interfaces;
using Moonlight.ApiServer.Models.Diagnose;
using System.IO.Compression;
using MoonCore.Attributes;
using Moonlight.Shared.Http.Responses.Admin.Sys;
using Moonlight.Shared.Misc;
namespace Moonlight.ApiServer.Services;
[Scoped]
public class DiagnoseService
{
private readonly IEnumerable<IDiagnoseProvider> DiagnoseProviders;
public DiagnoseService(IEnumerable<IDiagnoseProvider> diagnoseProviders)
@@ -17,21 +18,62 @@ public class DiagnoseService
DiagnoseProviders = diagnoseProviders;
}
public async Task GenerateDiagnose(Stream outputStream)
public async Task<DiagnoseProvider[]> GetAvailable()
{
var tasks = DiagnoseProviders
var availableProviders = new List<DiagnoseProvider>();
foreach (var diagnoseProvider in DiagnoseProviders)
{
var name = diagnoseProvider.GetType().Name;
var type = diagnoseProvider.GetType().FullName;
// The type name is null if the type is a generic type, unlikely, but still could happen
if (type != null)
availableProviders.Add(new DiagnoseProvider()
{
Name = name,
Type = type
});
}
return availableProviders.ToArray();
}
public async Task GenerateDiagnose(Stream outputStream, string[]? requestedProviders)
{
List<IDiagnoseProvider> providers;
if (requestedProviders != null && requestedProviders.Length > 0)
{
providers = new List<IDiagnoseProvider>();
foreach (var requestedProvider in requestedProviders)
{
var provider = DiagnoseProviders.FirstOrDefault(x => x.GetType().FullName == requestedProvider);
if (provider != null)
providers.Add(provider);
}
}
else
{
providers = DiagnoseProviders.ToList();
}
var tasks = providers
.SelectMany(x => x.GetFiles())
.Select(async x => await ProcessDiagnoseEntry(null, x));
var fileEntries = (await Task.WhenAll(tasks))
.SelectMany(x => x)
.ToDictionary();
try
{
using (var zip = new ZipArchive(outputStream, ZipArchiveMode.Create, leaveOpen: true))
{
foreach (var kv in fileEntries)
@@ -57,11 +99,11 @@ public class DiagnoseService
throw new Exception($"An unknown error occured while building the Diagnose: {ex.Message}");
}
}
private async Task<Dictionary<string, byte[]>> ProcessDiagnoseEntry(string? path, DiagnoseEntry entry)
{
var fileEntries = new Dictionary<string, byte[]>();
switch (entry)
{
case DiagnoseDirectory diagnoseDirectory:
@@ -80,20 +122,20 @@ public class DiagnoseService
var file = await ProcessDiagnoseFile(path, diagnoseFile);
fileEntries.Add(file.Key, file.Value);
break;
}
}
return fileEntries;
}
private async Task<Dictionary<string, byte[]>> ProcessDiagnoseDirectory(string? path, DiagnoseDirectory directory)
{
var result = new Dictionary<string, byte[]>();
var directoryPath = path != null ? string.Join("/", path, directory.Name) : directory.Name;
var directoryPath = path != null ? string.Join("/", path, directory.Name) : directory.Name;
foreach (var entry in directory.Children)
{
var files = await ProcessDiagnoseEntry(directoryPath, entry);
@@ -103,14 +145,13 @@ public class DiagnoseService
result.Add(file.Key, file.Value);
}
}
return result;
}
private async Task<KeyValuePair<string, byte[]>> ProcessDiagnoseFile(string? path, DiagnoseFile file)
{
var filePath = path != null ? string.Join("/", path, file.Name) : file.Name;
var filePath = path != null ? string.Join("/", path, file.Name) : file.Name;
var bytes = file.GetContent();

View File

@@ -2,6 +2,8 @@
@using MoonCore.Attributes
@using MoonCore.Helpers
@using Moonlight.Shared.Http.Responses.Admin.Sys
@using Moonlight.Shared.Misc
@attribute [RequirePermission("admin.system.diagnose")]
@@ -25,22 +27,51 @@
The report includes useful information about your system, plugins, and environment, making it easier to identify problems or share with support.
</p>
<WButton OnClick="GenerateFrontend" CssClasses="btn btn-primary mt-5">Generate diagnose</WButton>
<WButton OnClick="GenerateDiagnose" CssClasses="btn btn-primary my-5">Generate diagnose</WButton>
@* <div> *@
@* <a class="text-primary cursor-pointer" @onclick:preventDefault @onclick="ToggleDropDown">Advanced <i class="icon-chevron-@(DropdownOpen ? "up" : "down")"></i></a> *@
@* *@
@* <div class="@(DropdownOpen ? "" : "hidden")"> *@
@* *@
@* *@
@* <LazyLoader Load="Load"> *@
@* *@
@* @for (int i = 0; i < AvailableProviders.Length; i++) *@
@* { *@
@* <div> *@
@* <input type="checkbox" @bind="@CheckedProviders[i]"/> @Formatter.ConvertCamelCaseToSpaces(AvailableProviders[i].Name) *@
@* </div> *@
@* } *@
@* *@
@* </LazyLoader> *@
@* *@
@* *@
@* </div> *@
@* *@
@* </div> *@
</div>
</div>
</div>
@code
{
private async Task GenerateFrontend(WButton _)
{
var stream = await ApiClient.GetStream("api/admin/system/diagnose");
private async Task GenerateDiagnose(WButton _)
{
var stream = await ApiClient.PostStream("api/admin/system/diagnose");
await DownloadService.DownloadStream("diagnose.zip", stream);
}
}
private async Task Load(LazyLoader arg)
{
// AvailableProviders = (await ApiClient.GetJson<SystemAvailableDiagnoseProviderResponse>("api/admin/system/diagnose/available")).AvailableProviders;
}
@code {
}

View File

@@ -0,0 +1,8 @@
using Moonlight.Shared.Misc;
namespace Moonlight.Shared.Http.Responses.Admin.Sys;
public class SystemAvailableDiagnoseProviderResponse
{
public DiagnoseProvider[] AvailableProviders { get; set; }= [];
}

View File

@@ -0,0 +1,8 @@
namespace Moonlight.Shared.Misc;
public class DiagnoseProvider
{
public string Name { get; set; }
public string Type { get; set; }
}