Merge branch 'main' into TOTP

This commit is contained in:
Daniel Balk
2023-04-03 04:09:01 +02:00
committed by GitHub
8 changed files with 365 additions and 501 deletions

View File

@@ -13,353 +13,204 @@ namespace Moonlight.App.Services;
public class CleanupService
{
#region Stats
public DateTime StartedAt { get; private set; }
public DateTime CompletedAt { get; private set; }
public int ServersCleaned { get; private set; }
public int CleanupsPerformed { get; private set; }
public int ServersRunning { get; private set; }
public bool IsRunning { get; private set; }
public bool Activated { get; set; }
public int PercentProgress { get; private set; } = 100;
public string Status { get; private set; } = "N/A";
#endregion
private Task PerformTask;
private readonly ConfigService ConfigService;
private readonly MessageService MessageService;
private readonly IServiceScopeFactory ServiceScopeFactory;
private readonly PeriodicTimer Timer;
private int RequiredCpu;
private long RequiredMemory;
private int WaitTime;
public EventHandler OnUpdated;
public CleanupService(ConfigService configService, IServiceScopeFactory serviceScopeFactory)
public CleanupService(
ConfigService configService,
IServiceScopeFactory serviceScopeFactory,
MessageService messageService)
{
ServiceScopeFactory = serviceScopeFactory;
MessageService = messageService;
ConfigService = configService;
var config = configService.GetSection("Moonlight").GetSection("Cleanup");
RequiredCpu = config.GetValue<int>("Cpu");
RequiredMemory = config.GetValue<long>("Memory");
WaitTime = config.GetValue<int>("Wait");
if (!ConfigService.DebugMode)
Task.Run(Start);
}
private void Start()
{
StartedAt = DateTime.Now;
CompletedAt = DateTime.Now;
IsRunning = false;
Activated = true;
DoWaiting();
}
var config = ConfigService.GetSection("Moonlight").GetSection("Cleanup");
private async void DoWaiting()
{
while (true)
if (!config.GetValue<bool>("Enable") || ConfigService.DebugMode)
{
if (Activated)
await Perform();
try
{
await Task.Delay((int) TimeSpan.FromMinutes(WaitTime).TotalMilliseconds);
}
catch (Exception ex)
{
}
Logger.Info("Disabling cleanup service");
return;
}
Timer = new(TimeSpan.FromMinutes(config.GetValue<int>("Wait")));
Task.Run(Run);
}
public async Task TriggerPerform()
private async Task Run()
{
if (IsRunning)
return;
PerformTask = new Task(async () => await Perform());
PerformTask.Start();
}
private async Task Perform()
{
if (IsRunning)
return;
IsRunning = true;
StartedAt = DateTime.Now;
ServersRunning = 0;
OnUpdated?.Invoke(this, null);
using (var scope = ServiceScopeFactory.CreateScope())
while (await Timer.WaitForNextTickAsync())
{
// Setup time measure
var watch = new Stopwatch();
watch.Start();
IsRunning = true;
using var scope = ServiceScopeFactory.CreateScope();
var config = ConfigService.GetSection("Moonlight").GetSection("Cleanup");
var maxCpu = config.GetValue<int>("Cpu");
var minMemory = config.GetValue<int>("Memory");
var maxUptime = config.GetValue<int>("Uptime");
var minUptime = config.GetValue<int>("MinUptime");
// Get repos from dependency injection
var serverRepository = scope.ServiceProvider.GetRequiredService<ServerRepository>();
var nodeRepository = scope.ServiceProvider.GetRequiredService<NodeRepository>();
var nodeService = scope.ServiceProvider.GetRequiredService<NodeService>();
var imageRepo = scope.ServiceProvider.GetRequiredService<ImageRepository>();
var serverService = scope.ServiceProvider.GetRequiredService<ServerService>();
// Fetching data from mysql
var servers = serverRepository.Get()
.Include(x => x.Image)
var nodes = nodeRepository
.Get()
.ToArray();
var nodes = nodeRepository.Get().ToArray();
var exceptions = servers.Where(x => x.IsCleanupException);
var images = imageRepo.Get().ToArray();
var nodeCount = nodes.Count();
// We use this counter for the foreach loops
int counter = 0;
PercentProgress = 0;
// Fetching data from nodes so we know what nodes to scan
var nodeContainers = new Dictionary<Node, ContainerStats.Container[]>();
Status = "Checking Nodes";
counter = 0;
PercentProgress = 0;
OnUpdated?.Invoke(this, null);
foreach (var node in nodes)
{
try
{
var cpu = await nodeService.GetCpuStats(node);
var freeMemory = await nodeService.GetMemoryStats(node);
var cpuStats = await nodeService.GetCpuStats(node);
var memoryStats = await nodeService.GetMemoryStats(node);
if (cpu.Usage > RequiredCpu || freeMemory.Free < RequiredMemory)
if (cpuStats.Usage > maxCpu || memoryStats.Free < minMemory)
{
var c = await nodeService.GetContainerStats(node);
var containers = c.Containers;
nodeContainers.Add(node, containers.ToArray());
}
}
catch (Exception e)
{
Logger.Error($"Error fetching cleanup data from node {node.Id}");
Logger.Error(e);
}
var containerStats = await nodeService.GetContainerStats(node);
counter++;
CalculateAndUpdateProgress(counter, nodeCount);
OnUpdated?.Invoke(this, null);
}
var serverRepository = scope.ServiceProvider.GetRequiredService<ServerRepository>();
var imageRepository = scope.ServiceProvider.GetRequiredService<ImageRepository>();
// Searching for servers we can actually stop because they have the cleanup tag
// and determine which servers we have to check for an illegal mc server
var serversToCheck = new List<Server>();
var serversToCheckForMc = new List<Server>();
Status = "Checking found servers";
counter = 0;
PercentProgress = 0;
OnUpdated?.Invoke(this, null);
// Count every container for progress calculation
var allContainers = 0;
foreach (var array in nodeContainers)
{
allContainers += array.Value.Length;
}
foreach (var nodeContainer in nodeContainers)
{
try
{
foreach (var container in nodeContainer.Value)
{
var server = servers.First(x => x.Uuid.ToString() == container.Name);
var tagsJson = imageRepo
var images = imageRepository
.Get()
.First(x => x.Id == server.Image.Id).TagsJson;
.ToArray();
var tags = JsonConvert.DeserializeObject<string[]>(tagsJson) ?? Array.Empty<string>();
var imagesWithFlag = images
.Where(x =>
(JsonConvert.DeserializeObject<string[]>(x.TagsJson) ?? Array.Empty<string>()).Contains("cleanup")
)
.ToArray();
if (tags.FirstOrDefault(x => x == "cleanup") != null)
var containerMappedToServers = new Dictionary<ContainerStats.Container, Server>();
foreach (var container in containerStats.Containers)
{
serversToCheck.Add(server);
}
if (tags.FirstOrDefault(x => x == "illegalmc") != null)
{
serversToCheckForMc.Add(server);
}
}
}
catch (Exception e)
{
Logger.Error($"Error processing cleanup data from node {nodeContainer.Key.Id}");
Logger.Error(e);
}
counter++;
CalculateAndUpdateProgress(counter, allContainers);
OnUpdated?.Invoke(this, null);
}
// Now we gonna scan every tagged server
Status = "Scanning servers";
counter = 0;
PercentProgress = 0;
OnUpdated?.Invoke(this, null);
foreach (var server in serversToCheck)
{
try
{
var serverData = serverRepository
.Get()
.Include(x => x.MainAllocation)
.Include(x => x.Node)
.Include(x => x.Variables)
.First(x => x.Id == server.Id);
var players = GetPlayers(serverData.Node, serverData.MainAllocation);
var stats = await serverService.GetDetails(server);
var exception = exceptions.FirstOrDefault(x => x.Id == server.Id) != null;
if (stats != null)
{
if (exception)
{
if (players == 0 && stats.Utilization.Uptime > TimeSpan.FromHours(6).TotalMilliseconds)
if (Guid.TryParse(container.Name, out Guid uuid))
{
await serverService.SetPowerState(server, PowerSignal.Restart);
ServersCleaned++;
OnUpdated?.Invoke(this, null);
}
else
{
ServersRunning++;
OnUpdated?.Invoke(this, null);
}
}
else
{
if (players == 0 && stats.Utilization.Uptime > TimeSpan.FromMinutes(10).TotalMilliseconds)
{
var cleanupVar = serverData.Variables.FirstOrDefault(x => x.Key == "J2S");
var server = serverRepository
.Get()
.Include(x => x.Image)
.Include(x => x.MainAllocation)
.Include(x => x.Variables)
.FirstOrDefault(x => x.Uuid == uuid);
if (cleanupVar == null)
if (server != null && imagesWithFlag.Any(y => y.Id == server.Image.Id))
{
await serverService.SetPowerState(server, PowerSignal.Stop);
ServersCleaned++;
OnUpdated?.Invoke(this, null);
containerMappedToServers.Add(container, server);
}
}
}
var serverService = scope.ServiceProvider.GetRequiredService<ServerService>();
foreach (var containerMapped in containerMappedToServers)
{
var server = containerMapped.Value;
try
{
var stats = await serverService.GetDetails(server);
if (server.IsCleanupException)
{
if (stats.Utilization.Uptime > TimeSpan.FromHours(maxUptime).TotalMilliseconds)
{
var players = GetPlayers(node, server.MainAllocation);
if (players == 0)
{
await serverService.SetPowerState(server, PowerSignal.Restart);
ServersCleaned++;
}
else
{
ServersRunning++;
}
await MessageService.Emit("cleanup.updated", null);
}
}
else
{
if (cleanupVar.Value == "1")
if (stats.Utilization.Uptime > TimeSpan.FromMinutes(minUptime).TotalMilliseconds)
{
await serverService.SetPowerState(server, PowerSignal.Restart);
ServersCleaned++;
OnUpdated?.Invoke(this, null);
}
else
{
await serverService.SetPowerState(server, PowerSignal.Stop);
ServersCleaned++;
OnUpdated?.Invoke(this, null);
var players = GetPlayers(node, server.MainAllocation);
if (players < 1)
{
var j2SVar = server.Variables.FirstOrDefault(x => x.Key == "J2S");
var handleJ2S = j2SVar != null && j2SVar.Value == "1";
if (handleJ2S)
{
await serverService.SetPowerState(server, PowerSignal.Restart);
}
else
{
await serverService.SetPowerState(server, PowerSignal.Stop);
}
ServersCleaned++;
}
else
{
ServersRunning++;
}
await MessageService.Emit("cleanup.updated", null);
}
}
}
else
catch (Exception e)
{
ServersRunning++;
OnUpdated?.Invoke(this, null);
Logger.Warn($"Error checking server {server.Name} ({server.Id})");
Logger.Warn(e);
}
}
}
}
catch (Exception e)
{
Logger.Error($"Error scanning {server.Name}");
Logger.Error($"Error performing cleanup on node {node.Name} ({node.Id})");
Logger.Error(e);
}
counter++;
CalculateAndUpdateProgress(counter, serversToCheck.Count);
OnUpdated?.Invoke(this, null);
}
// Finally we have to check all code container allocations
// for illegal hosted mc servers
Status = "Scanning code containers";
counter = 0;
PercentProgress = 0;
OnUpdated?.Invoke(this, null);
foreach (var server in serversToCheckForMc)
{
try
{
var serverData = serverRepository
.Get()
.Include(x => x.Allocations)
.Include(x => x.Node)
.First(x => x.Id == server.Id);
foreach (var allocation in serverData.Allocations)
{
if (GetPlayers(server.Node, allocation) != -1)
{
// TODO: Suspend server
Logger.Warn("Found CC running mc: https://moonlight.endelon-hosting.de/server/" +
server.Uuid + "/");
}
}
}
catch (Exception e)
{
Logger.Error($"Error scanning (cc) {server.Name}");
Logger.Error(e);
}
counter++;
CalculateAndUpdateProgress(counter, serversToCheckForMc.Count);
OnUpdated?.Invoke(this, null);
}
watch.Stop();
Status = $"Cleanup finished. Duration: {Math.Round(TimeSpan.FromMilliseconds(watch.ElapsedMilliseconds).TotalMinutes, 2)} Minutes";
PercentProgress = 100;
OnUpdated?.Invoke(this, null);
IsRunning = false;
CleanupsPerformed++;
await MessageService.Emit("cleanup.updated", null);
}
IsRunning = false;
CompletedAt = DateTime.Now;
CleanupsPerformed++;
OnUpdated?.Invoke(this, null);
}
private int GetPlayers(Node node, NodeAllocation allocation)
{
var ms = new MineStat(node.Fqdn, (ushort)allocation.Port);
//TODO: Add fake player check
if (ms.ServerUp)
{
return ms.CurrentPlayersInt;
}
else
{
return -1;
}
}
private void CalculateAndUpdateProgress(int now, int all)
{
PercentProgress = (int)Math.Round((now / (double)all) * 100);
return -1;
}
}

