Merge pull request #350 from Moonlight-Panel/v2_AddDiagnoseFileExport

Added diagnose file export
This commit is contained in:
Marcel Baumgartner 2024-01-04 16:04:28 +01:00 committed by GitHub
commit 8b032462c0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 154 additions and 8 deletions

View file

@ -0,0 +1,26 @@
using System.IO.Compression;
using System.Text;
namespace Moonlight.App.Extensions;
public static class ZipArchiveExtension
{
public static async Task AddFromText(this ZipArchive archive, string entryName, string content)
{
using var memoryStream = new MemoryStream();
await memoryStream.WriteAsync(Encoding.UTF8.GetBytes(content));
await memoryStream.FlushAsync();
await archive.AddFromStream(entryName, memoryStream);
}
public static async Task AddFromStream(this ZipArchive archive, string entryName, Stream dataStream)
{
var entry = archive.CreateEntry(entryName, CompressionLevel.Fastest);
await using var stream = entry.Open();
dataStream.Position = 0;
await dataStream.CopyToAsync(stream);
await stream.FlushAsync();
}
}

View file

@ -34,4 +34,27 @@ public class ConfigService
{ {
return Data; return Data;
} }
public string GetDiagnoseJson()
{
var text = File.ReadAllText(Path);
var data = JsonConvert.DeserializeObject<ConfigV1>(text) ?? new();
// Security token
data.Security.Token = "";
// Database
if (string.IsNullOrEmpty(data.Database.Password))
data.Database.Password = "WAS EMPTY";
else
data.Database.Password = "WAS NOT EMPTY";
// Mailserver
if (string.IsNullOrEmpty(data.MailServer.Password))
data.MailServer.Password = "WAS EMPTY";
else
data.MailServer.Password = "WAS NOT EMPTY";
return JsonConvert.SerializeObject(data, Formatting.Indented);
}
} }

View file

