From 53509ecf63ff9ae58f7679b8d03c19837fbe9861 Mon Sep 17 00:00:00 2001 From: Marcel Baumgartner Date: Wed, 26 Jun 2024 10:59:14 +0200 Subject: [PATCH] Fixed smaller issues. Upgraded server node ui --- .../Core/UI/Views/Admin/Sys/Settings.razor | 2 - .../Core/UI/Views/Admin/Users/Index.razor | 4 +- .../Servers/Entities/ServerAllocation.cs | 2 +- .../UI/NodeComponents/NodeAllocations.razor | 142 +++++++---- .../UI/NodeComponents/NodeOverview.razor | 2 +- .../Servers/UI/Views/Admin/Nodes/Index.razor | 222 ++++++++++++------ .../Servers/UI/Views/Servers/Networks.razor | 2 +- Moonlight/Moonlight.csproj | 2 +- 8 files changed, 257 insertions(+), 121 deletions(-) diff --git a/Moonlight/Core/UI/Views/Admin/Sys/Settings.razor b/Moonlight/Core/UI/Views/Admin/Sys/Settings.razor index 57000001..f4079d13 100644 --- a/Moonlight/Core/UI/Views/Admin/Sys/Settings.razor +++ b/Moonlight/Core/UI/Views/Admin/Sys/Settings.razor @@ -169,8 +169,6 @@ else if (TryGetAttribute(customAttributes, out DisplayNameAttribute nameAttribute)) propConfig.WithName(nameAttribute.DisplayName); - else - propConfig.WithName(Formatter.ConvertCamelCaseToSpaces(property.Name)); if (TryGetAttribute(customAttributes, out DescriptionAttribute descriptionAttribute)) propConfig.WithDescription(descriptionAttribute.Description); diff --git a/Moonlight/Core/UI/Views/Admin/Users/Index.razor b/Moonlight/Core/UI/Views/Admin/Users/Index.razor index 28aa6d6c..c3812a57 100644 --- a/Moonlight/Core/UI/Views/Admin/Users/Index.razor +++ b/Moonlight/Core/UI/Views/Admin/Users/Index.razor @@ -53,7 +53,7 @@ private void OnConfigure(FastCrudConfiguration configuration) { - configuration.CustomCreate += async user => + configuration.CustomCreate = async user => { var result = await AuthenticationProvider.Register(user.Username, user.Email, user.Password); @@ -61,7 +61,7 @@ throw new DisplayException("An unknown error occured while creating user"); }; - configuration.ValidateEdit += async user => + configuration.ValidateEdit = async user => { await AuthenticationProvider.ChangeDetails(user, user.Email, user.Username); }; diff --git a/Moonlight/Features/Servers/Entities/ServerAllocation.cs b/Moonlight/Features/Servers/Entities/ServerAllocation.cs index 1ba9dfac..de017f3a 100644 --- a/Moonlight/Features/Servers/Entities/ServerAllocation.cs +++ b/Moonlight/Features/Servers/Entities/ServerAllocation.cs @@ -3,7 +3,7 @@ namespace Moonlight.Features.Servers.Entities; public class ServerAllocation { public int Id { get; set; } - public string IpAddress { get; set; } = ""; + public string IpAddress { get; set; } = "0.0.0.0"; public int Port { get; set; } public string Note { get; set; } = ""; } \ No newline at end of file diff --git a/Moonlight/Features/Servers/UI/NodeComponents/NodeAllocations.razor b/Moonlight/Features/Servers/UI/NodeComponents/NodeAllocations.razor index cc1317f3..1aebc220 100644 --- a/Moonlight/Features/Servers/UI/NodeComponents/NodeAllocations.razor +++ b/Moonlight/Features/Servers/UI/NodeComponents/NodeAllocations.razor @@ -1,7 +1,10 @@ +@using System.ComponentModel.DataAnnotations @using Moonlight.Features.Servers.Entities @using Moonlight.Features.Servers.Models.Forms.Admin.Nodes @using BlazorTable @using MoonCore.Abstractions +@using MoonCore.Blazor.Models.Fast +@using MoonCore.Blazor.Models.Fast.Validators @using MoonCore.Exceptions @@ -27,38 +30,31 @@
- + + Add +
-
@* - - - +
+ + + + + + + + - - - - - - - - - In order for a server to be deployed on this node allocations need to be created here - - - *@ + +
@@ -66,8 +62,7 @@ { [Parameter] public ServerNode Node { get; set; } - // A bit long, lol - //private AutoListCrud Crud; + private FastCrud Crud; // Quick add values private string IpAddress = "0.0.0.0"; @@ -101,7 +96,7 @@ NodeRepository.Update(Node!); await ToastService.Success($"Added {added} allocations and skipped {skipped} ports due to existing allocations"); - //await Crud.Reload(); + await Crud.Refresh(); } private async Task DeleteAllAllocations() @@ -124,30 +119,91 @@ } await ToastService.Success("Successfully deleted allocations"); - //await Crud.Reload(); + await Crud.Refresh(); }); } - private Task ValidateDelete(ServerAllocation allocation) + private IEnumerable Loader(Repository _) { - // Check if allocation is associated with a server - var serverWithThisAllocation = ServerRepository - .Get() - .FirstOrDefault(x => x.Allocations.Any(y => y.Id == allocation.Id)); + return Node.Allocations; + } - if (serverWithThisAllocation != null) + private void OnConfigure(FastCrudConfiguration configuration) + { + configuration.ValidateCreate = allocation => { - throw new DisplayException($"The server '{serverWithThisAllocation.Name}' (ID: {serverWithThisAllocation.Id}) is using this allocation. Delete the server in order to delete this allocation"); - } + if (Node.Allocations.Any(x => x.Port == allocation.Port && x.IpAddress == allocation.IpAddress)) + throw new DisplayException("A allocation with these ip and port does already exist"); + + return Task.CompletedTask; + }; - return Task.CompletedTask; + configuration.ValidateEdit = allocation => + { + if (Node.Allocations.Any(x => x.Port == allocation.Port && x.IpAddress == allocation.IpAddress && x.Id != allocation.Id)) + throw new DisplayException("A allocation with these ip and port does already exist"); + + return Task.CompletedTask; + }; + + configuration.ValidateDelete = allocation => + { + // Check if allocation is associated with a server + var serverWithThisAllocation = ServerRepository + .Get() + .FirstOrDefault(x => x.Allocations.Any(y => y.Id == allocation.Id)); + + if (serverWithThisAllocation != null) + { + throw new DisplayException($"The server '{serverWithThisAllocation.Name}' (ID: {serverWithThisAllocation.Id}) is using this allocation. Delete the server in order to delete this allocation"); + } + + return Task.CompletedTask; + }; + + configuration.CustomCreate = allocation => + { + Node.Allocations.Add(allocation); + NodeRepository.Update(Node); + + return Task.CompletedTask; + }; + + configuration.CustomEdit = allocation => + { + AllocationRepository.Update(allocation); + return Task.CompletedTask; + }; + + configuration.CustomDelete = allocation => + { + Node.Allocations.Remove(allocation); + NodeRepository.Update(Node); + + try + { + AllocationRepository.Delete(allocation); + } + catch (Exception) + { + /* do not fail here */ + } + + return Task.CompletedTask; + }; } - private Task ValidateAdd(ServerAllocation allocation) + private void OnConfigureCreate(FastConfiguration configuration) { - if (Node!.Allocations.Any(x => x.Port == allocation.Port && x.IpAddress == allocation.IpAddress)) - throw new DisplayException("A allocation with these ip and port does already exist"); + configuration.AddProperty(x => x.IpAddress) + .WithDefaultComponent() + .WithValidation(FastValidators.Required) + .WithValidation(RegexValidator.Create("^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$", "You need to provide a valid ipv4 address")); - return Task.CompletedTask; + configuration.AddProperty(x => x.Port) + .WithDefaultComponent() + .WithValidation(x => x >= 1 && x <= 65535 ? ValidationResult.Success : new ValidationResult("You need to provide a valid port")); } + + private void OnConfigureEdit(FastConfiguration configuration, ServerAllocation _) => OnConfigureCreate(configuration); } \ No newline at end of file diff --git a/Moonlight/Features/Servers/UI/NodeComponents/NodeOverview.razor b/Moonlight/Features/Servers/UI/NodeComponents/NodeOverview.razor index 13ca6920..302cd7c1 100644 --- a/Moonlight/Features/Servers/UI/NodeComponents/NodeOverview.razor +++ b/Moonlight/Features/Servers/UI/NodeComponents/NodeOverview.razor @@ -8,7 +8,7 @@ @implements IDisposable -
+
@{ var cpuName = Status.Hardware.Cores.Any() ? Status.Hardware.Cores.First().Name : "N/A"; diff --git a/Moonlight/Features/Servers/UI/Views/Admin/Nodes/Index.razor b/Moonlight/Features/Servers/UI/Views/Admin/Nodes/Index.razor index 7a0148a5..59ec6be5 100644 --- a/Moonlight/Features/Servers/UI/Views/Admin/Nodes/Index.razor +++ b/Moonlight/Features/Servers/UI/Views/Admin/Nodes/Index.razor @@ -1,5 +1,6 @@ @page "/admin/servers/nodes" +@using System.ComponentModel.DataAnnotations @using Moonlight.Features.Servers.Models.Forms.Admin.Nodes @using Moonlight.Features.Servers.UI.Components @using Moonlight.Features.Servers.Entities @@ -8,8 +9,11 @@ @using MoonCore.Exceptions @using MoonCore.Helpers @using System.Text.RegularExpressions; +@using MoonCore.Blazor.Forms.Fast.Components +@using MoonCore.Blazor.Models.Fast @using Moonlight.Features.Servers.Api.Resources @using Moonlight.Features.Servers.Services +@using Moonlight.Features.Servers.UI.NodeComponents @inject Repository ServerRepository @inject Repository NodeRepository @@ -24,18 +28,18 @@ - + @@ -63,7 +67,7 @@ @if (NodeStats.ContainsKey(context!.Id) && NodeStats[context.Id] != null) { var cores = NodeStats[context!.Id]!.Hardware.Cores; - var percent = cores.Any() ? Math.Round(cores.Average(x => x.Usage), 2) : 0; + var percent = cores.Any() ? Math.Round(cores.Average(x => x.Usage), 2) : 0; } @@ -78,10 +82,10 @@ @if (NodeStats.ContainsKey(context!.Id) && NodeStats[context.Id] != null) { var memory = NodeStats[context!.Id]!.Hardware.Memory; - + var used = memory.Total - (memory.Available + memory.Cached); - var percent = Math.Round((float) used / memory.Total * 100F, 2); - + var percent = Math.Round((float)used / memory.Total * 100F, 2); + //Logger.Debug($"Used: {used} Total: {memory.Total} => {percent}% ({Formatter.FormatSize(used)} / {Formatter.FormatSize(memory.Total)})"); @@ -108,13 +112,7 @@ - @* - - - Add a new node in order to get started. Need help? Check out our documentation - - *@ - + @code @@ -126,78 +124,162 @@ { UpdateTimer = new(async _ => { - NodeStats.Clear(); - - using var scope = ServiceProvider.CreateScope(); - var nodeRepo = scope.ServiceProvider.GetRequiredService>(); - var nodes = nodeRepo.Get().ToArray(); - - foreach (var node in nodes) + try { - try + NodeStats.Clear(); + + using var scope = ServiceProvider.CreateScope(); + var nodeRepo = scope.ServiceProvider.GetRequiredService>(); + var nodes = nodeRepo.Get().ToArray(); + + foreach (var node in nodes) { - var status = await NodeService.GetStatus(node); + try + { + var status = await NodeService.GetStatus(node); - NodeStats[node.Id] = status; + NodeStats[node.Id] = status; + } + catch (Exception e) + { + Logger.LogWarning("Unable to fetch system status for node '{name}': {e}", node.Name, e); + + NodeStats[node.Id] = null; + } + + await InvokeAsync(StateHasChanged); } - catch (Exception e) - { - Logger.LogWarning("Unable to fetch system status for node '{name}': {e}", node.Name, e); - - NodeStats[node.Id] = null; - } - - await InvokeAsync(StateHasChanged); + } + catch (Exception e) + { + Logger.LogError("Unable to update node stats due to an unhandled error: {e}", e); } }, null, TimeSpan.Zero, TimeSpan.FromSeconds(1)); return Task.CompletedTask; } - - private IEnumerable LoadData(Repository repository) + + private IEnumerable Loader(Repository repository) { return repository - .Get(); - } - - private Task ValidateDelete(ServerNode node) - { - if (ServerRepository .Get() - .Any(x => x.Node.Id == node.Id)) - { - throw new DisplayException("There are still servers on this node. Delete the servers in order to delete the node"); - } - - if (NodeRepository - .Get() - .Include(x => x.Allocations) - .First(x => x.Id == node.Id) - .Allocations - .Any()) - { - throw new DisplayException("There are still allocations on this node. Delete the allocations in order to delete the node"); - } - - return Task.CompletedTask; + .Include(x => x.Allocations); } - private Task ValidateAdd(ServerNode node) + private void OnConfigure(FastCrudConfiguration configuration) { - ValidateFqdn(node); + configuration.ValidateDelete = node => + { + if (ServerRepository + .Get() + .Any(x => x.Node.Id == node.Id)) + { + throw new DisplayException("There are still servers on this node. Delete the servers in order to delete the node"); + } - node.Token = Formatter.GenerateString(32); + if (NodeRepository + .Get() + .Include(x => x.Allocations) + .First(x => x.Id == node.Id) + .Allocations + .Any()) + { + throw new DisplayException("There are still allocations on this node. Delete the allocations in order to delete the node"); + } - return Task.CompletedTask; + return Task.CompletedTask; + }; + + configuration.ValidateCreate = node => + { + ValidateFqdn(node); + + node.Token = Formatter.GenerateString(32); + + return Task.CompletedTask; + }; + + configuration.ValidateEdit = node => + { + ValidateFqdn(node); + + return Task.CompletedTask; + }; } - private Task ValidateUpdate(ServerNode node) + private void OnConfigureCreate(FastConfiguration configuration) { - ValidateFqdn(node); + configuration.AddProperty(x => x.Name) + .WithDefaultComponent() + .WithValidation(FastValidators.Required); - return Task.CompletedTask; + configuration.AddProperty(x => x.Fqdn) + .WithDefaultComponent() + .WithDescription("This needs to be the ip or domain of the node depending on the ssl settings") + .WithValidation(FastValidators.Required); + + configuration.AddProperty(x => x.Ssl) + .WithComponent() + .WithDescription("This enables ssl for the http connections to the node. Only enable this if you have the cert installed on the node"); + + configuration.AddProperty(x => x.HttpPort) + .WithDefaultComponent() + .WithDescription("This is the http(s) port used by the node to allow communication to the node from the panel"); + + configuration.AddProperty(x => x.FtpPort) + .WithDefaultComponent() + .WithDescription("This is the ftp port users can use to access their servers filesystem via their ftp client"); } + private void OnConfigureEdit(FastConfiguration configuration, ServerNode node) + { + configuration.AddProperty(x => x.Name) + .WithDefaultComponent() + .WithPage("Settings") + .WithValidation(FastValidators.Required); + + configuration.AddProperty(x => x.Fqdn) + .WithDefaultComponent() + .WithPage("Settings") + .WithDescription("This needs to be the ip or domain of the node depending on the ssl settings") + .WithValidation(FastValidators.Required); + + configuration.AddProperty(x => x.Ssl) + .WithComponent() + .WithPage("Settings") + .WithDescription("This enables ssl for the http connections to the node. Only enable this if you have the cert installed on the node"); + + configuration.AddProperty(x => x.HttpPort) + .WithDefaultComponent() + .WithPage("Settings") + .WithDescription("This is the http(s) port used by the node to allow communication to the node from the panel"); + + configuration.AddProperty(x => x.FtpPort) + .WithDefaultComponent() + .WithPage("Settings") + .WithDescription("This is the ftp port users can use to access their servers filesystem via their ftp client"); + + configuration.AddCustomPage("Overview", ComponentHelper.FromType(parameters => + { + parameters.Add("Node", node); + })); + + configuration.AddCustomPage("Allocations", ComponentHelper.FromType(parameters => + { + parameters.Add("Node", node); + })); + + configuration.AddCustomPage("Setup", ComponentHelper.FromType(parameters => + { + parameters.Add("Node", node); + })); + + configuration.AddCustomPage("Logs", ComponentHelper.FromType(parameters => + { + parameters.Add("Node", node); + })); + } + private void ValidateFqdn(ServerNode node) { if (node.Ssl) @@ -213,7 +295,7 @@ // Is it a valid domain? if (Regex.IsMatch(node.Fqdn, "^(?!-)(?:[a-zA-Z\\d-]{0,62}[a-zA-Z\\d]\\.)+(?:[a-zA-Z]{2,})$")) return; - + // Is it a valid ip? if (Regex.IsMatch(node.Fqdn, "^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$")) return; @@ -221,7 +303,7 @@ throw new DisplayException("The fqdn needs to be either a domain or an ip"); } } - + public void Dispose() { UpdateTimer?.Dispose(); diff --git a/Moonlight/Features/Servers/UI/Views/Servers/Networks.razor b/Moonlight/Features/Servers/UI/Views/Servers/Networks.razor index 38fb1df1..7b01e632 100644 --- a/Moonlight/Features/Servers/UI/Views/Servers/Networks.razor +++ b/Moonlight/Features/Servers/UI/Views/Servers/Networks.razor @@ -74,7 +74,7 @@ private void OnConfigure(FastCrudConfiguration configuration) { - configuration.ValidateCreate += network => + configuration.ValidateCreate = network => { if (!ServerRepository .Get() diff --git a/Moonlight/Moonlight.csproj b/Moonlight/Moonlight.csproj index 29ffcc6d..ea125abc 100644 --- a/Moonlight/Moonlight.csproj +++ b/Moonlight/Moonlight.csproj @@ -94,7 +94,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - +