209 lines
7.3 KiB
Plaintext
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");
|
|
}
|
|
} |