Added diagnose file export
This commit is contained in:
parent
741d13b18a
commit
3ae694a3da
9 changed files with 154 additions and 8 deletions
26
Moonlight/App/Extensions/ZipArchiveExtension.cs
Normal file
26
Moonlight/App/Extensions/ZipArchiveExtension.cs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -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)
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -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>
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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; }
|
|
||||||
}
|
}
|
|
@ -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";
|
||||||
}
|
}
|
||||||
|
|
30
Moonlight/Shared/Views/Admin/Sys/Diagnose.razor
Normal file
30
Moonlight/Shared/Views/Admin/Sys/Diagnose.razor
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue