257 lines
9.3 KiB
Plaintext
257 lines
9.3 KiB
Plaintext
@page "/admin/servers"
|
|
|
|
@using System.ComponentModel.DataAnnotations
|
|
@using Moonlight.Features.Servers.UI.Components
|
|
@using Microsoft.EntityFrameworkCore
|
|
@using MoonCore.Abstractions
|
|
@using MoonCore.Exceptions
|
|
@using Moonlight.Core.Database.Entities
|
|
|
|
@using Moonlight.Features.Servers.Entities
|
|
@using Moonlight.Features.Servers.Models.Enums
|
|
@using Moonlight.Features.Servers.Services
|
|
|
|
@inject ServerService ServerService
|
|
@inject Repository<Server> ServerRepository
|
|
@inject Repository<ServerAllocation> AllocRepository
|
|
@inject ToastService ToastService
|
|
@inject AlertService AlertService
|
|
@inject ILogger<Index> Logger
|
|
|
|
@attribute [RequirePermission(5000)]
|
|
|
|
<AdminServersNavigation Index="0"/>
|
|
|
|
<FastCrud TItem="Server"
|
|
Loader="Loader"
|
|
OnConfigure="OnConfigure"
|
|
OnConfigureCreate="OnConfigureCreate"
|
|
OnConfigureEdit="OnConfigureEdit"
|
|
@ref="Crud">
|
|
<View>
|
|
<MCBColumn TItem="Server" Field="@(x => x.Id)" Title="Id" Filterable="true"/>
|
|
<MCBColumn TItem="Server" Field="@(x => x.Name)" Title="Name" Filterable="true"/>
|
|
<MCBColumn TItem="Server" Field="@(x => x.Id)" Title="Image">
|
|
<Template>
|
|
<span>@(context.Image.Name)</span>
|
|
</Template>
|
|
</MCBColumn>
|
|
<MCBColumn TItem="Server" Field="@(x => x.Id)" Title="Node">
|
|
<Template>
|
|
<span>@(context.Node.Name)</span>
|
|
</Template>
|
|
</MCBColumn>
|
|
<MCBColumn TItem="Server" Field="@(x => x.Id)" Title="User">
|
|
<Template>
|
|
<span>@(context.Owner.Username)</span>
|
|
</Template>
|
|
</MCBColumn>
|
|
</View>
|
|
<EditToolbar>
|
|
<WButton CssClasses="btn btn-danger me-2" OnClick="() => StartForceDelete(context)">
|
|
<i class="bx bx-sm bx-bomb"></i>
|
|
Force delete
|
|
</WButton>
|
|
</EditToolbar>
|
|
</FastCrud>
|
|
|
|
@code
|
|
{
|
|
private FastCrud<Server> Crud;
|
|
|
|
private async Task CustomUpdate(Server server)
|
|
{
|
|
ServerRepository.Update(server);
|
|
|
|
try
|
|
{
|
|
// Let the daemon know we changed this server
|
|
await ServerService.Sync(server);
|
|
|
|
// Check if the server is running to let the user know if he needs to restart the
|
|
// server. This should prevent the confusion why a running server does not get the changes applied
|
|
// ... hopefully ;)
|
|
try
|
|
{
|
|
if (await ServerService.GetState(server) == ServerState.Offline)
|
|
return;
|
|
|
|
await ToastService.Info("Server is currently running. It requires a restart of the server in order to apply the changes");
|
|
}
|
|
catch (Exception)
|
|
{
|
|
// ignore, sync has already happened
|
|
}
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Logger.LogError("Unable to sync server changes due to an error occuring: {e}", e);
|
|
|
|
await ToastService.Danger("An error occured while sending the changes to the daemon");
|
|
}
|
|
}
|
|
|
|
private Task ValidateUpdate(Server server)
|
|
{
|
|
var oldServer = ServerRepository
|
|
.Get()
|
|
.Include(x => x.Image)
|
|
.First(x => x.Id == server.Id);
|
|
|
|
// Virtual disk check
|
|
if (oldServer.UseVirtualDisk != server.UseVirtualDisk)
|
|
throw new DisplayException("Unable to switch from/to virtual disks. This is not supported at the moment");
|
|
|
|
// Allocation amount check
|
|
if (server.Allocations.Count < oldServer.Image.AllocationsNeeded)
|
|
throw new DisplayException($"The server image requires at least {oldServer.Image.AllocationsNeeded} allocation(s) in order to work");
|
|
|
|
// Set the correct main allocation
|
|
server.MainAllocation = server.Allocations.First();
|
|
|
|
// Check for image changes
|
|
if (oldServer.Image.Id != server.Image.Id)
|
|
throw new DisplayException("Changing the server image has not been implemented yet");
|
|
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
private async Task StartForceDelete(Server server)
|
|
{
|
|
await AlertService.Confirm(
|
|
"Confirm forcefully server deletion",
|
|
"Do you really want to delete this server forcefully?",
|
|
async () =>
|
|
{
|
|
await ServerService.Delete(server, safeDelete: false);
|
|
await ToastService.Success("Successfully deleted server");
|
|
|
|
await Crud.SetState(FastCrudState.View);
|
|
}
|
|
);
|
|
}
|
|
|
|
private IEnumerable<Server> Loader(Repository<Server> repository)
|
|
{
|
|
return repository
|
|
.Get()
|
|
.Include(x => x.Image)
|
|
.Include(x => x.Node)
|
|
.Include(x => x.Owner)
|
|
.Include(x => x.Allocations);
|
|
}
|
|
|
|
private void OnConfigure(FastCrudConfiguration<Server> configuration)
|
|
{
|
|
configuration.CustomCreate = ServerService.Create;
|
|
configuration.CustomDelete = server => ServerService.Delete(server);
|
|
configuration.CustomEdit = CustomUpdate;
|
|
|
|
configuration.ValidateEdit = ValidateUpdate;
|
|
}
|
|
|
|
// Shared form
|
|
private void OnConfigureBase(FastFormConfiguration<Server> configuration, Server server)
|
|
{
|
|
configuration.AddProperty(x => x.Name)
|
|
.WithDefaultComponent()
|
|
.WithValidation(FastFormValidators.Required);
|
|
|
|
configuration.AddProperty(x => x.Owner)
|
|
.WithComponent<DropdownComponent<User>>(component =>
|
|
{
|
|
component.SearchFunc = x => x.Username;
|
|
component.DisplayFunc = x => x.Username;
|
|
})
|
|
.WithValidation(FastFormValidators.Required);
|
|
|
|
configuration.AddProperty(x => x.Image)
|
|
.WithComponent<DropdownComponent<ServerImage>>(component =>
|
|
{
|
|
component.SearchFunc = x => x.Name;
|
|
component.DisplayFunc = x => x.Name;
|
|
})
|
|
.WithValidation(FastFormValidators.Required);
|
|
|
|
configuration.AddProperty(x => x.Cpu)
|
|
.WithDefaultComponent()
|
|
.WithValidation(x => x > 0 ? ValidationResult.Success : new("You need to provide a valid value"))
|
|
.WithSection("Resources", "bxs-chip")
|
|
.WithDescription("The cores the server will be able to use. 100 = 1 Core");
|
|
|
|
configuration.AddProperty(x => x.Memory)
|
|
.WithComponent<ByteSizeComponent>(component =>
|
|
{
|
|
component.MinimumUnit = "MB";
|
|
component.DefaultUnit = "GB";
|
|
component.Converter = 1;
|
|
})
|
|
.WithValidation(x => x > 0 ? ValidationResult.Success : new("You need to provide a valid value"))
|
|
.WithSection("Resources")
|
|
.WithDescription("The amount of memory this server will be able to use");
|
|
|
|
configuration.AddProperty(x => x.Disk)
|
|
.WithComponent<ByteSizeComponent>(component =>
|
|
{
|
|
component.MinimumUnit = "MB";
|
|
component.DefaultUnit = "GB";
|
|
component.Converter = 1;
|
|
})
|
|
.WithValidation(x => x > 0 ? ValidationResult.Success : new("You need to provide a valid value"))
|
|
.WithSection("Resources")
|
|
.WithDescription("The amount of disk space this server will be able to use");
|
|
|
|
configuration.AddProperty(x => x.UseVirtualDisk)
|
|
.WithComponent<SwitchComponent>()
|
|
.WithPage("Advanced options")
|
|
.WithDescription("Whether to use a virtual disk for storing server files. Dont use this if you want to overallocate as the virtual disks will fill out the space you allocate");
|
|
|
|
configuration.AddProperty(x => x.DisablePublicNetwork)
|
|
.WithComponent<SwitchComponent>()
|
|
.WithPage("Network")
|
|
.WithDescription("Whether to block all incoming connections to this server from the internet");
|
|
|
|
configuration.AddProperty(x => x.Allocations)
|
|
.WithComponent<MultiSelectComponent<ServerAllocation>>(component =>
|
|
{
|
|
component.SearchFunc = x => $"{x.IpAddress}:{x.Port}";
|
|
component.DisplayFunc = x => $"{x.IpAddress}:{x.Port}";
|
|
component.ItemsCallback = () =>GetAllocation(server);
|
|
component.ColumnsMd = 6;
|
|
})
|
|
.WithPage("Network");
|
|
}
|
|
|
|
// Specific form
|
|
private void OnConfigureCreate(FastFormConfiguration<Server> configuration, Server server)
|
|
{
|
|
OnConfigureBase(configuration, server);
|
|
|
|
configuration.AddProperty(x => x.Node)
|
|
.WithComponent<DropdownComponent<ServerNode>>(component =>
|
|
{
|
|
component.SearchFunc = x => x.Name;
|
|
component.DisplayFunc = x => x.Name;
|
|
})
|
|
.WithValidation(FastFormValidators.Required);
|
|
}
|
|
|
|
private void OnConfigureEdit(FastFormConfiguration<Server> configuration, Server server)
|
|
{
|
|
OnConfigureBase(configuration, server);
|
|
}
|
|
|
|
private IEnumerable<ServerAllocation> GetAllocation(Server server)
|
|
{
|
|
if (server == null)
|
|
return Array.Empty<ServerAllocation>();
|
|
|
|
if (server.Node == null)
|
|
return Array.Empty<ServerAllocation>();
|
|
|
|
return server.Allocations.Concat(
|
|
AllocRepository
|
|
.Get()
|
|
.FromSqlRaw($"SELECT * FROM `ServerAllocations` WHERE ServerId IS NULL AND ServerNodeId = {server.Node.Id}"));
|
|
}
|
|
} |