From a4e0175173b026035fd25c88ea296e840ab5b566 Mon Sep 17 00:00:00 2001 From: mxritzdev Date: Mon, 12 May 2025 19:00:09 +0200 Subject: [PATCH] finished diagnose system --- .../Controllers/Admin/Sys/SystemController.cs | 24 +++- .../Diagnose/CoreDiagnoseProvider.cs | 14 ++- .../Implementations/Diagnose/TestProvider.cs | 6 - .../Implementations/Startup/CoreStartup.cs | 8 ++ .../Interfaces/IDiagnoseProvider.cs | 2 +- .../Models/Diagnose/DiagnoseEntry.cs | 2 +- .../Services/DiagnoseService.cs | 113 ++++++++++++++++++ 7 files changed, 155 insertions(+), 14 deletions(-) delete mode 100644 Moonlight.ApiServer/Implementations/Diagnose/TestProvider.cs diff --git a/Moonlight.ApiServer/Http/Controllers/Admin/Sys/SystemController.cs b/Moonlight.ApiServer/Http/Controllers/Admin/Sys/SystemController.cs index b0c9f8d2..faeef12f 100644 --- a/Moonlight.ApiServer/Http/Controllers/Admin/Sys/SystemController.cs +++ b/Moonlight.ApiServer/Http/Controllers/Admin/Sys/SystemController.cs @@ -1,5 +1,12 @@ +using System.IO.Compression; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging.Console; using MoonCore.Attributes; +using MoonCore.Exceptions; +using MoonCore.Helpers; +using Moonlight.ApiServer.Helpers; +using Moonlight.ApiServer.Interfaces; +using Moonlight.ApiServer.Models.Diagnose; using Moonlight.ApiServer.Services; using Moonlight.Shared.Http.Responses.Admin.Sys; @@ -10,10 +17,14 @@ namespace Moonlight.ApiServer.Http.Controllers.Admin.Sys; public class SystemController : Controller { private readonly ApplicationService ApplicationService; + private readonly IEnumerable DiagnoseProviders; + private readonly DiagnoseService DiagnoseService; - public SystemController(ApplicationService applicationService) + public SystemController(ApplicationService applicationService, IEnumerable diagnoseProviders, DiagnoseService diagnoseService) { ApplicationService = applicationService; + DiagnoseProviders = diagnoseProviders; + DiagnoseService = diagnoseService; } [HttpGet] @@ -35,4 +46,15 @@ public class SystemController : Controller { await ApplicationService.Shutdown(); } + + [HttpGet("diagnose")] + [RequirePermission("admin.system.diagnose")] + public async Task Diagnose() + { + var stream = new MemoryStream(); + + await DiagnoseService.GenerateDiagnose(stream); + + return File(stream, "application/zip", "diagnose.zip"); + } } \ No newline at end of file diff --git a/Moonlight.ApiServer/Implementations/Diagnose/CoreDiagnoseProvider.cs b/Moonlight.ApiServer/Implementations/Diagnose/CoreDiagnoseProvider.cs index 479c8991..92d5d09a 100644 --- a/Moonlight.ApiServer/Implementations/Diagnose/CoreDiagnoseProvider.cs +++ b/Moonlight.ApiServer/Implementations/Diagnose/CoreDiagnoseProvider.cs @@ -6,11 +6,15 @@ namespace Moonlight.ApiServer.Implementations.Diagnose; public class CoreDiagnoseProvider : IDiagnoseProvider { - public async Task GetFiles() + public DiagnoseEntry[] GetFiles() { - return new DiagnoseFile() - { - GetContent = () => Encoding.UTF8.GetBytes("hello world") - }; + return + [ + new DiagnoseFile() + { + Name = "test.txt", + GetContent = () => Encoding.UTF8.GetBytes("hello world") + } + ]; } } \ No newline at end of file diff --git a/Moonlight.ApiServer/Implementations/Diagnose/TestProvider.cs b/Moonlight.ApiServer/Implementations/Diagnose/TestProvider.cs deleted file mode 100644 index 2b6e4b7b..00000000 --- a/Moonlight.ApiServer/Implementations/Diagnose/TestProvider.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Moonlight.ApiServer.Implementations.Diagnose; - -public class TestProvider -{ - -} \ No newline at end of file diff --git a/Moonlight.ApiServer/Implementations/Startup/CoreStartup.cs b/Moonlight.ApiServer/Implementations/Startup/CoreStartup.cs index 6ca274db..19782349 100644 --- a/Moonlight.ApiServer/Implementations/Startup/CoreStartup.cs +++ b/Moonlight.ApiServer/Implementations/Startup/CoreStartup.cs @@ -1,6 +1,8 @@ using Microsoft.OpenApi.Models; using Moonlight.ApiServer.Configuration; using Moonlight.ApiServer.Database; +using Moonlight.ApiServer.Implementations.Diagnose; +using Moonlight.ApiServer.Interfaces; using Moonlight.ApiServer.Interfaces.Startup; namespace Moonlight.ApiServer.Implementations.Startup; @@ -48,6 +50,12 @@ public class CoreStartup : IPluginStartup builder.Services.AddDbContext(); + #endregion + + #region Diagnose + + builder.Services.AddSingleton(); + #endregion return Task.CompletedTask; diff --git a/Moonlight.ApiServer/Interfaces/IDiagnoseProvider.cs b/Moonlight.ApiServer/Interfaces/IDiagnoseProvider.cs index 02ad81b7..d2f1b95c 100644 --- a/Moonlight.ApiServer/Interfaces/IDiagnoseProvider.cs +++ b/Moonlight.ApiServer/Interfaces/IDiagnoseProvider.cs @@ -4,5 +4,5 @@ namespace Moonlight.ApiServer.Interfaces; public interface IDiagnoseProvider { - public Task GetFiles(); + public DiagnoseEntry[] GetFiles(); } \ No newline at end of file diff --git a/Moonlight.ApiServer/Models/Diagnose/DiagnoseEntry.cs b/Moonlight.ApiServer/Models/Diagnose/DiagnoseEntry.cs index 96dc3439..f83d4670 100644 --- a/Moonlight.ApiServer/Models/Diagnose/DiagnoseEntry.cs +++ b/Moonlight.ApiServer/Models/Diagnose/DiagnoseEntry.cs @@ -2,7 +2,7 @@ namespace Moonlight.ApiServer.Models.Diagnose; public abstract class DiagnoseEntry { - public string Name { get; set; } = ""; + public required string Name { get; set; } = ""; public abstract bool IsDirectory { get; } } \ No newline at end of file diff --git a/Moonlight.ApiServer/Services/DiagnoseService.cs b/Moonlight.ApiServer/Services/DiagnoseService.cs index 1abdeafa..3c4aca15 100644 --- a/Moonlight.ApiServer/Services/DiagnoseService.cs +++ b/Moonlight.ApiServer/Services/DiagnoseService.cs @@ -1,6 +1,119 @@ +using MoonCore.Helpers; +using Moonlight.ApiServer.Interfaces; +using Moonlight.ApiServer.Models.Diagnose; +using System.IO.Compression; +using MoonCore.Attributes; + namespace Moonlight.ApiServer.Services; +[Scoped] public class DiagnoseService { + + private readonly IEnumerable DiagnoseProviders; + + public DiagnoseService(IEnumerable diagnoseProviders) + { + DiagnoseProviders = diagnoseProviders; + } + + public async Task GenerateDiagnose(Stream outputStream) + { + var tasks = DiagnoseProviders + .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) + { + 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); + } + } + } + + outputStream.Seek(0, SeekOrigin.Begin); + } + catch (Exception ex) + { + throw new Exception($"An unknown error occured while building the Diagnose: {ex.Message}"); + } + } + private async Task> ProcessDiagnoseEntry(string? path, DiagnoseEntry entry) + { + var fileEntries = new Dictionary(); + + 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> ProcessDiagnoseDirectory(string? path, DiagnoseDirectory directory) + { + var result = new Dictionary(); + + 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> ProcessDiagnoseFile(string? path, DiagnoseFile file) + { + + var filePath = path != null ? string.Join("/", path, file.Name) : file.Name; + + var bytes = file.GetContent(); + + return new KeyValuePair(filePath, bytes); + } } \ No newline at end of file