changed the diagnose to be easier to use

This commit is contained in:
mxritzdev
2025-05-14 20:13:24 +02:00
parent 609a0297d5
commit ebc1b9441e
12 changed files with 193 additions and 219 deletions

View 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();
}
}

View File

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

View File

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

View File

@@ -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";
}
}

View File

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

View File

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

View File

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

View File

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