Files
Moonlight/Moonlight.Frontend/UI/Admin/Views/Sys/Diagnose.razor

252 lines
12 KiB
Plaintext

@using LucideBlazor
@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Components.Authorization
@using Moonlight.Shared
@using Moonlight.Shared.Http.Responses.Admin
@using ShadcnBlazor.Accordions
@using ShadcnBlazor.Alerts
@using ShadcnBlazor.Buttons
@using ShadcnBlazor.Cards
@using ShadcnBlazor.Emptys
@using ShadcnBlazor.Extras.Common
@using ShadcnBlazor.Spinners
@inject HttpClient HttpClient
@inject IAuthorizationService AuthorizationService
<div class="grid grid-cols-1 xl:grid-cols-2 gap-5 mt-5">
<div class="col-span-1">
<Card>
<CardHeader>
<CardTitle>Automatic diagnosis</CardTitle>
<CardDescription>
Use a diagnostic report to share configuration details and errors with Moonlight developers, with
sensitive data automatically censored.
</CardDescription>
</CardHeader>
<CardContent ClassName="flex flex-col gap-y-5">
<Alert
ClassName="w-full flex flex-row items-center gap-3 border-yellow-500/80 bg-yellow-500/5 text-yellow-500">
<div class="flex shrink-0 items-center">
<TriangleAlertIcon ClassName="size-6 text-yellow-500/60"/>
</div>
<div class="flex flex-1 items-center justify-between gap-4">
<div class="flex flex-col gap-0.5">
<AlertTitle>Notice</AlertTitle>
<AlertDescription ClassName="text-yellow-500/80">
Only share these reports with the moonlight developers or the corresponding plugin
developers.
Even though we do our best to censor sensitive data it may still contain information you
dont want a random person on the internet to know
</AlertDescription>
</div>
</div>
</Alert>
</CardContent>
<CardFooter ClassName="justify-end">
<WButtom OnClick="DiagnoseAsync" disabled="@(!AccessResult.Succeeded)">
<StethoscopeIcon/>
Start diagnostics
</WButtom>
</CardFooter>
</Card>
</div>
<div class="col-span-1">
@if (IsLoading)
{
<Card>
<CardContent ClassName="flex justify-center items-center">
<Spinner ClassName="size-10"/>
</CardContent>
</Card>
}
else
{
if (HasDiagnosed)
{
if (Entries.Length == 0)
{
<Card>
<CardContent ClassName="flex justify-center items-center">
<Empty>
<EmptyHeader>
<EmptyMedia Variant="EmptyMediaVariant.Icon">
<SearchIcon/>
</EmptyMedia>
<EmptyTitle>No results available</EmptyTitle>
<EmptyDescription>
Diagnosis didnt return any results
</EmptyDescription>
</EmptyHeader>
</Empty>
</CardContent>
</Card>
}
else
{
<Accordion
ClassName="flex w-full flex-col gap-2"
Type="AccordionType.Multiple">
@for (var i = 0; i < Entries.Length; i++)
{
var entry = Entries[i];
var textColor = entry.Level switch
{
DiagnoseLevel.Error => "text-destructive",
DiagnoseLevel.Warning => "text-yellow-400",
DiagnoseLevel.Healthy => "text-green-500"
};
<AccordionItem
ClassName="overflow-hidden border bg-card px-4 rounded-lg last:border-b"
Value="@($"diagnoseEntry{i}")">
<AccordionTrigger className="hover:no-underline">
<div class="flex items-center gap-3">
@switch (entry.Level)
{
case DiagnoseLevel.Error:
<CircleXIcon ClassName="@textColor"/>
break;
case DiagnoseLevel.Warning:
<TriangleAlertIcon ClassName="@textColor"/>
break;
case DiagnoseLevel.Healthy:
<CircleCheckIcon ClassName="@textColor"/>
break;
}
<div class="flex flex-col items-start text-left">
<span class="@textColor">
@entry.Title
</span>
<span class="text-sm text-muted-foreground">
@(string.Join(" / ", entry.Tags))
</span>
</div>
</div>
</AccordionTrigger>
<AccordionContent ClassName="ps-7">
<div class="text-muted-foreground flex flex-col gap-y-3">
@if (!string.IsNullOrWhiteSpace(entry.StackStrace))
{
<div
class="rounded-xl p-2.5 bg-black max-h-36 overflow-auto scrollbar-thin">
@entry.StackStrace
</div>
}
@if (!string.IsNullOrWhiteSpace(entry.Message))
{
<p>
@entry.Message
</p>
}
@if (
!string.IsNullOrWhiteSpace(entry.ReportUrl) ||
!string.IsNullOrWhiteSpace(entry.StackStrace) ||
!string.IsNullOrWhiteSpace(entry.SolutionUrl)
)
{
<div class="flex justify-end gap-x-1">
@if (!string.IsNullOrWhiteSpace(entry.StackStrace))
{
<Button Variant="ButtonVariant.Outline">
<CopyIcon/>
Copy
</Button>
}
@if (!string.IsNullOrWhiteSpace(entry.SolutionUrl))
{
<Button Variant="ButtonVariant.Outline">
<Slot>
<a href="@entry.SolutionUrl" @attributes="context">
<WrenchIcon/>
Show suggested solution
</a>
</Slot>
</Button>
}
@if (!string.IsNullOrWhiteSpace(entry.ReportUrl))
{
<Button Variant="ButtonVariant.Outline">
<Slot>
<a href="@entry.ReportUrl" @attributes="context">
<GitBranchIcon/>
Report on Github
</a>
</Slot>
</Button>
}
</div>
}
</div>
</AccordionContent>
</AccordionItem>
}
</Accordion>
}
}
else
{
<Card>
<CardContent ClassName="flex justify-center items-center">
<Empty>
<EmptyHeader>
<EmptyMedia Variant="EmptyMediaVariant.Icon">
<CircleQuestionMarkIcon/>
</EmptyMedia>
<EmptyTitle>No results available yet</EmptyTitle>
<EmptyDescription>
Press the start button to start the automatic diagnosis
</EmptyDescription>
</EmptyHeader>
</Empty>
</CardContent>
</Card>
}
}
</div>
</div>
@code
{
[CascadingParameter] public Task<AuthenticationState> AuthState { get; set; }
private AuthorizationResult AccessResult;
private bool IsLoading = false;
private bool HasDiagnosed = false;
private DiagnoseResultDto[] Entries;
protected override async Task OnInitializedAsync()
{
var authState = await AuthState;
AccessResult = await AuthorizationService.AuthorizeAsync(authState.User, Permissions.System.Diagnose);
}
private async Task DiagnoseAsync()
{
IsLoading = true;
HasDiagnosed = false;
await InvokeAsync(StateHasChanged);
var results = await HttpClient.GetFromJsonAsync<DiagnoseResultDto[]>("api/admin/system/diagnose");
Entries = results ?? [];
IsLoading = false;
HasDiagnosed = true;
await InvokeAsync(StateHasChanged);
}
}