View File

@@ -1,4 +1,5 @@
using System.Text;
using Logging.Net;
using Microsoft.Extensions.Primitives;
using Moonlight.App.Helpers;
@@ -21,6 +22,9 @@ public class ConfigService : IConfiguration
if (debugVar != null)
DebugMode = bool.Parse(debugVar);
if(DebugMode)
Logger.Debug("Debug mode enabled");
}
public IEnumerable<IConfigurationSection> GetChildren()

View File

@@ -9,6 +9,34 @@
@inject NavigationManager NavigationManager
@inject CookieService CookieService
<div class="menu menu-column justify-content-center"
data-kt-menu="true">
<div class="menu-item">
<div class="dropdown">
<button class="btn btn-success dropdown-toggle" type="button" data-bs-toggle="dropdown">
<TL>Create</TL>
</button>
<ul class="dropdown-menu">
<li>
<a class="dropdown-item py-2" href="/servers/create">
<TL>Server</TL>
</a>
</li>
<li>
<a class="dropdown-item py-2" href="/domains/create">
<TL>Domain</TL>
</a>
</li>
<li>
<a class="dropdown-item py-2" href="/websites/create">
<TL>Website</TL>
</a>
</li>
</ul>
</div>
</div>
</div>
<div class="app-navbar flex-shrink-0">
<div class="app-navbar-item ms-1 ms-lg-3">
<ThemeSwitcher
@@ -51,10 +79,14 @@
</div>
<div class="separator my-2"></div>
<div class="menu-item px-5 my-1">
<a href="/profile" class="menu-link px-5"><TL>Profile</TL></a>
<a href="/profile" class="menu-link px-5">
<TL>Profile</TL>
</a>
</div>
<div class="menu-item px-5">
<a @onclick="Logout" class="menu-link px-5"><TL>Logout</TL></a>
<a @onclick="Logout" class="menu-link px-5">
<TL>Logout</TL>
</a>
</div>
</div>
</div>