@ -81,6 +81,8 @@ public class PluginService
Logger.Info($"Loaded {Plugins.Count} plugin(s)"); Logger.Info($"Loaded {Plugins.Count} plugin(s)");
} }
public Task<MoonlightPlugin[]> GetLoadedPlugins() => Task.FromResult(Plugins.ToArray());
public async Task RunPreInit() public async Task RunPreInit()
{ {
foreach (var plugin in Plugins) foreach (var plugin in Plugins)

View file

@ -1,4 +1,5 @@
using Moonlight.App.Event; using System.IO.Compression;
using Moonlight.App.Event;
using Moonlight.App.Extensions; using Moonlight.App.Extensions;
using Moonlight.App.Helpers; using Moonlight.App.Helpers;
@ -10,7 +11,8 @@ public class MoonlightService // This service can be used to perform strictly pa
private readonly IServiceProvider ServiceProvider; private readonly IServiceProvider ServiceProvider;
public WebApplication Application { get; set; } // Do NOT modify using a plugin public WebApplication Application { get; set; } // Do NOT modify using a plugin
public MoonlightThemeService Theme { get; set; } public string LogPath { get; set; } // Do NOT modify using a plugin
public MoonlightThemeService Theme => ServiceProvider.GetRequiredService<MoonlightThemeService>();
public MoonlightService(ConfigService configService, IServiceProvider serviceProvider) public MoonlightService(ConfigService configService, IServiceProvider serviceProvider)
{ {
@ -28,4 +30,52 @@ public class MoonlightService // This service can be used to perform strictly pa
await Application.StopAsync(); await Application.StopAsync();
} }
public async Task<byte[]> GenerateDiagnoseReport()
{
var scope = ServiceProvider.CreateScope();
// Prepare zip file
var memoryStream = new MemoryStream();
var zip = new ZipArchive(memoryStream, ZipArchiveMode.Create, true);
// Add current log
// We need to open the file this way because we need to specify the access and share mode directly
// in order to read from a file which is currently written to
var fs = File.Open(LogPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
var sr = new StreamReader(fs);
var log = await sr.ReadToEndAsync();
sr.Close();
fs.Close();
await zip.AddFromText("log.txt", log);
// TODO: Add node settings here
// Add config
var config = ConfigService.GetDiagnoseJson();
await zip.AddFromText("config.json", config);
// Make a list of plugins
var pluginService = scope.ServiceProvider.GetRequiredService<PluginService>();
var plugins = await pluginService.GetLoadedPlugins();
var pluginList = "Installed plugins:\n";
foreach (var plugin in plugins)
{
var assembly = plugin.GetType().Assembly;
pluginList += $"{assembly.FullName} ({assembly.Location})\n";
}
await zip.AddFromText("pluginList.txt", pluginList);
// Add more information here
// Finalize file
zip.Dispose();
memoryStream.Close();
var data = memoryStream.ToArray();
return data;
}
} }

View file

@ -43,5 +43,6 @@
<PackageReference Include="QRCoder" Version="1.4.3" /> <PackageReference Include="QRCoder" Version="1.4.3" />
<PackageReference Include="Serilog" Version="3.1.0-dev-02078" /> <PackageReference Include="Serilog" Version="3.1.0-dev-02078" />
<PackageReference Include="Serilog.Sinks.Console" Version="5.0.0-dev-00923" /> <PackageReference Include="Serilog.Sinks.Console" Version="5.0.0-dev-00923" />
<PackageReference Include="Serilog.Sinks.File" Version="5.0.1-dev-00972" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View file

@ -18,22 +18,29 @@ using Moonlight.App.Services.Users;
using Moonlight.App.Services.Utils; using Moonlight.App.Services.Utils;
using Serilog; using Serilog;
var builder = WebApplication.CreateBuilder(args);
var configService = new ConfigService(); var configService = new ConfigService();
Directory.CreateDirectory(PathBuilder.Dir("storage")); Directory.CreateDirectory(PathBuilder.Dir("storage"));
Directory.CreateDirectory(PathBuilder.Dir("storage", "logs")); Directory.CreateDirectory(PathBuilder.Dir("storage", "logs"));
var logConfig = new LoggerConfiguration(); var logConfig = new LoggerConfiguration();
var now = DateTime.UtcNow;
var logPath = PathBuilder.File("storage", "logs", $"moonlight-{now.Day}-{now.Month}-{now.Year}---{now.Hour}-{now.Minute}.log");
logConfig = logConfig.Enrich.FromLogContext() logConfig = logConfig.Enrich.FromLogContext()
.MinimumLevel.Debug() .WriteTo.File(logPath, outputTemplate: "{Timestamp:HH:mm:ss} [{Level:u3}] {SourceContext} {Message:lj}{NewLine}{Exception}")
.WriteTo.Console( .WriteTo.Console(
outputTemplate: outputTemplate:
"{Timestamp:HH:mm:ss} [{Level:u3}] {SourceContext} {Message:lj}{NewLine}{Exception}"); "{Timestamp:HH:mm:ss} [{Level:u3}] {SourceContext} {Message:lj}{NewLine}{Exception}");
Log.Logger = logConfig.CreateLogger(); if (args.Contains("--debug") || builder.Environment.IsDevelopment())
logConfig = logConfig.MinimumLevel.Debug();
else
logConfig = logConfig.MinimumLevel.Information();
var builder = WebApplication.CreateBuilder(args); Log.Logger = logConfig.CreateLogger();
// Init plugin system // Init plugin system
var pluginService = new PluginService(); var pluginService = new PluginService();
@ -126,6 +133,7 @@ app.Services.GetRequiredService<AutoMailSendService>();
var moonlightService = app.Services.GetRequiredService<MoonlightService>(); var moonlightService = app.Services.GetRequiredService<MoonlightService>();
moonlightService.Application = app; moonlightService.Application = app;
moonlightService.LogPath = logPath;
var serviceService = app.Services.GetRequiredService<ServiceDefinitionService>(); var serviceService = app.Services.GetRequiredService<ServiceDefinitionService>();
serviceService.Register<DummyServiceDefinition>(ServiceType.Server); serviceService.Register<DummyServiceDefinition>(ServiceType.Server);

View file

@ -16,12 +16,16 @@
<i class="bx bx-sm bx-palette me-2"></i> Themes <i class="bx bx-sm bx-palette me-2"></i> Themes
</a> </a>
</li> </li>
<li class="nav-item mt-2">
<a class="nav-link text-active-primary ms-0 me-10 py-5 @(Index == 3 ? "active" : "")" href="/admin/sys/diagnose">
<i class="bx bx-sm bx-pulse me-2"></i> Diagnose
</a>
</li>
</ul> </ul>
</div> </div>
</div> </div>
@code @code
{ {
[Parameter] [Parameter] public int Index { get; set; }
public int Index { get; set; }
} }

View file

@ -1,4 +1,4 @@
<div class="card card-body border-primary fs-5 mt-5"> <div class="card card-body border-@(Color) fs-5 mt-5">
@ChildContent @ChildContent
</div> </div>
@ -6,4 +6,6 @@
{ {
[Parameter] [Parameter]
public RenderFragment ChildContent { get; set; } public RenderFragment ChildContent { get; set; }
[Parameter] public string Color { get; set; } = "primary";
} }

View file

@ -0,0 +1,30 @@
@page "/admin/sys/diagnose"
@using Moonlight.App.Extensions.Attributes
@using Moonlight.App.Models.Enums
@using Moonlight.App.Services.Sys
@attribute [RequirePermission(Permission.AdminRoot)]
@inject MoonlightService MoonlightService
@inject FileDownloadService DownloadService
<AdminSysNavigation Index="3" />
<Tooltip Color="danger">
Dont share this file with random people.
We do our best to clear this file from sensitive information but it can still contain some.
Its safe to share with official moonlight panel developers
</Tooltip>
<div class="card card-body mt-5">
<WButton Text="Generate diagnose" OnClick="GenerateDiagnose" />
</div>
@code
{
private async Task GenerateDiagnose()
{
var data = await MoonlightService.GenerateDiagnoseReport();
await DownloadService.DownloadBytes("moonlight-diagnose.zip", data);
}
}