changed the diagnose to be easier to use
This commit is contained in:
33
Moonlight.ApiServer/Extensions/ZipArchiveExtensions.cs
Normal file
33
Moonlight.ApiServer/Extensions/ZipArchiveExtensions.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
using System.IO.Compression;
|
||||
using System.Text;
|
||||
|
||||
namespace Moonlight.ApiServer.Extensions;
|
||||
|
||||
public static class ZipArchiveExtensions
|
||||
{
|
||||
public static async Task AddBinary(this ZipArchive archive, string name, byte[] bytes)
|
||||
{
|
||||
var entry = archive.CreateEntry(name);
|
||||
await using var dataStream = entry.Open();
|
||||
|
||||
await dataStream.WriteAsync(bytes);
|
||||
await dataStream.FlushAsync();
|
||||
}
|
||||
|
||||
public static async Task AddText(this ZipArchive archive, string name, string content)
|
||||
{
|
||||
var data = Encoding.UTF8.GetBytes(content);
|
||||
await archive.AddBinary(name, data);
|
||||
}
|
||||
|
||||
public static async Task AddFile(this ZipArchive archive, string name, string path)
|
||||
{
|
||||
var fs = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
|
||||
var entry = archive.CreateEntry(name);
|
||||
await using var dataStream = entry.Open();
|
||||
|
||||
await fs.CopyToAsync(dataStream);
|
||||
await dataStream.FlushAsync();
|
||||
}
|
||||
}
|
||||
@@ -32,13 +32,8 @@ public class DiagnoseController : Controller
|
||||
|
||||
[HttpGet("available")]
|
||||
[RequirePermission("admin.system.diagnose")]
|
||||
public async Task<SystemAvailableDiagnoseProviderResponse> GetAvailable()
|
||||
public async Task<DiagnoseProvideResponse[]> GetAvailable()
|
||||
{
|
||||
var availableProviders = await DiagnoseService.GetAvailable();
|
||||
|
||||
return new SystemAvailableDiagnoseProviderResponse()
|
||||
{
|
||||
AvailableProviders = availableProviders
|
||||
};
|
||||
return await DiagnoseService.GetAvailable();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
using System.IO.Compression;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using MoonCore.Helpers;
|
||||
using Moonlight.ApiServer.Configuration;
|
||||
using Moonlight.ApiServer.Extensions;
|
||||
using Moonlight.ApiServer.Interfaces;
|
||||
using Moonlight.ApiServer.Models.Diagnose;
|
||||
|
||||
namespace Moonlight.ApiServer.Implementations.Diagnose;
|
||||
|
||||
public class CoreConfigDiagnoseProvider : IDiagnoseProvider
|
||||
{
|
||||
private readonly AppConfiguration Config;
|
||||
|
||||
public CoreConfigDiagnoseProvider(AppConfiguration config)
|
||||
{
|
||||
Config = config;
|
||||
}
|
||||
|
||||
private string CheckForNullOrEmpty(string? content)
|
||||
{
|
||||
return string.IsNullOrEmpty(content)
|
||||
? "ISEMPTY"
|
||||
: "ISNOTEMPTY";
|
||||
}
|
||||
|
||||
public async Task ModifyZipArchive(ZipArchive archive)
|
||||
{
|
||||
|
||||
var json = JsonSerializer.Serialize(Config);
|
||||
var config = JsonSerializer.Deserialize<AppConfiguration>(json);
|
||||
|
||||
if (config == null)
|
||||
{
|
||||
await archive.AddText("core/config.txt","Could not fetch config.");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
config.Database.Password = CheckForNullOrEmpty(config.Database.Password);
|
||||
|
||||
config.Authentication.OAuth2.ClientSecret = CheckForNullOrEmpty(config.Authentication.OAuth2.ClientSecret);
|
||||
|
||||
config.Authentication.OAuth2.Secret = CheckForNullOrEmpty(config.Authentication.OAuth2.Secret);
|
||||
|
||||
config.Authentication.Secret = CheckForNullOrEmpty(config.Authentication.Secret);
|
||||
|
||||
await archive.AddText("core/config.txt",
|
||||
JsonSerializer.Serialize(config, new JsonSerializerOptions() { WriteIndented = true }));
|
||||
}
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using MoonCore.Helpers;
|
||||
using Moonlight.ApiServer.Configuration;
|
||||
using Moonlight.ApiServer.Interfaces;
|
||||
using Moonlight.ApiServer.Models.Diagnose;
|
||||
|
||||
namespace Moonlight.ApiServer.Implementations.Diagnose;
|
||||
|
||||
public class CoreDiagnoseProvider : IDiagnoseProvider
|
||||
{
|
||||
private readonly AppConfiguration Config;
|
||||
|
||||
public CoreDiagnoseProvider(AppConfiguration config)
|
||||
{
|
||||
Config = config;
|
||||
}
|
||||
|
||||
public DiagnoseEntry[] GetFiles()
|
||||
{
|
||||
return
|
||||
[
|
||||
new DiagnoseDirectory()
|
||||
{
|
||||
Name = "core",
|
||||
Children = [
|
||||
|
||||
new DiagnoseFile()
|
||||
{
|
||||
Name = "logs.txt",
|
||||
GetContent = () =>
|
||||
{
|
||||
var logs = File.ReadAllText(PathBuilder.File("storage", "logs", "latest.log"));
|
||||
|
||||
if (string.IsNullOrEmpty(logs))
|
||||
return Encoding.UTF8.GetBytes("Could not get the latest logs.");
|
||||
|
||||
return Encoding.UTF8.GetBytes(logs);
|
||||
}
|
||||
},
|
||||
|
||||
new DiagnoseFile()
|
||||
{
|
||||
Name = "config.txt",
|
||||
GetContent = () =>
|
||||
{
|
||||
var json = JsonSerializer.Serialize(Config);
|
||||
var config = JsonSerializer.Deserialize<AppConfiguration>(json);
|
||||
|
||||
if (config == null)
|
||||
{
|
||||
return Encoding.UTF8.GetBytes("Could not fetch config.");
|
||||
}
|
||||
|
||||
config.Database.Password = CheckForNullOrEmpty(config.Database.Password);
|
||||
|
||||
config.Authentication.OAuth2.ClientSecret = CheckForNullOrEmpty(config.Authentication.OAuth2.ClientSecret);
|
||||
|
||||
config.Authentication.OAuth2.Secret = CheckForNullOrEmpty(config.Authentication.OAuth2.Secret);
|
||||
|
||||
config.Authentication.Secret = CheckForNullOrEmpty(config.Authentication.Secret);
|
||||
|
||||
return Encoding.UTF8.GetBytes(JsonSerializer.Serialize(config, new JsonSerializerOptions() { WriteIndented = true}));
|
||||
}
|
||||
}
|
||||
|
||||
]
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
private string CheckForNullOrEmpty(string? content)
|
||||
{
|
||||
return string.IsNullOrEmpty(content)
|
||||
? "ISEMPTY"
|
||||
: "ISNOTEMPTY";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using System.IO.Compression;
|
||||
using System.Text;
|
||||
using MoonCore.Helpers;
|
||||
using Moonlight.ApiServer.Extensions;
|
||||
using Moonlight.ApiServer.Interfaces;
|
||||
|
||||
namespace Moonlight.ApiServer.Implementations.Diagnose;
|
||||
|
||||
public class LogsDiagnoseProvider : IDiagnoseProvider
|
||||
{
|
||||
public async Task ModifyZipArchive(ZipArchive archive)
|
||||
{
|
||||
|
||||
var logs = await File.ReadAllTextAsync(PathBuilder.File("storage", "logs", "latest.log"));
|
||||
|
||||
if (string.IsNullOrEmpty(logs))
|
||||
{
|
||||
await archive.AddText("logs.txt", "Could not read the logs");
|
||||
return;
|
||||
}
|
||||
|
||||
await archive.AddText("logs.txt", logs);
|
||||
}
|
||||
}
|
||||
@@ -54,7 +54,8 @@ public class CoreStartup : IPluginStartup
|
||||
|
||||
#region Diagnose
|
||||
|
||||
builder.Services.AddSingleton<IDiagnoseProvider, CoreDiagnoseProvider>();
|
||||
builder.Services.AddSingleton<IDiagnoseProvider, CoreConfigDiagnoseProvider>();
|
||||
builder.Services.AddSingleton<IDiagnoseProvider, LogsDiagnoseProvider>();
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
using System.IO.Compression;
|
||||
using Moonlight.ApiServer.Models.Diagnose;
|
||||
|
||||
namespace Moonlight.ApiServer.Interfaces;
|
||||
|
||||
public interface IDiagnoseProvider
|
||||
{
|
||||
public DiagnoseEntry[] GetFiles();
|
||||
public Task ModifyZipArchive(ZipArchive archive);
|
||||
}
|
||||
@@ -2,6 +2,7 @@ using MoonCore.Helpers;
|
||||
using Moonlight.ApiServer.Interfaces;
|
||||
using Moonlight.ApiServer.Models.Diagnose;
|
||||
using System.IO.Compression;
|
||||
using System.Text;
|
||||
using MoonCore.Attributes;
|
||||
using Moonlight.Shared.Http.Responses.Admin.Sys;
|
||||
using Moonlight.Shared.Misc;
|
||||
@@ -18,9 +19,9 @@ public class DiagnoseService
|
||||
DiagnoseProviders = diagnoseProviders;
|
||||
}
|
||||
|
||||
public async Task<DiagnoseProvider[]> GetAvailable()
|
||||
public async Task<DiagnoseProvideResponse[]> GetAvailable()
|
||||
{
|
||||
var availableProviders = new List<DiagnoseProvider>();
|
||||
var availableProviders = new List<DiagnoseProvideResponse>();
|
||||
|
||||
foreach (var diagnoseProvider in DiagnoseProviders)
|
||||
{
|
||||
@@ -30,7 +31,7 @@ public class DiagnoseService
|
||||
|
||||
// The type name is null if the type is a generic type, unlikely, but still could happen
|
||||
if (type != null)
|
||||
availableProviders.Add(new DiagnoseProvider()
|
||||
availableProviders.Add(new DiagnoseProvideResponse()
|
||||
{
|
||||
Name = name,
|
||||
Type = type
|
||||
@@ -42,53 +43,34 @@ public class DiagnoseService
|
||||
|
||||
public async Task GenerateDiagnose(Stream outputStream, string[]? requestedProviders)
|
||||
{
|
||||
List<IDiagnoseProvider> providers;
|
||||
IDiagnoseProvider[] providers;
|
||||
|
||||
if (requestedProviders != null && requestedProviders.Length > 0)
|
||||
{
|
||||
providers = new List<IDiagnoseProvider>();
|
||||
var requesteDiagnoseProviders = new List<IDiagnoseProvider>();
|
||||
|
||||
foreach (var requestedProvider in requestedProviders)
|
||||
{
|
||||
var provider = DiagnoseProviders.FirstOrDefault(x => x.GetType().FullName == requestedProvider);
|
||||
|
||||
if (provider != null)
|
||||
providers.Add(provider);
|
||||
requesteDiagnoseProviders.Add(provider);
|
||||
}
|
||||
|
||||
providers = requesteDiagnoseProviders.ToArray();
|
||||
}
|
||||
else
|
||||
{
|
||||
providers = DiagnoseProviders.ToList();
|
||||
providers = DiagnoseProviders.ToArray();
|
||||
}
|
||||
|
||||
|
||||
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)
|
||||
foreach (var provider in providers)
|
||||
{
|
||||
string entryName = kv.Key.Replace('\\', '/');
|
||||
byte[] data = kv.Value;
|
||||
|
||||
// Optionally pick CompressionLevel.Optimal or NoCompression
|
||||
var entry = zip.CreateEntry(entryName, CompressionLevel.Fastest);
|
||||
|
||||
using (var entryStream = entry.Open())
|
||||
using (var ms = new MemoryStream(data))
|
||||
{
|
||||
await ms.CopyToAsync(entryStream);
|
||||
}
|
||||
await provider.ModifyZipArchive(zip);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,62 +81,4 @@ 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:
|
||||
{
|
||||
var files = await ProcessDiagnoseDirectory(path, diagnoseDirectory);
|
||||
|
||||
foreach (var file in files)
|
||||
{
|
||||
fileEntries.Add(file.Key, file.Value);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case DiagnoseFile diagnoseFile:
|
||||
{
|
||||
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;
|
||||
|
||||
foreach (var entry in directory.Children)
|
||||
{
|
||||
var files = await ProcessDiagnoseEntry(directoryPath, entry);
|
||||
|
||||
foreach (var file in files)
|
||||
{
|
||||
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 bytes = file.GetContent();
|
||||
|
||||
return new KeyValuePair<string, byte[]>(filePath, bytes);
|
||||
}
|
||||
}
|
||||
@@ -29,27 +29,25 @@
|
||||
|
||||
<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>
|
||||
<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">
|
||||
<div class="mb-2 pb-2 pt-4 border-b border-white/5 flex items-center gap-3">
|
||||
<input class="rounded" @bind="SelectAll" type="checkbox" id="selectall_checkbox"/>
|
||||
<label for="selectall_checkbox">Select all</label>
|
||||
</div>
|
||||
|
||||
@foreach (var item in AvailableProviders)
|
||||
{
|
||||
<div class="mt-1 flex gap-3 items-center">
|
||||
<input class="rounded" type="checkbox" id="@(item.Key.Type + "_checkbox")" @bind="@AvailableProviders[item.Key]" />
|
||||
<label for="@(item.Key.Type + "_checkbox")">@item.Key.Name</label>
|
||||
</div>
|
||||
}
|
||||
</LazyLoader>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -59,19 +57,51 @@
|
||||
|
||||
private async Task GenerateDiagnose(WButton _)
|
||||
{
|
||||
var stream = await ApiClient.PostStream("api/admin/system/diagnose");
|
||||
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 async Task Load(LazyLoader arg)
|
||||
private bool DropdownOpen = false;
|
||||
private bool AllSelected = true;
|
||||
private Dictionary<DiagnoseProvideResponse, bool> AvailableProviders;
|
||||
|
||||
private async Task ToggleDropDown()
|
||||
{
|
||||
// AvailableProviders = (await ApiClient.GetJson<SystemAvailableDiagnoseProviderResponse>("api/admin/system/diagnose/available")).AvailableProviders;
|
||||
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
|
||||
{
|
||||
get => AvailableProviders.Values.All(v => v);
|
||||
set
|
||||
{
|
||||
// flip every entry to the new value
|
||||
var keys = AvailableProviders.Keys.ToList();
|
||||
foreach (var k in keys)
|
||||
{
|
||||
AvailableProviders[k] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace Moonlight.Shared.Http.Responses.Admin.Sys;
|
||||
|
||||
public class DiagnoseProvideResponse
|
||||
{
|
||||
public string Name { get; set; }
|
||||
|
||||
public string Type { get; set; }
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
using Moonlight.Shared.Misc;
|
||||
|
||||
namespace Moonlight.Shared.Http.Responses.Admin.Sys;
|
||||
|
||||
public class SystemAvailableDiagnoseProviderResponse
|
||||
{
|
||||
public DiagnoseProvider[] AvailableProviders { get; set; }= [];
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
namespace Moonlight.Shared.Misc;
|
||||
|
||||
public class DiagnoseProvider
|
||||
{
|
||||
public string Name { get; set; }
|
||||
|
||||
public string Type { get; set; }
|
||||
}
|
||||
Reference in New Issue
Block a user