View File

@@ -53,6 +53,8 @@
if(Tags.Contains("pythonfile"))
Settings.Add("Python file", typeof(PythonFileSetting));
Settings.Add("Server reset", typeof(ServerResetSetting));
return Task.CompletedTask;
}

View File

@@ -0,0 +1,62 @@
@using Moonlight.App.Services
@using Moonlight.App.Database.Entities
@using Moonlight.App.Helpers
@using Moonlight.App.Helpers.Files
@using Moonlight.App.Services.Interop
@inject SmartTranslateService SmartTranslateService
@inject AlertService AlertService
@inject ToastService ToastService
@inject WingsApiHelper WingsApiHelper
@inject ConfigService ConfigService
@inject ServerService ServerService
<WButton Text="@(SmartTranslateService.Translate("Reset"))"
WorkingText="@(SmartTranslateService.Translate("Resetting"))"
CssClasses="btn-danger"
OnClick="OnClick">
</WButton>
@code
{
[CascadingParameter]
public Server CurrentServer { get; set; }
private async Task OnClick()
{
var b = await AlertService.YesNo(
SmartTranslateService.Translate("Are you sure you want to reset this server?"),
SmartTranslateService.Translate("Are you sure? This cannot be undone"),
SmartTranslateService.Translate("Yes"),
SmartTranslateService.Translate("No")
);
if (b)
{
await ToastService.CreateProcessToast("serverReset", SmartTranslateService.Translate("Resetting server"));
var access = new WingsFileAccess(
WingsApiHelper,
null!,
CurrentServer,
ConfigService,
null!
);
var files = await access.Ls();
int i = 1;
foreach (var file in files)
{
await access.Delete(file);
await ToastService.UpdateProcessToast("serverReset", $"{SmartTranslateService.Translate("Deleted file")} {i}/{files.Length}");
i++;
}
await ToastService.UpdateProcessToast("serverReset", SmartTranslateService.Translate("Reinstalling server"));
await ServerService.Reinstall(CurrentServer);
await ToastService.RemoveProcessToast("serverReset");
}
}
}

View File

@@ -0,0 +1,103 @@
@page "/admin/servers/cleanup"
@using Moonlight.App.Services
@using Moonlight.App.Models.Misc
@using Moonlight.App.Services.LogServices
@inject CleanupService CleanupService
@inject AuditLogService AuditLogService
@inject MessageService MessageService
@implements IDisposable
<OnlyAdmin>
<div class="row g-5 g-xl-10 mb-5 mb-xl-10">
<div class="col-xl-3">
<div class="card card-flush bgi-no-repeat bgi-size-contain bgi-position-x-end h-xl-100" style="background-color: #170049;">
<div class="card-header pt-5 mb-3">
<div class="d-flex flex-center rounded-circle h-80px w-80px" style="border: 1px rgba(255, 255, 255, 0.4);background-color: #7239EA">
<i class="text-white bx bxs-skull bx-lg"></i>
</div>
</div>
<div class="card-body d-flex align-items-end mb-3">
<div class="d-flex align-items-center">
<span class="fs-4hx text-white fw-bold me-6">@(CleanupService.ServersCleaned)</span>
<div class="fw-bold fs-6 text-white">
<span class="d-block">
<TL>Servers</TL>
</span>
<span>
<TL>stopped</TL>
</span>
</div>
</div>
</div>
</div>
</div>
<div class="col-xl-3">
<div class="card card-flush bgi-no-repeat bgi-size-contain bgi-position-x-end h-xl-100" style="background-color: #170049;">
<div class="card-header pt-5 mb-3">
<div class="d-flex flex-center rounded-circle h-80px w-80px" style="border: 1px rgba(255, 255, 255, 0.4);background-color: #7239EA">
<i class="text-white bx bx-transfer bx-lg"></i>
</div>
</div>
<div class="card-body d-flex align-items-end mb-3">
<div class="d-flex align-items-center">
<span class="fs-4hx text-white fw-bold me-6">@(CleanupService.CleanupsPerformed)</span>
<div class="fw-bold fs-6 text-white">
<span class="d-block">
<TL>Cleanups</TL>
</span>
<span>
<TL>executed</TL>
</span>
</div>
</div>
</div>
</div>
</div>
<div class="col-xl-3">
<div class="card card-flush bgi-no-repeat bgi-size-contain bgi-position-x-end h-xl-100" style="background-color: #170049;">
<div class="card-header pt-5 mb-3">
<div class="d-flex flex-center rounded-circle h-80px w-80px" style="border: 1px rgba(255, 255, 255, 0.4);background-color: #7239EA">
<i class="text-white bx bx-rocket bx-lg"></i>
</div>
</div>
<div class="card-body d-flex align-items-end mb-3">
<div class="d-flex align-items-center">
<span class="fs-4hx text-white fw-bold me-6">@(CleanupService.ServersRunning)</span>
<div class="fw-bold fs-6 text-white">
<span class="d-block">
<TL>Running cleanup</TL>
</span>
<span>
<TL>servers</TL>
</span>
</div>
</div>
</div>
</div>
</div>
</div>
</OnlyAdmin>
@code
{
protected override void OnInitialized()
{
MessageService.Subscribe<Cleanup, Object>("cleanup.updated", this, _ =>
{
Task.Run(async () =>
{
await InvokeAsync(StateHasChanged);
});
return Task.CompletedTask;
});
}
public void Dispose()
{
MessageService.Unsubscribe("cleanup.updated", this);
}
}

