Files
Moonlight/Moonlight/Shared/Views/Admin/Security/Malware.razor
2023-08-28 05:08:52 +02:00

209 lines
7.3 KiB
Plaintext

@page "/admin/security/malware"
@using Moonlight.Shared.Components.Navigations
@using Moonlight.App.Services.Background
@using Moonlight.App.Services
@using BlazorTable
@using Microsoft.EntityFrameworkCore
@using Moonlight.App.ApiClients.Wings
@using Moonlight.App.Database.Entities
@using Moonlight.App.Events
@using Moonlight.App.Helpers
@using Moonlight.App.Models.Misc
@using Moonlight.App.Repositories
@using Moonlight.App.Services.Interop
@using Moonlight.App.Services.Sessions
@inject MalwareBackgroundScanService MalwareBackgroundScanService
@inject SmartTranslateService SmartTranslateService
@inject ServerService ServerService
@inject ToastService ToastService
@inject SessionServerService SessionServerService
@inject Repository<Server> ServerRepository
@inject Repository<User> UserRepository
@inject EventSystem Event
@implements IDisposable
@attribute [PermissionRequired(nameof(Permissions.AdminSecurityMalware))]
<AdminSecurityNavigation Index="1"/>
<div class="row">
<div class="col-12 col-lg-6">
<div class="card">
<div class="card-body">
@if (MalwareBackgroundScanService.IsRunning)
{
<span class="fs-3 spinner-border align-middle me-3"></span>
}
<span class="fs-3">@(MalwareBackgroundScanService.Status)</span>
</div>
<div class="card-footer">
@if (MalwareBackgroundScanService.IsRunning)
{
<button class="btn btn-success disabled">
<TL>Scan in progress</TL>
</button>
}
else
{
<div class="mb-5">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="scanAllServers" @bind="MalwareBackgroundScanService.ScanAllServers">
<label class="form-check-label" for="scanAllServers">
<TL>Scan all servers</TL>
</label>
</div>
</div>
<WButton Text="@(SmartTranslateService.Translate("Start scan"))"
CssClasses="btn-success me-3"
OnClick="MalwareBackgroundScanService.Start">
</WButton>
<WButton Text="@(SmartTranslateService.Translate("Purge page"))"
CssClasses="btn-danger"
OnClick="PurgeSelected">
</WButton>
}
</div>
</div>
</div>
<div class="col-12 col-lg-6">
<div class="card">
<div class="card-header">
<span class="card-title">
<TL>Results</TL>
</span>
</div>
<div class="card-body">
<LazyLoader @ref="LazyLoaderResults" Load="LoadResults">
<div class="table-responsive">
<Table @ref="Table" TableItem="Server" Items="ScanResults.Keys" PageSize="25" TableClass="table table-row-bordered table-row-gray-100 align-middle gs-0 gy-3" TableHeadClass="fw-bold text-muted">
<Column TableItem="Server" Title="@(SmartTranslateService.Translate("Server"))" Field="@(x => x.Id)" Sortable="false" Filterable="false">
<Template>
<a href="/server/@(context.Uuid)">@(context.Name)</a>
</Template>
</Column>
<Column TableItem="Server" Title="@(SmartTranslateService.Translate("Title"))" Field="@(x => ScanResults[x].Title)" Sortable="false" Filterable="true" />
<Column TableItem="Server" Title="@(SmartTranslateService.Translate("Description"))" Field="@(x => ScanResults[x].Description)" Sortable="false" Filterable="true" />
<Pager ShowPageNumber="true" ShowTotalCount="true"/>
</Table>
</div>
</LazyLoader>
</div>
</div>
</div>
</div>
@code
{
private readonly Dictionary<Server, MalwareScanResult> ScanResults = new();
private Table<Server> Table;
private LazyLoader LazyLoaderResults;
protected override async Task OnInitializedAsync()
{
await Event.On<Object>("malwareScan.status", this, async o => { await InvokeAsync(StateHasChanged); });
await Event.On<Server>("malwareScan.result", this, async server =>
{
lock (MalwareBackgroundScanService.ScanResults)
{
ScanResults.Add(server, MalwareBackgroundScanService.ScanResults[server]);
}
await InvokeAsync(StateHasChanged);
});
}
private Task LoadResults(LazyLoader arg)
{
ScanResults.Clear();
lock (MalwareBackgroundScanService.ScanResults)
{
foreach (var result in MalwareBackgroundScanService.ScanResults)
{
ScanResults.Add(result.Key, result.Value);
}
}
return Task.CompletedTask;
}
public async void Dispose()
{
await Event.Off("malwareScan.status", this);
await Event.Off("malwareScan.result", this);
}
private async Task PurgeSelected()
{
int users = 0;
int servers = 0;
int allServersCount = Table.FilteredItems.Count();
int position = 0;
await ToastService.CreateProcessToast("purgeProcess", "Purging");
foreach (var item in Table.FilteredItems)
{
position++;
if (item == null)
continue;
try
{
var server = ServerRepository.Get()
.Include(x => x.Owner)
.FirstOrDefault(x => x.Id == item.Id);
if(server == null)
continue;
await ToastService.UpdateProcessToast("purgeProcess", $"[{position}/{allServersCount}] {server.Name}");
ScanResults.Remove(item);
await InvokeAsync(StateHasChanged);
// Owner
server.Owner.Status = UserStatus.Banned;
UserRepository.Update(server.Owner);
users++;
try
{
await SessionServerService.ReloadUserSessions(server.Owner);
}
catch (Exception) {/* Ignored */}
// Server itself
await ServerService.SetPowerState(server, PowerSignal.Kill);
await ServerService.Delete(server);
servers++;
}
catch (Exception e)
{
Logger.Warn($"Error purging server: {item.Uuid}");
Logger.Warn(e);
await ToastService.Error(
$"Failed to purge server '{item.Name}': {e.Message}"
);
}
}
await ToastService.RemoveProcessToast("purgeProcess");
await ToastService.Success($"Successfully purged {servers} servers by {users} users");
}
}