diff --git a/MoonlightServers.ApiServer/Database/Entities/Server.cs b/MoonlightServers.ApiServer/Database/Entities/Server.cs index c25fedf..97f2232 100644 --- a/MoonlightServers.ApiServer/Database/Entities/Server.cs +++ b/MoonlightServers.ApiServer/Database/Entities/Server.cs @@ -8,6 +8,8 @@ public class Server public Star Star { get; set; } public Node Node { get; set; } public List Allocations { get; set; } = new(); + public List Variables { get; set; } = new(); + public List Backups { get; set; } = new(); // Meta public string Name { get; set; } diff --git a/MoonlightServers.ApiServer/Database/Migrations/20241215151257_AddedServerRelations.Designer.cs b/MoonlightServers.ApiServer/Database/Migrations/20241215151257_AddedServerRelations.Designer.cs new file mode 100644 index 0000000..0d8d09a --- /dev/null +++ b/MoonlightServers.ApiServer/Database/Migrations/20241215151257_AddedServerRelations.Designer.cs @@ -0,0 +1,453 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using MoonlightServers.ApiServer.Database; + +#nullable disable + +namespace MoonlightServers.ApiServer.Database.Migrations +{ + [DbContext(typeof(ServersDataContext))] + [Migration("20241215151257_AddedServerRelations")] + partial class AddedServerRelations + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasDefaultSchema("Servers") + .HasAnnotation("ProductVersion", "8.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.Allocation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("IpAddress") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("NodeId") + .HasColumnType("int"); + + b.Property("Port") + .HasColumnType("int"); + + b.Property("ServerId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("NodeId"); + + b.HasIndex("ServerId"); + + b.ToTable("Allocations", "Servers"); + }); + + modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.Node", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("EnableDynamicFirewall") + .HasColumnType("tinyint(1)"); + + b.Property("EnableTransparentMode") + .HasColumnType("tinyint(1)"); + + b.Property("Fqdn") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("FtpPort") + .HasColumnType("int"); + + b.Property("HttpPort") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Token") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("UseSsl") + .HasColumnType("tinyint(1)"); + + b.HasKey("Id"); + + b.ToTable("Nodes", "Servers"); + }); + + modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.Server", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("Bandwidth") + .HasColumnType("int"); + + b.Property("Cpu") + .HasColumnType("int"); + + b.Property("Disk") + .HasColumnType("int"); + + b.Property("DockerImageIndex") + .HasColumnType("int"); + + b.Property("Memory") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("NodeId") + .HasColumnType("int"); + + b.Property("OwnerId") + .HasColumnType("int"); + + b.Property("StarId") + .HasColumnType("int"); + + b.Property("StartupOverride") + .HasColumnType("longtext"); + + b.Property("UseVirtualDisk") + .HasColumnType("tinyint(1)"); + + b.HasKey("Id"); + + b.HasIndex("NodeId"); + + b.HasIndex("StarId"); + + b.ToTable("Servers", "Servers"); + }); + + modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.ServerBackup", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("Completed") + .HasColumnType("tinyint(1)"); + + b.Property("CompletedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("ServerId") + .HasColumnType("int"); + + b.Property("Size") + .HasColumnType("bigint"); + + b.Property("Successful") + .HasColumnType("tinyint(1)"); + + b.HasKey("Id"); + + b.HasIndex("ServerId"); + + b.ToTable("ServerBackups", "Servers"); + }); + + modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.ServerVariable", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("Key") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ServerId") + .HasColumnType("int"); + + b.Property("Value") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("ServerId"); + + b.ToTable("ServerVariables", "Servers"); + }); + + modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.Star", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("AllowDockerImageChange") + .HasColumnType("tinyint(1)"); + + b.Property("Author") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("DefaultDockerImage") + .HasColumnType("int"); + + b.Property("DonateUrl") + .HasColumnType("longtext"); + + b.Property("InstallDockerImage") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("InstallScript") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("InstallShell") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("OnlineDetection") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ParseConfiguration") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("RequiredAllocations") + .HasColumnType("int"); + + b.Property("StartupCommand") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("StopCommand") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("UpdateUrl") + .HasColumnType("longtext"); + + b.Property("Version") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("Stars", "Servers"); + }); + + modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.StarDockerImage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("AutoPulling") + .HasColumnType("tinyint(1)"); + + b.Property("DisplayName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Identifier") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("StarId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("StarId"); + + b.ToTable("StarDockerImages", "Servers"); + }); + + modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.StarVariable", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("AllowEditing") + .HasColumnType("tinyint(1)"); + + b.Property("AllowViewing") + .HasColumnType("tinyint(1)"); + + b.Property("DefaultValue") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Description") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Filter") + .HasColumnType("longtext"); + + b.Property("Key") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("StarId") + .HasColumnType("int"); + + b.Property("Type") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("StarId"); + + b.ToTable("StarVariables", "Servers"); + }); + + modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.Allocation", b => + { + b.HasOne("MoonlightServers.ApiServer.Database.Entities.Node", "Node") + .WithMany("Allocations") + .HasForeignKey("NodeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MoonlightServers.ApiServer.Database.Entities.Server", "Server") + .WithMany("Allocations") + .HasForeignKey("ServerId"); + + b.Navigation("Node"); + + b.Navigation("Server"); + }); + + modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.Server", b => + { + b.HasOne("MoonlightServers.ApiServer.Database.Entities.Node", "Node") + .WithMany("Servers") + .HasForeignKey("NodeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MoonlightServers.ApiServer.Database.Entities.Star", "Star") + .WithMany() + .HasForeignKey("StarId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Node"); + + b.Navigation("Star"); + }); + + modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.ServerBackup", b => + { + b.HasOne("MoonlightServers.ApiServer.Database.Entities.Server", null) + .WithMany("Backups") + .HasForeignKey("ServerId"); + }); + + modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.ServerVariable", b => + { + b.HasOne("MoonlightServers.ApiServer.Database.Entities.Server", "Server") + .WithMany("Variables") + .HasForeignKey("ServerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Server"); + }); + + modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.StarDockerImage", b => + { + b.HasOne("MoonlightServers.ApiServer.Database.Entities.Star", "Star") + .WithMany("DockerImages") + .HasForeignKey("StarId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Star"); + }); + + modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.StarVariable", b => + { + b.HasOne("MoonlightServers.ApiServer.Database.Entities.Star", "Star") + .WithMany("Variables") + .HasForeignKey("StarId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Star"); + }); + + modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.Node", b => + { + b.Navigation("Allocations"); + + b.Navigation("Servers"); + }); + + modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.Server", b => + { + b.Navigation("Allocations"); + + b.Navigation("Backups"); + + b.Navigation("Variables"); + }); + + modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.Star", b => + { + b.Navigation("DockerImages"); + + b.Navigation("Variables"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/MoonlightServers.ApiServer/Database/Migrations/20241215151257_AddedServerRelations.cs b/MoonlightServers.ApiServer/Database/Migrations/20241215151257_AddedServerRelations.cs new file mode 100644 index 0000000..c13aef3 --- /dev/null +++ b/MoonlightServers.ApiServer/Database/Migrations/20241215151257_AddedServerRelations.cs @@ -0,0 +1,55 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace MoonlightServers.ApiServer.Database.Migrations +{ + /// + public partial class AddedServerRelations : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "ServerId", + schema: "Servers", + table: "ServerBackups", + type: "int", + nullable: true); + + migrationBuilder.CreateIndex( + name: "IX_ServerBackups_ServerId", + schema: "Servers", + table: "ServerBackups", + column: "ServerId"); + + migrationBuilder.AddForeignKey( + name: "FK_ServerBackups_Servers_ServerId", + schema: "Servers", + table: "ServerBackups", + column: "ServerId", + principalSchema: "Servers", + principalTable: "Servers", + principalColumn: "Id"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_ServerBackups_Servers_ServerId", + schema: "Servers", + table: "ServerBackups"); + + migrationBuilder.DropIndex( + name: "IX_ServerBackups_ServerId", + schema: "Servers", + table: "ServerBackups"); + + migrationBuilder.DropColumn( + name: "ServerId", + schema: "Servers", + table: "ServerBackups"); + } + } +} diff --git a/MoonlightServers.ApiServer/Database/Migrations/ServersDataContextModelSnapshot.cs b/MoonlightServers.ApiServer/Database/Migrations/ServersDataContextModelSnapshot.cs index c1fb055..b52522f 100644 --- a/MoonlightServers.ApiServer/Database/Migrations/ServersDataContextModelSnapshot.cs +++ b/MoonlightServers.ApiServer/Database/Migrations/ServersDataContextModelSnapshot.cs @@ -161,6 +161,9 @@ namespace MoonlightServers.ApiServer.Database.Migrations b.Property("CreatedAt") .HasColumnType("datetime(6)"); + b.Property("ServerId") + .HasColumnType("int"); + b.Property("Size") .HasColumnType("bigint"); @@ -169,6 +172,8 @@ namespace MoonlightServers.ApiServer.Database.Migrations b.HasKey("Id"); + b.HasIndex("ServerId"); + b.ToTable("ServerBackups", "Servers"); }); @@ -377,10 +382,17 @@ namespace MoonlightServers.ApiServer.Database.Migrations b.Navigation("Star"); }); + modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.ServerBackup", b => + { + b.HasOne("MoonlightServers.ApiServer.Database.Entities.Server", null) + .WithMany("Backups") + .HasForeignKey("ServerId"); + }); + modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.ServerVariable", b => { b.HasOne("MoonlightServers.ApiServer.Database.Entities.Server", "Server") - .WithMany() + .WithMany("Variables") .HasForeignKey("ServerId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); @@ -420,6 +432,10 @@ namespace MoonlightServers.ApiServer.Database.Migrations modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.Server", b => { b.Navigation("Allocations"); + + b.Navigation("Backups"); + + b.Navigation("Variables"); }); modelBuilder.Entity("MoonlightServers.ApiServer.Database.Entities.Star", b => diff --git a/MoonlightServers.ApiServer/Http/Controllers/Admin/Nodes/NodesController.cs b/MoonlightServers.ApiServer/Http/Controllers/Admin/Nodes/NodesController.cs index 66c39ea..5025ef2 100644 --- a/MoonlightServers.ApiServer/Http/Controllers/Admin/Nodes/NodesController.cs +++ b/MoonlightServers.ApiServer/Http/Controllers/Admin/Nodes/NodesController.cs @@ -25,18 +25,6 @@ public class NodesController : Controller { CrudHelper = crudHelper; NodeRepository = nodeRepository; - - CrudHelper.QueryModifier = nodes => - nodes.Include(x => x.Allocations); - - CrudHelper.LateMapper = (node, response) => - { - response.Allocations = node.Allocations - .Select(x => Mapper.Map(x)) - .ToArray(); - - return response; - }; } [HttpGet] diff --git a/MoonlightServers.ApiServer/Http/Controllers/Admin/Servers/ServersController.cs b/MoonlightServers.ApiServer/Http/Controllers/Admin/Servers/ServersController.cs new file mode 100644 index 0000000..a162f6e --- /dev/null +++ b/MoonlightServers.ApiServer/Http/Controllers/Admin/Servers/ServersController.cs @@ -0,0 +1,174 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using MoonCore.Attributes; +using MoonCore.Exceptions; +using MoonCore.Extended.Abstractions; +using MoonCore.Extended.Helpers; +using MoonCore.Helpers; +using MoonCore.Models; +using MoonlightServers.ApiServer.Database.Entities; +using MoonlightServers.Shared.Http.Requests.Admin.Servers; +using MoonlightServers.Shared.Http.Responses.Admin.NodeAllocations; +using MoonlightServers.Shared.Http.Responses.Admin.Servers; +using MoonlightServers.Shared.Http.Responses.Admin.ServerVariables; + +namespace MoonlightServers.ApiServer.Http.Controllers.Admin.Servers; + +[ApiController] +[Route("api/admin/servers")] +public class ServersController : Controller +{ + private readonly CrudHelper CrudHelper; + private readonly DatabaseRepository StarRepository; + private readonly DatabaseRepository NodeRepository; + private readonly DatabaseRepository AllocationRepository; + private readonly DatabaseRepository VariableRepository; + private readonly DatabaseRepository ServerRepository; + + public ServersController( + CrudHelper crudHelper, + DatabaseRepository starRepository, + DatabaseRepository nodeRepository, + DatabaseRepository allocationRepository, + DatabaseRepository variableRepository, + DatabaseRepository serverRepository + ) + { + CrudHelper = crudHelper; + StarRepository = starRepository; + NodeRepository = nodeRepository; + AllocationRepository = allocationRepository; + VariableRepository = variableRepository; + ServerRepository = serverRepository; + + CrudHelper.QueryModifier = servers => servers + .Include(x => x.Node) + .Include(x => x.Allocations) + .Include(x => x.Variables) + .Include(x => x.Star); + + CrudHelper.LateMapper = (server, response) => + { + response.NodeId = server.Node.Id; + response.StarId = server.Star.Id; + + return response; + }; + } + + [HttpGet] + [RequirePermission("admin.servers.get")] + public async Task> Get([FromQuery] int page, [FromQuery] int pageSize) + { + return await CrudHelper.Get(page, pageSize); + } + + [HttpGet("{id:int}")] + [RequirePermission("admin.servers.get")] + public async Task GetSingle([FromRoute] int id) + { + return await CrudHelper.GetSingle(id); + } + + [HttpPost] + [RequirePermission("admin.servers.create")] + public async Task Create([FromBody] CreateServerRequest request) + { + // Construct model + var server = Mapper.Map(request); + + var star = StarRepository + .Get() + .Include(x => x.Variables) + .Include(x => x.DockerImages) + .FirstOrDefault(x => x.Id == request.StarId); + + if (star == null) + throw new HttpApiException("No star with this id found", 400); + + var node = NodeRepository + .Get() + .FirstOrDefault(x => x.Id == request.NodeId); + + if (node == null) + throw new HttpApiException("No node with this id found", 400); + + var allocations = new List(); + + // Fetch specified allocations from the request + foreach (var allocationId in request.AllocationIds) + { + var allocation = AllocationRepository + .Get() + .Where(x => x.Server == null) + .Where(x => x.Node.Id == node.Id) + .FirstOrDefault(x => x.Id == allocationId); + + if (allocation == null) + continue; + + allocations.Add(allocation); + } + + // Check if the specified allocations are enough for the star + if (allocations.Count < star.RequiredAllocations) + { + var amountRequiredToSatisfy = star.RequiredAllocations - allocations.Count; + + var freeAllocations = AllocationRepository + .Get() + .Where(x => x.Server == null) + .Where(x => x.Node.Id == node.Id) + .Take(amountRequiredToSatisfy); + + allocations.AddRange(freeAllocations); + + if (allocations.Count < star.RequiredAllocations) + { + throw new HttpApiException( + $"Unable to find enough free allocations. Found: {allocations.Count}, Required: {star.RequiredAllocations}", + 400 + ); + } + } + + // Set allocations + server.Allocations = allocations; + + // Variables + foreach (var variable in star.Variables) + { + var serverVar = new ServerVariable() + { + Key = variable.Key, + Value = request.Variables.TryGetValue(variable.Key, out var value) + ? value + : variable.DefaultValue + }; + + server.Variables.Add(serverVar); + } + + // Set relations + server.Node = node; + server.Star = star; + + // TODO: Call node + + var finalServer = ServerRepository.Add(server); + + return CrudHelper.MapToResult(finalServer); + } + + [HttpPatch("{id:int}")] + public async Task Update([FromRoute] int id, [FromBody] UpdateServerRequest request) + { + return await CrudHelper.Update(id, request); + } + + [HttpDelete("{id:int}")] + public async Task Delete([FromRoute] int id) + { + await CrudHelper.Delete(id); + } +} \ No newline at end of file diff --git a/MoonlightServers.ApiServer/Http/Controllers/Admin/Stars/StarsController.cs b/MoonlightServers.ApiServer/Http/Controllers/Admin/Stars/StarsController.cs index 7b743ce..342c0ed 100644 --- a/MoonlightServers.ApiServer/Http/Controllers/Admin/Stars/StarsController.cs +++ b/MoonlightServers.ApiServer/Http/Controllers/Admin/Stars/StarsController.cs @@ -24,23 +24,6 @@ public class StarsController : Controller { CrudHelper = crudHelper; StarRepository = starRepository; - - CrudHelper.QueryModifier = stars => stars - .Include(x => x.Variables) - .Include(x => x.DockerImages); - - CrudHelper.LateMapper = (star, response) => - { - response.DockerImages = star.DockerImages - .Select(x => Mapper.Map(x)) - .ToArray(); - - response.Variables = star.Variables - .Select(x => Mapper.Map(x)) - .ToArray(); - - return response; - }; } [HttpGet] diff --git a/MoonlightServers.Frontend/MoonlightServers.Frontend.csproj b/MoonlightServers.Frontend/MoonlightServers.Frontend.csproj index 1657a57..82a338f 100644 --- a/MoonlightServers.Frontend/MoonlightServers.Frontend.csproj +++ b/MoonlightServers.Frontend/MoonlightServers.Frontend.csproj @@ -20,7 +20,6 @@ - diff --git a/MoonlightServers.Frontend/UI/Components/Nodes/UpdateNodePartials/AllocationsNodeUpdate.razor b/MoonlightServers.Frontend/UI/Components/Nodes/UpdateNodePartials/AllocationsNodeUpdate.razor index 7ebeba1..57ca53d 100644 --- a/MoonlightServers.Frontend/UI/Components/Nodes/UpdateNodePartials/AllocationsNodeUpdate.razor +++ b/MoonlightServers.Frontend/UI/Components/Nodes/UpdateNodePartials/AllocationsNodeUpdate.razor @@ -23,8 +23,8 @@
- - + +
diff --git a/MoonlightServers.Frontend/UI/Components/Servers/CreateServerPartials/AdvancedServerCreate.razor b/MoonlightServers.Frontend/UI/Components/Servers/CreateServerPartials/AdvancedServerCreate.razor new file mode 100644 index 0000000..7e76db5 --- /dev/null +++ b/MoonlightServers.Frontend/UI/Components/Servers/CreateServerPartials/AdvancedServerCreate.razor @@ -0,0 +1,23 @@ +@using MoonlightServers.Shared.Http.Requests.Admin.Servers +@using MoonlightServers.Frontend.UI.Components.Forms + +
+
+ +
+ +
+
+ +
+ +
+ +
+
+
+ +@code +{ + [Parameter] public CreateServerRequest Request { get; set; } +} \ No newline at end of file diff --git a/MoonlightServers.Frontend/UI/Components/Servers/CreateServerPartials/GeneralServerCreate.razor b/MoonlightServers.Frontend/UI/Components/Servers/CreateServerPartials/GeneralServerCreate.razor new file mode 100644 index 0000000..158b1ed --- /dev/null +++ b/MoonlightServers.Frontend/UI/Components/Servers/CreateServerPartials/GeneralServerCreate.razor @@ -0,0 +1,89 @@ +@using MoonlightServers.Shared.Http.Requests.Admin.Servers +@using MoonlightServers.Frontend.UI.Components.Forms +@using MoonCore.Helpers +@using MoonCore.Models +@using MoonlightServers.Shared.Http.Responses.Admin.Nodes +@using MoonlightServers.Shared.Http.Responses.Admin.Stars +@using MoonCore.Blazor.Tailwind.Inputs + +@inject HttpApiClient ApiClient + +
+
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+
+ +
+

