@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 ServerRepository @inject Repository AllocRepository @inject ToastService ToastService @inject AlertService AlertService @inject ILogger Logger @attribute [RequirePermission(5000)] Force delete @code { private FastCrud 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 Loader(Repository repository) { return repository .Get() .Include(x => x.Image) .Include(x => x.Node) .Include(x => x.Owner) .Include(x => x.Allocations); } private void OnConfigure(FastCrudConfiguration configuration) { configuration.CustomCreate = ServerService.Create; configuration.CustomDelete = server => ServerService.Delete(server); configuration.CustomEdit = CustomUpdate; configuration.ValidateEdit = ValidateUpdate; } // Shared form private void OnConfigureBase(FastFormConfiguration configuration, Server server) { configuration.AddProperty(x => x.Name) .WithDefaultComponent() .WithValidation(FastFormValidators.Required); configuration.AddProperty(x => x.Owner) .WithComponent>(component => { component.SearchFunc = x => x.Username; component.DisplayFunc = x => x.Username; }) .WithValidation(FastFormValidators.Required); configuration.AddProperty(x => x.Image) .WithComponent>(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(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(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() .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() .WithPage("Network") .WithDescription("Whether to block all incoming connections to this server from the internet"); configuration.AddProperty(x => x.Allocations) .WithComponent>(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 configuration, Server server) { OnConfigureBase(configuration, server); configuration.AddProperty(x => x.Node) .WithComponent>(component => { component.SearchFunc = x => x.Name; component.DisplayFunc = x => x.Name; }) .WithValidation(FastFormValidators.Required); } private void OnConfigureEdit(FastFormConfiguration configuration, Server server) { OnConfigureBase(configuration, server); } private IEnumerable GetAllocation(Server server) { if (server == null) return Array.Empty(); if (server.Node == null) return Array.Empty(); return server.Allocations.Concat( AllocRepository .Get() .FromSqlRaw($"SELECT * FROM `ServerAllocations` WHERE ServerId IS NULL AND ServerNodeId = {server.Node.Id}")); } }