View File

@@ -1,200 +0,0 @@
@page "/admin/servers/cleanup"
@using Moonlight.App.Services
@using Logging.Net
@using Moonlight.App.Models.Misc
@using Moonlight.App.Services.LogServices
@inject CleanupService CleanupService
@inject AuditLogService AuditLogService
@implements IDisposable
<PageTitle>
<TL>Cleanup</TL>
</PageTitle>
<IsAdmin>
<div class="row g-5 g-xl-10 mb-5 mb-xl-10">
<div class="col-xl-3">
<div class="card card-flush bgi-no-repeat bgi-size-contain bgi-position-x-end h-xl-100" style="background-color: #170049;">
<div class="card-header pt-5 mb-3">
<div class="d-flex flex-center rounded-circle h-80px w-80px" style="border: 1px rgba(255, 255, 255, 0.4);background-color: #7239EA">
<i class="text-white bx bxs-skull bx-lg"></i>
</div>
</div>
<div class="card-body d-flex align-items-end mb-3">
<div class="d-flex align-items-center">
<span class="fs-4hx text-white fw-bold me-6">@(CleanupService.ServersCleaned)</span>
<div class="fw-bold fs-6 text-white">
<span class="d-block">
<TL>Servers</TL>
</span>
<span>
<TL>stopped</TL>
</span>
</div>
</div>
</div>
</div>
</div>
<div class="col-xl-3">
<div class="card card-flush bgi-no-repeat bgi-size-contain bgi-position-x-end h-xl-100" style="background-color: #170049;">
<div class="card-header pt-5 mb-3">
<div class="d-flex flex-center rounded-circle h-80px w-80px" style="border: 1px rgba(255, 255, 255, 0.4);background-color: #7239EA">
<i class="text-white bx bx-transfer bx-lg"></i>
</div>
</div>
<div class="card-body d-flex align-items-end mb-3">
<div class="d-flex align-items-center">
<span class="fs-4hx text-white fw-bold me-6">@(CleanupService.CleanupsPerformed)</span>
<div class="fw-bold fs-6 text-white">
<span class="d-block">
<TL>Cleanups</TL>
</span>
<span>
<TL>executed</TL>
</span>
</div>
</div>
</div>
</div>
</div>
<div class="col-xl-3">
<div class="card card-flush bgi-no-repeat bgi-size-contain bgi-position-x-end h-xl-100" style="background-color: #170049;">
<div class="card-header pt-5 mb-3">
<div class="d-flex flex-center rounded-circle h-80px w-80px" style="border: 1px rgba(255, 255, 255, 0.4);background-color: #7239EA">
<i class="text-white bx bx-rocket bx-lg"></i>
</div>
</div>
<div class="card-body d-flex align-items-end mb-3">
<div class="d-flex align-items-center">
<span class="fs-4hx text-white fw-bold me-6">@(CleanupService.ServersRunning)</span>
<div class="fw-bold fs-6 text-white">
<span class="d-block">
<TL>Used clanup</TL>
</span>
<span>
<TL>Servers</TL>
</span>
</div>
</div>
</div>
</div>
</div>
<div class="col-xl-3">
<div class="card card-flush bgi-no-repeat bgi-size-contain bgi-position-x-end h-xl-100" style="background-color: #170049;">
<div class="card-body d-flex align-items-end mb-3">
<div class="d-flex align-items-center">
<div class="mb-3">
<label class="form-label"><TL>Status</TL>: @(CleanupService.Status)</label>
<div class="progress">
<div class="progress-bar progress-bar-striped progress-bar-animated bg-primary" role="progressbar" aria-valuenow="@(CleanupService.PercentProgress)" aria-valuemin="0" aria-valuemax="100" style="width: @(CleanupService.PercentProgress)%"></div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-xl-3">
<div class="card card-flush bgi-no-repeat bgi-size-contain bgi-position-x-end">
<div class="card-body mb-3">
<div class="align-items-top">
<div class="fw-bold fs-6 text-white">
<div class="row mb-3">
@if (CleanupService.IsRunning)
{
<button class="btn btn-primary disabled" disabled="">
<TL>Start</TL>
</button>
}
else
{
<button @onclick="Trigger" class="btn btn-primary">
<TL>Start</TL>
</button>
}
</div>
<div class="row mb-3">
@if (CleanupService.Activated)
{
<button class="btn btn-success disabled" disabled="">
<TL>Enable</TL>
</button>
}
else
{
<button @onclick="Enable" class="btn btn-success">
<TL>Enable</TL>
</button>
}
</div>
<div class="row">
@if (CleanupService.Activated)
{
<button @onclick="Disabled" class="btn btn-danger">
<TL>Disable</TL>
</button>
}
else
{
<button class="btn btn-danger disabled" disabled="">
<TL>Disable</TL>
</button>
}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</IsAdmin>
<NonAdmin>
<UnauthorizedAlert></UnauthorizedAlert>
</NonAdmin>
@code
{
protected async override Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
CleanupService.OnUpdated += OnUpdated;
}
}
private void OnUpdated(object? sender, EventArgs e)
{
Logger.Debug(CleanupService.PercentProgress);
InvokeAsync(StateHasChanged);
}
public void Dispose()
{
CleanupService.OnUpdated -= OnUpdated;
}
private void Enable()
{
CleanupService.Activated = true;
AuditLogService.Log(AuditLogType.CleanupEnabled, "The automatic cleanup has been activated");
InvokeAsync(StateHasChanged);
}
private void Disabled()
{
CleanupService.Activated = false;
AuditLogService.Log(AuditLogType.CleanupDisabled, "The automatic cleanup has been disabled");
InvokeAsync(StateHasChanged);
}
private async void Trigger()
{
await CleanupService.TriggerPerform();
await AuditLogService.Log(AuditLogType.CleanupTriggered, "The automatic cleanup has been manually triggered");
await InvokeAsync(StateHasChanged);
}
}