+ Resources +

+

Define the servers resource limit

+ +
+
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+
+
+ +@code +{ + [Parameter] public CreateServerRequest Request { get; set; } + + private async Task LoadStars() + { + var starData = await ApiClient.GetJson>("api/admin/servers/stars?page=0&pageSize=50"); + return starData.Items; + } + + private async Task LoadNodes() + { + var nodeData = await ApiClient.GetJson>("api/admin/servers/nodes?page=0&pageSize=50"); + return nodeData.Items; + } +} \ No newline at end of file diff --git a/MoonlightServers.Frontend/UI/Views/Admin/All/Create.razor b/MoonlightServers.Frontend/UI/Views/Admin/All/Create.razor new file mode 100644 index 0000000..6a51c91 --- /dev/null +++ b/MoonlightServers.Frontend/UI/Views/Admin/All/Create.razor @@ -0,0 +1,58 @@ +@page "/admin/servers/all/create" + +@using MoonCore.Blazor.Tailwind.Components +@using MoonCore.Blazor.Tailwind.Forms +@using MoonCore.Blazor.Tailwind.Toasts +@using MoonCore.Helpers +@using MoonlightServers.Shared.Http.Requests.Admin.Servers +@using MoonlightServers.Frontend.UI.Components.Servers.CreateServerPartials + +@inject HttpApiClient ApiClient +@inject NavigationManager Navigation +@inject ToastService ToastService + + + + + + Create + + + +
+ + + + + + + + + + +
+ +@code +{ + private HandleForm Form; + private CreateServerRequest Request; + + protected override void OnInitialized() + { + Request = new(); + } + + private async Task OnSubmit() + { + await ApiClient.Post("api/admin/servers", Request); + + await ToastService.Success("Successfully created Server"); + GoBack(); + } + + private void GoBack() + => Navigation.NavigateTo(ComponentHelper.GetRouteOfComponent()!); +} \ No newline at end of file diff --git a/MoonlightServers.Frontend/UI/Views/Admin/All/Index.razor b/MoonlightServers.Frontend/UI/Views/Admin/All/Index.razor new file mode 100644 index 0000000..fa92449 --- /dev/null +++ b/MoonlightServers.Frontend/UI/Views/Admin/All/Index.razor @@ -0,0 +1,94 @@ +@page "/admin/servers/all" + +@using MoonCore.Blazor.Tailwind.MinimalCrud +@using MoonCore.Blazor.Tailwind.DataTable +@using MoonCore.Helpers +@using MoonCore.Models +@using MoonlightServers.Shared.Http.Responses.Admin.Nodes +@using MoonlightServers.Shared.Http.Responses.Admin.Servers +@using MoonlightServers.Shared.Http.Responses.Admin.Stars +@using MoonCore.Blazor.Tailwind.Components + +@inject HttpApiClient ApiClient + +
+ +
+ + + + + + + + + + + + + + +@code +{ + private List Stars = new(); + private List Nodes = new(); + + private void OnConfigure(MinimalCrudOptions options) + { + options.Title = "Servers"; + options.ItemLoader = LoadItems; + + options.CreateUrl = ComponentHelper.GetRouteOfComponent(); + options.UpdateUrl = item => ComponentHelper.GetRouteOfComponent(item.Id)!; + options.DeleteFunction = async item => await ApiClient.Delete($"api/admin/servers/{item.Id}"); + } + + private async Task> LoadItems(int page, int pageSize) + { + // Clear potential previous data + var data = await ApiClient.GetJson>($"api/admin/servers?page={page}&pageSize={pageSize}"); + + foreach (var item in data.Items) + { + if (Nodes.All(x => x.Id != item.NodeId)) + { + var node = await ApiClient.GetJson($"api/admin/servers/nodes/{item.NodeId}"); + Nodes.Add(node); + } + + if (Stars.All(x => x.Id != item.StarId)) + { + var star = await ApiClient.GetJson($"api/admin/servers/stars/{item.StarId}"); + Stars.Add(star); + } + } + + return data; + } +} \ No newline at end of file diff --git a/MoonlightServers.Frontend/UI/Views/Admin/All/Update.razor b/MoonlightServers.Frontend/UI/Views/Admin/All/Update.razor new file mode 100644 index 0000000..682660c --- /dev/null +++ b/MoonlightServers.Frontend/UI/Views/Admin/All/Update.razor @@ -0,0 +1,61 @@ +@page "/admin/servers/all/update/{Id:int}" + +@using MoonCore.Blazor.Tailwind.Components +@using MoonCore.Blazor.Tailwind.Forms +@using MoonCore.Blazor.Tailwind.Toasts +@using MoonCore.Helpers +@using MoonlightServers.Shared.Http.Requests.Admin.Servers +@using MoonlightServers.Shared.Http.Responses.Admin.Servers + +@inject HttpApiClient ApiClient +@inject NavigationManager Navigation +@inject ToastService ToastService + + + + + + + Update + + + +
+ + + +
+
+ +@code +{ + [Parameter] public int Id { get; set; } + + private HandleForm Form; + private UpdateServerRequest Request; + + private async Task Load(LazyLoader _) + { + var detail = await ApiClient.GetJson($"api/admin/servers/{Id}"); + Request = Mapper.Map(detail); + } + + private void OnConfigure(FormConfiguration configuration) + { + + } + + private async Task OnSubmit() + { + await ApiClient.Patch($"api/admin/servers/{Id}", Request); + + await ToastService.Success("Successfully updated Server"); + GoBack(); + } + + private void GoBack() + => Navigation.NavigateTo(ComponentHelper.GetRouteOfComponent()!); +} \ No newline at end of file diff --git a/MoonlightServers.Shared/Http/Requests/Admin/Servers/CreateServerRequest.cs b/MoonlightServers.Shared/Http/Requests/Admin/Servers/CreateServerRequest.cs new file mode 100644 index 0000000..7b6ae88 --- /dev/null +++ b/MoonlightServers.Shared/Http/Requests/Admin/Servers/CreateServerRequest.cs @@ -0,0 +1,35 @@ +using System.ComponentModel.DataAnnotations; + +namespace MoonlightServers.Shared.Http.Requests.Admin.Servers; + +public class CreateServerRequest +{ + [Required(ErrorMessage = "You need to provide a name for the server")] + public string Name { get; set; } + public int OwnerId { get; set; } + + [Range(1, int.MaxValue, ErrorMessage = "You need to provide a valid cpu percent amount")] + public int Cpu { get; set; } + + [Range(1, int.MaxValue, ErrorMessage = "You need to provide a valid memory amount")] + public int Memory { get; set; } + + [Range(1, int.MaxValue, ErrorMessage = "You need to provide a valid disk amount")] + public int Disk { get; set; } + + public bool UseVirtualDisk { get; set; } + + [Range(0, int.MaxValue, ErrorMessage = "You need to provide a valid bandwidth amount")] + public int Bandwidth { get; set; } + + public string? StartupOverride { get; set; } + + public int DockerImageIndex { get; set; } + + public int StarId { get; set; } + + public int NodeId { get; set; } + + public int[] AllocationIds { get; set; } = []; + public Dictionary Variables { get; set; } = new(); +} \ No newline at end of file diff --git a/MoonlightServers.Shared/Http/Requests/Admin/Servers/UpdateServerRequest.cs b/MoonlightServers.Shared/Http/Requests/Admin/Servers/UpdateServerRequest.cs new file mode 100644 index 0000000..b01af90 --- /dev/null +++ b/MoonlightServers.Shared/Http/Requests/Admin/Servers/UpdateServerRequest.cs @@ -0,0 +1,35 @@ +using System.ComponentModel.DataAnnotations; + +namespace MoonlightServers.Shared.Http.Requests.Admin.Servers; + +public class UpdateServerRequest +{ + [Required(ErrorMessage = "You need to provide a name for the server")] + public string Name { get; set; } + public int OwnerId { get; set; } + + [Range(1, int.MaxValue, ErrorMessage = "You need to provide a valid cpu percent amount")] + public int Cpu { get; set; } + + [Range(1, int.MaxValue, ErrorMessage = "You need to provide a valid memory amount")] + public int Memory { get; set; } + + [Range(1, int.MaxValue, ErrorMessage = "You need to provide a valid disk amount")] + public int Disk { get; set; } + + public bool UseVirtualDisk { get; set; } + + [Range(0, int.MaxValue, ErrorMessage = "You need to provide a valid bandwidth amount")] + public int Bandwidth { get; set; } + + public string? StartupOverride { get; set; } + + public int DockerImageIndex { get; set; } + + public int StarId { get; set; } + + public int NodeId { get; set; } + + public int[] AllocationIds { get; set; } = []; + public Dictionary Variables { get; set; } = new(); +} \ No newline at end of file diff --git a/MoonlightServers.Shared/Http/Responses/Admin/Nodes/NodeDetailResponse.cs b/MoonlightServers.Shared/Http/Responses/Admin/Nodes/NodeDetailResponse.cs index ff97587..3ca8790 100644 --- a/MoonlightServers.Shared/Http/Responses/Admin/Nodes/NodeDetailResponse.cs +++ b/MoonlightServers.Shared/Http/Responses/Admin/Nodes/NodeDetailResponse.cs @@ -5,9 +5,6 @@ namespace MoonlightServers.Shared.Http.Responses.Admin.Nodes; public class NodeDetailResponse { public int Id { get; set; } - - //public List Servers { get; set; } = new(); - public NodeAllocationDetailResponse[] Allocations { get; set; } public string Name { get; set; } diff --git a/MoonlightServers.Shared/Http/Responses/Admin/ServerVariables/ServerVariableDetailResponse.cs b/MoonlightServers.Shared/Http/Responses/Admin/ServerVariables/ServerVariableDetailResponse.cs new file mode 100644 index 0000000..f829c0b --- /dev/null +++ b/MoonlightServers.Shared/Http/Responses/Admin/ServerVariables/ServerVariableDetailResponse.cs @@ -0,0 +1,8 @@ +namespace MoonlightServers.Shared.Http.Responses.Admin.ServerVariables; + +public class ServerVariableDetailResponse +{ + public int Id { get; set; } + public string Key { get; set; } + public string Value { get; set; } +} \ No newline at end of file diff --git a/MoonlightServers.Shared/Http/Responses/Admin/Servers/ServerDetailResponse.cs b/MoonlightServers.Shared/Http/Responses/Admin/Servers/ServerDetailResponse.cs new file mode 100644 index 0000000..d4e4d8a --- /dev/null +++ b/MoonlightServers.Shared/Http/Responses/Admin/Servers/ServerDetailResponse.cs @@ -0,0 +1,25 @@ +using MoonlightServers.Shared.Http.Responses.Admin.NodeAllocations; +using MoonlightServers.Shared.Http.Responses.Admin.ServerVariables; + +namespace MoonlightServers.Shared.Http.Responses.Admin.Servers; + +public class ServerDetailResponse +{ + public int Id { get; set; } + + public string Name { get; set; } + public int OwnerId { get; set; } + public int Cpu { get; set; } + public int Memory { get; set; } + public int Disk { get; set; } + public bool UseVirtualDisk { get; set; } + public int Bandwidth { get; set; } + + public string? StartupOverride { get; set; } + + public int DockerImageIndex { get; set; } + + public int StarId { get; set; } + + public int NodeId { get; set; } +} \ No newline at end of file diff --git a/MoonlightServers.Shared/Http/Responses/Admin/Stars/StarDetailResponse.cs b/MoonlightServers.Shared/Http/Responses/Admin/Stars/StarDetailResponse.cs index 7dac373..2958a9c 100644 --- a/MoonlightServers.Shared/Http/Responses/Admin/Stars/StarDetailResponse.cs +++ b/MoonlightServers.Shared/Http/Responses/Admin/Stars/StarDetailResponse.cs @@ -6,10 +6,6 @@ namespace MoonlightServers.Shared.Http.Responses.Admin.Stars; public class StarDetailResponse { public int Id { get; set; } - - // References - public StarVariableDetailResponse[] Variables { get; set; } - public StarDockerImageDetailResponse[] DockerImages { get; set; } // Meta public string Name { get; set; }