View File

@@ -407,24 +407,34 @@ The State field is required.;The State field is required.
The Country field is required.;The Country field is required.
Street and house number requered;Street and house number requered
Max lenght reached;Max lenght reached
Security;Security
Subscriptions;Subscriptions
Secure your account;Secure your account
2fa adds another layer of security to your account. You have to enter a 6 digit code in order to login.;2fa adds another layer of security to your account. You have to enter a 6 digit code in order to login.
enable;enable
Activate 2fa;Activate 2fa
2fa apps;2fa apps
Use an app like ;Use an app like
or;or
and scan the following QR Code;and scan the following QR Code
If you have trouble using the QR Code, select manual input in the app and enter your email and the following code:;If you have trouble using the QR Code, select manual input in the app and enter your email and the following code:
Finish activation;Finish activation
New password;New password
Server;Server
stopped;stopped
Cleanups;Cleanups
executed;executed
Used clanup;Used clanup
Enable;Enable
Your account is secured with 2fa;Your account is secured with 2fa
anyone write a fancy text here?;anyone write a fancy text here?
Disabble;Disabble
Disable;Disable
You need to enter a 2fa code;You need to enter a 2fa code
2FA code invalid;2FA code invalid
2fa Code;2fa Code
2fa Code requiered;2fa Code requiered
Addons;Addons
Javascript version;Javascript version
Javascript file;Javascript file
Select javascript file to execute on start;Select javascript file to execute on start
Submit;Submit
Processing;Processing
Go up;Go up
Running cleanup;Running cleanup
servers;servers
Select folder to move the file(s) to;Select folder to move the file(s) to
Paper version;Paper version
Join2Start;Join2Start
Server reset;Server reset
Reset;Reset
Resetting;Resetting
Are you sure you want to reset this server?;Are you sure you want to reset this server?
Are you sure? This cannot be undone;Are you sure? This cannot be undone
Resetting server;Resetting server
Deleted file;Deleted file
Reinstalling server;Reinstalling server
Uploading files;Uploading files
complete;complete
Upload complete;Upload complete