From d42fd2095c75abfa826acb026be939c90521fab4 Mon Sep 17 00:00:00 2001 From: Marcel Baumgartner Date: Tue, 14 Mar 2023 22:37:41 +0100 Subject: [PATCH 1/7] Added new image manager. CRUD implemeted --- Moonlight/App/Database/Entities/Image.cs | 4 +- ...30314164750_SwitchedToJsonTags.Designer.cs | 1023 ++++++++++++++++ .../20230314164750_SwitchedToJsonTags.cs | 174 +++ ...190100_AddedAllocationsToImage.Designer.cs | 1026 +++++++++++++++++ .../20230314190100_AddedAllocationsToImage.cs | 29 + .../Migrations/DataContextModelSnapshot.cs | 120 +- Moonlight/Program.cs | 2 +- .../FileManagerPartials/FileEditor.razor | 35 +- .../Components/Partials/SidebarMenu.razor | 2 +- .../Shared/Views/Admin/Servers/Edit.razor | 6 +- .../Views/Admin/Servers/Images/Edit.razor | 353 ++++++ .../Views/Admin/Servers/Images/Index.razor | 91 ++ .../Views/Admin/Servers/Images/New.razor | 340 ++++++ Moonlight/Shared/Views/Server/Index.razor | 10 +- Moonlight/resources/lang/de_de.lang | 23 + 15 files changed, 3198 insertions(+), 40 deletions(-) create mode 100644 Moonlight/App/Database/Migrations/20230314164750_SwitchedToJsonTags.Designer.cs create mode 100644 Moonlight/App/Database/Migrations/20230314164750_SwitchedToJsonTags.cs create mode 100644 Moonlight/App/Database/Migrations/20230314190100_AddedAllocationsToImage.Designer.cs create mode 100644 Moonlight/App/Database/Migrations/20230314190100_AddedAllocationsToImage.cs create mode 100644 Moonlight/Shared/Views/Admin/Servers/Images/Edit.razor create mode 100644 Moonlight/Shared/Views/Admin/Servers/Images/Index.razor create mode 100644 Moonlight/Shared/Views/Admin/Servers/Images/New.razor diff --git a/Moonlight/App/Database/Entities/Image.cs b/Moonlight/App/Database/Entities/Image.cs index 6485dc0a..6e2b6041 100644 --- a/Moonlight/App/Database/Entities/Image.cs +++ b/Moonlight/App/Database/Entities/Image.cs @@ -13,8 +13,8 @@ public class Image public string InstallDockerImage { get; set; } = ""; public string InstallEntrypoint { get; set; } = ""; public string Startup { get; set; } = ""; - + public int Allocations { get; set; } = 1; public List DockerImages { get; set; } = new(); public List Variables { get; set; } = new(); - public List Tags { get; set; } = new(); + public string TagsJson { get; set; } = ""; } \ No newline at end of file diff --git a/Moonlight/App/Database/Migrations/20230314164750_SwitchedToJsonTags.Designer.cs b/Moonlight/App/Database/Migrations/20230314164750_SwitchedToJsonTags.Designer.cs new file mode 100644 index 00000000..5ddb3854 --- /dev/null +++ b/Moonlight/App/Database/Migrations/20230314164750_SwitchedToJsonTags.Designer.cs @@ -0,0 +1,1023 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Moonlight.App.Database; + +#nullable disable + +namespace Moonlight.App.Database.Migrations +{ + [DbContext(typeof(DataContext))] + [Migration("20230314164750_SwitchedToJsonTags")] + partial class SwitchedToJsonTags + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "7.0.3") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + modelBuilder.Entity("Moonlight.App.Database.Entities.AaPanel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("BaseDomain") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Key") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Url") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("AaPanels"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Database", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("AaPanelId") + .HasColumnType("int"); + + b.Property("InternalAaPanelId") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("OwnerId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("AaPanelId"); + + b.HasIndex("OwnerId"); + + b.ToTable("Databases"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.DockerImage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Default") + .HasColumnType("tinyint(1)"); + + b.Property("ImageId") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("ImageId"); + + b.ToTable("DockerImages"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Domain", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("OwnerId") + .HasColumnType("int"); + + b.Property("SharedDomainId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("OwnerId"); + + b.HasIndex("SharedDomainId"); + + b.ToTable("Domains"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Image", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("ConfigFiles") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Description") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("InstallDockerImage") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("InstallEntrypoint") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("InstallScript") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Startup") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("StartupDetection") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("StopCommand") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("TagsJson") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Uuid") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.ToTable("Images"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.ImageTag", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("ImageTags"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.ImageVariable", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("DefaultValue") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ImageId") + .HasColumnType("int"); + + b.Property("Key") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("ImageId"); + + b.ToTable("ImageVariables"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.LoadingMessage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Message") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("LoadingMessages"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.LogsEntries.AuditLogEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Ip") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("JsonData") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("System") + .HasColumnType("tinyint(1)"); + + b.Property("Type") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("AuditLog"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.LogsEntries.ErrorLogEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Class") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Ip") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("JsonData") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Stacktrace") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("System") + .HasColumnType("tinyint(1)"); + + b.HasKey("Id"); + + b.ToTable("ErrorLog"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.LogsEntries.SecurityLogEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Ip") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("JsonData") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("System") + .HasColumnType("tinyint(1)"); + + b.Property("Type") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("SecurityLog"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Node", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Fqdn") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("HttpPort") + .HasColumnType("int"); + + b.Property("MoonlightDaemonPort") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("SftpPort") + .HasColumnType("int"); + + b.Property("Ssl") + .HasColumnType("tinyint(1)"); + + b.Property("Token") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("TokenId") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("Nodes"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.NodeAllocation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + 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("NodeAllocations"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Notification.NotificationAction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Action") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("NotificationClientId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("NotificationClientId"); + + b.ToTable("NotificationActions"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Notification.NotificationClient", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("NotificationClients"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Revoke", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Identifier") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("Revokes"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Server", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Cpu") + .HasColumnType("int"); + + b.Property("Disk") + .HasColumnType("bigint"); + + b.Property("DockerImageIndex") + .HasColumnType("int"); + + b.Property("ImageId") + .HasColumnType("int"); + + b.Property("Installing") + .HasColumnType("tinyint(1)"); + + b.Property("MainAllocationId") + .HasColumnType("int"); + + b.Property("Memory") + .HasColumnType("bigint"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("NodeId") + .HasColumnType("int"); + + b.Property("OverrideStartup") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("OwnerId") + .HasColumnType("int"); + + b.Property("Suspended") + .HasColumnType("tinyint(1)"); + + b.Property("Uuid") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("ImageId"); + + b.HasIndex("MainAllocationId"); + + b.HasIndex("NodeId"); + + b.HasIndex("OwnerId"); + + b.ToTable("Servers"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.ServerBackup", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Bytes") + .HasColumnType("bigint"); + + b.Property("Created") + .HasColumnType("tinyint(1)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ServerId") + .HasColumnType("int"); + + b.Property("Uuid") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("ServerId"); + + b.ToTable("ServerBackups"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.ServerVariable", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + 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"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.SharedDomain", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("CloudflareId") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("SharedDomains"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Subscription", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Description") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Duration") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("SellPassId") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("Subscriptions"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.SubscriptionLimit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Amount") + .HasColumnType("int"); + + b.Property("Cpu") + .HasColumnType("int"); + + b.Property("Disk") + .HasColumnType("int"); + + b.Property("ImageId") + .HasColumnType("int"); + + b.Property("Memory") + .HasColumnType("int"); + + b.Property("SubscriptionId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("ImageId"); + + b.HasIndex("SubscriptionId"); + + b.ToTable("SubscriptionLimits"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.SupportMessage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Answer") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsQuestion") + .HasColumnType("tinyint(1)"); + + b.Property("IsSupport") + .HasColumnType("tinyint(1)"); + + b.Property("IsSystem") + .HasColumnType("tinyint(1)"); + + b.Property("Message") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("RecipientId") + .HasColumnType("int"); + + b.Property("SenderId") + .HasColumnType("int"); + + b.Property("Type") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("RecipientId"); + + b.HasIndex("SenderId"); + + b.ToTable("SupportMessages"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Address") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Admin") + .HasColumnType("tinyint(1)"); + + b.Property("City") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Country") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("DiscordDiscriminator") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("DiscordId") + .HasColumnType("bigint"); + + b.Property("DiscordUsername") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Email") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("FirstName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("LastName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Password") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("State") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("SubscriptionDuration") + .HasColumnType("int"); + + b.Property("SubscriptionId") + .HasColumnType("int"); + + b.Property("SubscriptionSince") + .HasColumnType("datetime(6)"); + + b.Property("SupportPending") + .HasColumnType("tinyint(1)"); + + b.Property("TokenValidTime") + .HasColumnType("datetime(6)"); + + b.Property("TotpEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("TotpSecret") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("SubscriptionId"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Website", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("AaPanelId") + .HasColumnType("int"); + + b.Property("DomainName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("FtpPassword") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("FtpUsername") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("InternalAaPanelId") + .HasColumnType("int"); + + b.Property("OwnerId") + .HasColumnType("int"); + + b.Property("PhpVersion") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("AaPanelId"); + + b.HasIndex("OwnerId"); + + b.ToTable("Websites"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Database", b => + { + b.HasOne("Moonlight.App.Database.Entities.AaPanel", "AaPanel") + .WithMany() + .HasForeignKey("AaPanelId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Moonlight.App.Database.Entities.User", "Owner") + .WithMany() + .HasForeignKey("OwnerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("AaPanel"); + + b.Navigation("Owner"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.DockerImage", b => + { + b.HasOne("Moonlight.App.Database.Entities.Image", null) + .WithMany("DockerImages") + .HasForeignKey("ImageId"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Domain", b => + { + b.HasOne("Moonlight.App.Database.Entities.User", "Owner") + .WithMany() + .HasForeignKey("OwnerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Moonlight.App.Database.Entities.SharedDomain", "SharedDomain") + .WithMany() + .HasForeignKey("SharedDomainId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Owner"); + + b.Navigation("SharedDomain"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.ImageVariable", b => + { + b.HasOne("Moonlight.App.Database.Entities.Image", null) + .WithMany("Variables") + .HasForeignKey("ImageId"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.NodeAllocation", b => + { + b.HasOne("Moonlight.App.Database.Entities.Node", null) + .WithMany("Allocations") + .HasForeignKey("NodeId"); + + b.HasOne("Moonlight.App.Database.Entities.Server", null) + .WithMany("Allocations") + .HasForeignKey("ServerId"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Notification.NotificationAction", b => + { + b.HasOne("Moonlight.App.Database.Entities.Notification.NotificationClient", "NotificationClient") + .WithMany() + .HasForeignKey("NotificationClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("NotificationClient"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Notification.NotificationClient", b => + { + b.HasOne("Moonlight.App.Database.Entities.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Server", b => + { + b.HasOne("Moonlight.App.Database.Entities.Image", "Image") + .WithMany() + .HasForeignKey("ImageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Moonlight.App.Database.Entities.NodeAllocation", "MainAllocation") + .WithMany() + .HasForeignKey("MainAllocationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Moonlight.App.Database.Entities.Node", "Node") + .WithMany() + .HasForeignKey("NodeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Moonlight.App.Database.Entities.User", "Owner") + .WithMany() + .HasForeignKey("OwnerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Image"); + + b.Navigation("MainAllocation"); + + b.Navigation("Node"); + + b.Navigation("Owner"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.ServerBackup", b => + { + b.HasOne("Moonlight.App.Database.Entities.Server", null) + .WithMany("Backups") + .HasForeignKey("ServerId"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.ServerVariable", b => + { + b.HasOne("Moonlight.App.Database.Entities.Server", null) + .WithMany("Variables") + .HasForeignKey("ServerId"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.SubscriptionLimit", b => + { + b.HasOne("Moonlight.App.Database.Entities.Image", "Image") + .WithMany() + .HasForeignKey("ImageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Moonlight.App.Database.Entities.Subscription", null) + .WithMany("Limits") + .HasForeignKey("SubscriptionId"); + + b.Navigation("Image"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.SupportMessage", b => + { + b.HasOne("Moonlight.App.Database.Entities.User", "Recipient") + .WithMany() + .HasForeignKey("RecipientId"); + + b.HasOne("Moonlight.App.Database.Entities.User", "Sender") + .WithMany() + .HasForeignKey("SenderId"); + + b.Navigation("Recipient"); + + b.Navigation("Sender"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.User", b => + { + b.HasOne("Moonlight.App.Database.Entities.Subscription", "Subscription") + .WithMany() + .HasForeignKey("SubscriptionId"); + + b.Navigation("Subscription"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Website", b => + { + b.HasOne("Moonlight.App.Database.Entities.AaPanel", "AaPanel") + .WithMany() + .HasForeignKey("AaPanelId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Moonlight.App.Database.Entities.User", "Owner") + .WithMany() + .HasForeignKey("OwnerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("AaPanel"); + + b.Navigation("Owner"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Image", b => + { + b.Navigation("DockerImages"); + + b.Navigation("Variables"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Node", b => + { + b.Navigation("Allocations"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Server", b => + { + b.Navigation("Allocations"); + + b.Navigation("Backups"); + + b.Navigation("Variables"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Subscription", b => + { + b.Navigation("Limits"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Moonlight/App/Database/Migrations/20230314164750_SwitchedToJsonTags.cs b/Moonlight/App/Database/Migrations/20230314164750_SwitchedToJsonTags.cs new file mode 100644 index 00000000..916cdae4 --- /dev/null +++ b/Moonlight/App/Database/Migrations/20230314164750_SwitchedToJsonTags.cs @@ -0,0 +1,174 @@ +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Moonlight.App.Database.Migrations +{ + /// + public partial class SwitchedToJsonTags : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_ImageTags_Images_ImageId", + table: "ImageTags"); + + migrationBuilder.DropIndex( + name: "IX_ImageTags_ImageId", + table: "ImageTags"); + + migrationBuilder.DropColumn( + name: "ImageId", + table: "ImageTags"); + + migrationBuilder.AddColumn( + name: "TagsJson", + table: "Images", + type: "longtext", + nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.AddColumn( + name: "InternalAaPanelId", + table: "Databases", + type: "int", + nullable: false, + defaultValue: 0); + + migrationBuilder.AddColumn( + name: "Name", + table: "Databases", + type: "longtext", + nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "AaPanels", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + Url = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Key = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + BaseDomain = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK_AaPanels", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "Websites", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + InternalAaPanelId = table.Column(type: "int", nullable: false), + AaPanelId = table.Column(type: "int", nullable: false), + OwnerId = table.Column(type: "int", nullable: false), + DomainName = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + PhpVersion = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + FtpUsername = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + FtpPassword = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK_Websites", x => x.Id); + table.ForeignKey( + name: "FK_Websites_AaPanels_AaPanelId", + column: x => x.AaPanelId, + principalTable: "AaPanels", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_Websites_Users_OwnerId", + column: x => x.OwnerId, + principalTable: "Users", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateIndex( + name: "IX_Databases_AaPanelId", + table: "Databases", + column: "AaPanelId"); + + migrationBuilder.CreateIndex( + name: "IX_Websites_AaPanelId", + table: "Websites", + column: "AaPanelId"); + + migrationBuilder.CreateIndex( + name: "IX_Websites_OwnerId", + table: "Websites", + column: "OwnerId"); + + migrationBuilder.AddForeignKey( + name: "FK_Databases_AaPanels_AaPanelId", + table: "Databases", + column: "AaPanelId", + principalTable: "AaPanels", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_Databases_AaPanels_AaPanelId", + table: "Databases"); + + migrationBuilder.DropTable( + name: "Websites"); + + migrationBuilder.DropTable( + name: "AaPanels"); + + migrationBuilder.DropIndex( + name: "IX_Databases_AaPanelId", + table: "Databases"); + + migrationBuilder.DropColumn( + name: "TagsJson", + table: "Images"); + + migrationBuilder.DropColumn( + name: "InternalAaPanelId", + table: "Databases"); + + migrationBuilder.DropColumn( + name: "Name", + table: "Databases"); + + migrationBuilder.AddColumn( + name: "ImageId", + table: "ImageTags", + type: "int", + nullable: true); + + migrationBuilder.CreateIndex( + name: "IX_ImageTags_ImageId", + table: "ImageTags", + column: "ImageId"); + + migrationBuilder.AddForeignKey( + name: "FK_ImageTags_Images_ImageId", + table: "ImageTags", + column: "ImageId", + principalTable: "Images", + principalColumn: "Id"); + } + } +} diff --git a/Moonlight/App/Database/Migrations/20230314190100_AddedAllocationsToImage.Designer.cs b/Moonlight/App/Database/Migrations/20230314190100_AddedAllocationsToImage.Designer.cs new file mode 100644 index 00000000..38247d55 --- /dev/null +++ b/Moonlight/App/Database/Migrations/20230314190100_AddedAllocationsToImage.Designer.cs @@ -0,0 +1,1026 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Moonlight.App.Database; + +#nullable disable + +namespace Moonlight.App.Database.Migrations +{ + [DbContext(typeof(DataContext))] + [Migration("20230314190100_AddedAllocationsToImage")] + partial class AddedAllocationsToImage + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "7.0.3") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + modelBuilder.Entity("Moonlight.App.Database.Entities.AaPanel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("BaseDomain") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Key") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Url") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("AaPanels"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Database", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("AaPanelId") + .HasColumnType("int"); + + b.Property("InternalAaPanelId") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("OwnerId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("AaPanelId"); + + b.HasIndex("OwnerId"); + + b.ToTable("Databases"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.DockerImage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Default") + .HasColumnType("tinyint(1)"); + + b.Property("ImageId") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("ImageId"); + + b.ToTable("DockerImages"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Domain", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("OwnerId") + .HasColumnType("int"); + + b.Property("SharedDomainId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("OwnerId"); + + b.HasIndex("SharedDomainId"); + + b.ToTable("Domains"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Image", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Allocations") + .HasColumnType("int"); + + b.Property("ConfigFiles") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Description") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("InstallDockerImage") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("InstallEntrypoint") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("InstallScript") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Startup") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("StartupDetection") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("StopCommand") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("TagsJson") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Uuid") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.ToTable("Images"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.ImageTag", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("ImageTags"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.ImageVariable", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("DefaultValue") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ImageId") + .HasColumnType("int"); + + b.Property("Key") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("ImageId"); + + b.ToTable("ImageVariables"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.LoadingMessage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Message") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("LoadingMessages"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.LogsEntries.AuditLogEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Ip") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("JsonData") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("System") + .HasColumnType("tinyint(1)"); + + b.Property("Type") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("AuditLog"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.LogsEntries.ErrorLogEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Class") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Ip") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("JsonData") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Stacktrace") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("System") + .HasColumnType("tinyint(1)"); + + b.HasKey("Id"); + + b.ToTable("ErrorLog"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.LogsEntries.SecurityLogEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Ip") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("JsonData") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("System") + .HasColumnType("tinyint(1)"); + + b.Property("Type") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("SecurityLog"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Node", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Fqdn") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("HttpPort") + .HasColumnType("int"); + + b.Property("MoonlightDaemonPort") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("SftpPort") + .HasColumnType("int"); + + b.Property("Ssl") + .HasColumnType("tinyint(1)"); + + b.Property("Token") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("TokenId") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("Nodes"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.NodeAllocation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + 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("NodeAllocations"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Notification.NotificationAction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Action") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("NotificationClientId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("NotificationClientId"); + + b.ToTable("NotificationActions"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Notification.NotificationClient", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("NotificationClients"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Revoke", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Identifier") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("Revokes"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Server", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Cpu") + .HasColumnType("int"); + + b.Property("Disk") + .HasColumnType("bigint"); + + b.Property("DockerImageIndex") + .HasColumnType("int"); + + b.Property("ImageId") + .HasColumnType("int"); + + b.Property("Installing") + .HasColumnType("tinyint(1)"); + + b.Property("MainAllocationId") + .HasColumnType("int"); + + b.Property("Memory") + .HasColumnType("bigint"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("NodeId") + .HasColumnType("int"); + + b.Property("OverrideStartup") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("OwnerId") + .HasColumnType("int"); + + b.Property("Suspended") + .HasColumnType("tinyint(1)"); + + b.Property("Uuid") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("ImageId"); + + b.HasIndex("MainAllocationId"); + + b.HasIndex("NodeId"); + + b.HasIndex("OwnerId"); + + b.ToTable("Servers"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.ServerBackup", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Bytes") + .HasColumnType("bigint"); + + b.Property("Created") + .HasColumnType("tinyint(1)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ServerId") + .HasColumnType("int"); + + b.Property("Uuid") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("ServerId"); + + b.ToTable("ServerBackups"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.ServerVariable", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + 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"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.SharedDomain", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("CloudflareId") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("SharedDomains"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Subscription", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Description") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Duration") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("SellPassId") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("Subscriptions"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.SubscriptionLimit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Amount") + .HasColumnType("int"); + + b.Property("Cpu") + .HasColumnType("int"); + + b.Property("Disk") + .HasColumnType("int"); + + b.Property("ImageId") + .HasColumnType("int"); + + b.Property("Memory") + .HasColumnType("int"); + + b.Property("SubscriptionId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("ImageId"); + + b.HasIndex("SubscriptionId"); + + b.ToTable("SubscriptionLimits"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.SupportMessage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Answer") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsQuestion") + .HasColumnType("tinyint(1)"); + + b.Property("IsSupport") + .HasColumnType("tinyint(1)"); + + b.Property("IsSystem") + .HasColumnType("tinyint(1)"); + + b.Property("Message") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("RecipientId") + .HasColumnType("int"); + + b.Property("SenderId") + .HasColumnType("int"); + + b.Property("Type") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("RecipientId"); + + b.HasIndex("SenderId"); + + b.ToTable("SupportMessages"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Address") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Admin") + .HasColumnType("tinyint(1)"); + + b.Property("City") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Country") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("DiscordDiscriminator") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("DiscordId") + .HasColumnType("bigint"); + + b.Property("DiscordUsername") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Email") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("FirstName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("LastName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Password") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("State") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("SubscriptionDuration") + .HasColumnType("int"); + + b.Property("SubscriptionId") + .HasColumnType("int"); + + b.Property("SubscriptionSince") + .HasColumnType("datetime(6)"); + + b.Property("SupportPending") + .HasColumnType("tinyint(1)"); + + b.Property("TokenValidTime") + .HasColumnType("datetime(6)"); + + b.Property("TotpEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("TotpSecret") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("SubscriptionId"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Website", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("AaPanelId") + .HasColumnType("int"); + + b.Property("DomainName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("FtpPassword") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("FtpUsername") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("InternalAaPanelId") + .HasColumnType("int"); + + b.Property("OwnerId") + .HasColumnType("int"); + + b.Property("PhpVersion") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("AaPanelId"); + + b.HasIndex("OwnerId"); + + b.ToTable("Websites"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Database", b => + { + b.HasOne("Moonlight.App.Database.Entities.AaPanel", "AaPanel") + .WithMany() + .HasForeignKey("AaPanelId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Moonlight.App.Database.Entities.User", "Owner") + .WithMany() + .HasForeignKey("OwnerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("AaPanel"); + + b.Navigation("Owner"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.DockerImage", b => + { + b.HasOne("Moonlight.App.Database.Entities.Image", null) + .WithMany("DockerImages") + .HasForeignKey("ImageId"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Domain", b => + { + b.HasOne("Moonlight.App.Database.Entities.User", "Owner") + .WithMany() + .HasForeignKey("OwnerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Moonlight.App.Database.Entities.SharedDomain", "SharedDomain") + .WithMany() + .HasForeignKey("SharedDomainId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Owner"); + + b.Navigation("SharedDomain"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.ImageVariable", b => + { + b.HasOne("Moonlight.App.Database.Entities.Image", null) + .WithMany("Variables") + .HasForeignKey("ImageId"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.NodeAllocation", b => + { + b.HasOne("Moonlight.App.Database.Entities.Node", null) + .WithMany("Allocations") + .HasForeignKey("NodeId"); + + b.HasOne("Moonlight.App.Database.Entities.Server", null) + .WithMany("Allocations") + .HasForeignKey("ServerId"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Notification.NotificationAction", b => + { + b.HasOne("Moonlight.App.Database.Entities.Notification.NotificationClient", "NotificationClient") + .WithMany() + .HasForeignKey("NotificationClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("NotificationClient"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Notification.NotificationClient", b => + { + b.HasOne("Moonlight.App.Database.Entities.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Server", b => + { + b.HasOne("Moonlight.App.Database.Entities.Image", "Image") + .WithMany() + .HasForeignKey("ImageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Moonlight.App.Database.Entities.NodeAllocation", "MainAllocation") + .WithMany() + .HasForeignKey("MainAllocationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Moonlight.App.Database.Entities.Node", "Node") + .WithMany() + .HasForeignKey("NodeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Moonlight.App.Database.Entities.User", "Owner") + .WithMany() + .HasForeignKey("OwnerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Image"); + + b.Navigation("MainAllocation"); + + b.Navigation("Node"); + + b.Navigation("Owner"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.ServerBackup", b => + { + b.HasOne("Moonlight.App.Database.Entities.Server", null) + .WithMany("Backups") + .HasForeignKey("ServerId"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.ServerVariable", b => + { + b.HasOne("Moonlight.App.Database.Entities.Server", null) + .WithMany("Variables") + .HasForeignKey("ServerId"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.SubscriptionLimit", b => + { + b.HasOne("Moonlight.App.Database.Entities.Image", "Image") + .WithMany() + .HasForeignKey("ImageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Moonlight.App.Database.Entities.Subscription", null) + .WithMany("Limits") + .HasForeignKey("SubscriptionId"); + + b.Navigation("Image"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.SupportMessage", b => + { + b.HasOne("Moonlight.App.Database.Entities.User", "Recipient") + .WithMany() + .HasForeignKey("RecipientId"); + + b.HasOne("Moonlight.App.Database.Entities.User", "Sender") + .WithMany() + .HasForeignKey("SenderId"); + + b.Navigation("Recipient"); + + b.Navigation("Sender"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.User", b => + { + b.HasOne("Moonlight.App.Database.Entities.Subscription", "Subscription") + .WithMany() + .HasForeignKey("SubscriptionId"); + + b.Navigation("Subscription"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Website", b => + { + b.HasOne("Moonlight.App.Database.Entities.AaPanel", "AaPanel") + .WithMany() + .HasForeignKey("AaPanelId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Moonlight.App.Database.Entities.User", "Owner") + .WithMany() + .HasForeignKey("OwnerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("AaPanel"); + + b.Navigation("Owner"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Image", b => + { + b.Navigation("DockerImages"); + + b.Navigation("Variables"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Node", b => + { + b.Navigation("Allocations"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Server", b => + { + b.Navigation("Allocations"); + + b.Navigation("Backups"); + + b.Navigation("Variables"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Subscription", b => + { + b.Navigation("Limits"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Moonlight/App/Database/Migrations/20230314190100_AddedAllocationsToImage.cs b/Moonlight/App/Database/Migrations/20230314190100_AddedAllocationsToImage.cs new file mode 100644 index 00000000..15284fd1 --- /dev/null +++ b/Moonlight/App/Database/Migrations/20230314190100_AddedAllocationsToImage.cs @@ -0,0 +1,29 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Moonlight.App.Database.Migrations +{ + /// + public partial class AddedAllocationsToImage : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "Allocations", + table: "Images", + type: "int", + nullable: false, + defaultValue: 0); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "Allocations", + table: "Images"); + } + } +} diff --git a/Moonlight/App/Database/Migrations/DataContextModelSnapshot.cs b/Moonlight/App/Database/Migrations/DataContextModelSnapshot.cs index 335da5bd..1b34c852 100644 --- a/Moonlight/App/Database/Migrations/DataContextModelSnapshot.cs +++ b/Moonlight/App/Database/Migrations/DataContextModelSnapshot.cs @@ -19,6 +19,29 @@ namespace Moonlight.App.Database.Migrations .HasAnnotation("ProductVersion", "7.0.3") .HasAnnotation("Relational:MaxIdentifierLength", 64); + modelBuilder.Entity("Moonlight.App.Database.Entities.AaPanel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("BaseDomain") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Key") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Url") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("AaPanels"); + }); + modelBuilder.Entity("Moonlight.App.Database.Entities.Database", b => { b.Property("Id") @@ -28,11 +51,20 @@ namespace Moonlight.App.Database.Migrations b.Property("AaPanelId") .HasColumnType("int"); + b.Property("InternalAaPanelId") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + b.Property("OwnerId") .HasColumnType("int"); b.HasKey("Id"); + b.HasIndex("AaPanelId"); + b.HasIndex("OwnerId"); b.ToTable("Databases"); @@ -92,6 +124,9 @@ namespace Moonlight.App.Database.Migrations .ValueGeneratedOnAdd() .HasColumnType("int"); + b.Property("Allocations") + .HasColumnType("int"); + b.Property("ConfigFiles") .IsRequired() .HasColumnType("longtext"); @@ -128,6 +163,10 @@ namespace Moonlight.App.Database.Migrations .IsRequired() .HasColumnType("longtext"); + b.Property("TagsJson") + .IsRequired() + .HasColumnType("longtext"); + b.Property("Uuid") .HasColumnType("char(36)"); @@ -142,17 +181,12 @@ namespace Moonlight.App.Database.Migrations .ValueGeneratedOnAdd() .HasColumnType("int"); - b.Property("ImageId") - .HasColumnType("int"); - b.Property("Name") .IsRequired() .HasColumnType("longtext"); b.HasKey("Id"); - b.HasIndex("ImageId"); - b.ToTable("ImageTags"); }); @@ -726,14 +760,62 @@ namespace Moonlight.App.Database.Migrations b.ToTable("Users"); }); + modelBuilder.Entity("Moonlight.App.Database.Entities.Website", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("AaPanelId") + .HasColumnType("int"); + + b.Property("DomainName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("FtpPassword") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("FtpUsername") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("InternalAaPanelId") + .HasColumnType("int"); + + b.Property("OwnerId") + .HasColumnType("int"); + + b.Property("PhpVersion") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("AaPanelId"); + + b.HasIndex("OwnerId"); + + b.ToTable("Websites"); + }); + modelBuilder.Entity("Moonlight.App.Database.Entities.Database", b => { + b.HasOne("Moonlight.App.Database.Entities.AaPanel", "AaPanel") + .WithMany() + .HasForeignKey("AaPanelId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + b.HasOne("Moonlight.App.Database.Entities.User", "Owner") .WithMany() .HasForeignKey("OwnerId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); + b.Navigation("AaPanel"); + b.Navigation("Owner"); }); @@ -763,13 +845,6 @@ namespace Moonlight.App.Database.Migrations b.Navigation("SharedDomain"); }); - modelBuilder.Entity("Moonlight.App.Database.Entities.ImageTag", b => - { - b.HasOne("Moonlight.App.Database.Entities.Image", null) - .WithMany("Tags") - .HasForeignKey("ImageId"); - }); - modelBuilder.Entity("Moonlight.App.Database.Entities.ImageVariable", b => { b.HasOne("Moonlight.App.Database.Entities.Image", null) @@ -898,12 +973,29 @@ namespace Moonlight.App.Database.Migrations b.Navigation("Subscription"); }); + modelBuilder.Entity("Moonlight.App.Database.Entities.Website", b => + { + b.HasOne("Moonlight.App.Database.Entities.AaPanel", "AaPanel") + .WithMany() + .HasForeignKey("AaPanelId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Moonlight.App.Database.Entities.User", "Owner") + .WithMany() + .HasForeignKey("OwnerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("AaPanel"); + + b.Navigation("Owner"); + }); + modelBuilder.Entity("Moonlight.App.Database.Entities.Image", b => { b.Navigation("DockerImages"); - b.Navigation("Tags"); - b.Navigation("Variables"); }); diff --git a/Moonlight/Program.cs b/Moonlight/Program.cs index 3e3d1a52..64635f14 100644 --- a/Moonlight/Program.cs +++ b/Moonlight/Program.cs @@ -152,7 +152,7 @@ namespace Moonlight var supportServerService = app.Services.GetRequiredService(); // Discord bot service - var discordBotService = app.Services.GetRequiredService(); + //var discordBotService = app.Services.GetRequiredService(); app.Run(); } diff --git a/Moonlight/Shared/Components/FileManagerPartials/FileEditor.razor b/Moonlight/Shared/Components/FileManagerPartials/FileEditor.razor index c70d3ad8..0a2888c1 100644 --- a/Moonlight/Shared/Components/FileManagerPartials/FileEditor.razor +++ b/Moonlight/Shared/Components/FileManagerPartials/FileEditor.razor @@ -8,19 +8,22 @@ -
-
- Back +
+ + Cancel + +
+ + @if (Image == null) + { +
+ No image with this id found +
+ } + else + { +
+
+
+
+ + +
+
+ + +
+
+
+
+
+ +
+ + +
+
+ @if (Tags.Any()) + { +
+ @foreach (var tag in Tags) + { + + } +
+ } + else + { +
+ No tags found +
+ } +
+
+
+
+
+
+
+ +
+ + +
+
+ @if (Image.DockerImages.Any()) + { +
+ @foreach (var imageDocker in Image.DockerImages) + { + + } +
+ } + else + { +
+ No docker images found +
+ } +
+
+
+
+
+
+ + +
+
+ + +
+
+
+
+
+
+
+ + +
+ +
+
+
+ + +
+
+
+
+ + +
+
+ +
+ +
+ +
+
+
+
+
+
+
+ + +
+
+
+
+
+
+ + +
+
+ + +
+
+
+
+
+
+
+ + + +
+ +
+ @if (Image!.Variables.Any()) + { +
+ @foreach (var variable in Image!.Variables) + { +
+ + + +
+ } +
+ } + else + { +
+ No variables found +
+ } +
+
+
+
+
+
+ + Cancel + + + +
+
+
+ } +
+
+ + +@code +{ + [Parameter] + public int Id { get; set; } + + private Image? Image; + + private List Tags; + private string AddTagName = ""; + + private DockerImage NewDockerImage = new(); + private ImageVariable ImageVariable = new(); + + private FileEditor Editor; + + private int DefaultImageIndex + { + get + { + var i = Image.DockerImages.FirstOrDefault(x => x.Default); + return i?.Id ?? -1; + } + set + { + foreach (var image in Image!.DockerImages) + { + image.Default = false; + } + + var i = Image.DockerImages.FirstOrDefault(x => x.Id == value); + + if (i != null) + i.Default = true; + } + } + + private LazyLoader LazyLoader; + + private Task Load(LazyLoader arg) + { + Image = ImageRepository + .Get() + .Include(x => x.Variables) + .Include(x => x.DockerImages) + .FirstOrDefault(x => x.Id == Id); + + if (Image != null) + { + Tags = new(); + + foreach (var tag in JsonConvert.DeserializeObject(Image.TagsJson) ?? Array.Empty()) + { + Tags.Add(tag); + } + + // Editor + } + + return Task.CompletedTask; + } + + private void AddTag() + { + Tags.Add(AddTagName); + } + + private void RemoveTag(string tag) + { + Tags.Remove(tag); + } + + private void AddDockerImage() + { + Image!.DockerImages.Add(NewDockerImage); + NewDockerImage = new(); + } + + private void RemoveDockerImage(DockerImage image) + { + Image!.DockerImages.Remove(image); + } + + private void AddVariable() + { + Image!.Variables.Add(ImageVariable); + ImageVariable = new(); + } + + private void RemoveVariable(ImageVariable variable) + { + Image!.Variables.Remove(variable); + } + + private async Task Save() + { + if (Image == null) + return; + + Image.TagsJson = JsonConvert.SerializeObject(Tags); + Image.InstallScript = await Editor.GetData(); + + ImageRepository.Update(Image); + + await ToastService.Success(SmartTranslateService.Translate("Successfully saved image")); + + await LazyLoader.Reload(); + } +} \ No newline at end of file diff --git a/Moonlight/Shared/Views/Admin/Servers/Images/Index.razor b/Moonlight/Shared/Views/Admin/Servers/Images/Index.razor new file mode 100644 index 00000000..48318c2f --- /dev/null +++ b/Moonlight/Shared/Views/Admin/Servers/Images/Index.razor @@ -0,0 +1,91 @@ +@page "/admin/servers/images" + +@using BlazorTable +@using Microsoft.EntityFrameworkCore +@using Moonlight.App.Database.Entities +@using Moonlight.App.Repositories +@using Moonlight.App.Services + +@inject ImageRepository ImageRepository +@inject SmartTranslateService SmartTranslateService + + +
+ +
+
+

+ + Images + +

+ +
+
+ @if (Images.Any()) + { +
+ + + + + + + + + + + + +
+
+ } + else + { +
+ No images found +
+ } +
+
+
+
+
+ +@code +{ + private Image[] Images; + private LazyLoader LazyLoader; + + private Task Load(LazyLoader arg) + { + Images = ImageRepository + .Get() + .Include(x => x.DockerImages) + .Include(x => x.Variables) + .ToArray(); + + return Task.CompletedTask; + } + + private async Task Delete(Image image) + { + ImageRepository.Delete(image); + await LazyLoader.Reload(); + } +} \ No newline at end of file diff --git a/Moonlight/Shared/Views/Admin/Servers/Images/New.razor b/Moonlight/Shared/Views/Admin/Servers/Images/New.razor new file mode 100644 index 00000000..64d918a4 --- /dev/null +++ b/Moonlight/Shared/Views/Admin/Servers/Images/New.razor @@ -0,0 +1,340 @@ +@page "/admin/servers/images/new" +@using Moonlight.App.Repositories +@using Moonlight.Shared.Components.FileManagerPartials +@using Moonlight.App.Database.Entities +@using Moonlight.App.Services +@using Moonlight.App.Services.Interop +@using Newtonsoft.Json + +@inject ImageRepository ImageRepository +@inject SmartTranslateService SmartTranslateService +@inject NavigationManager NavigationManager +@inject ToastService ToastService + + +
+ +
+
+
+
+ + +
+
+ + +
+
+
+
+
+ +
+ + +
+
+ @if (Tags.Any()) + { +
+ @foreach (var tag in Tags) + { + + } +
+ } + else + { +
+ No tags found +
+ } +
+
+
+
+
+
+
+ +
+ + +
+
+ @if (Image.DockerImages.Any()) + { +
+ @foreach (var imageDocker in Image.DockerImages) + { + + } +
+ } + else + { +
+ No docker images found +
+ } +
+
+
+
+
+
+ + +
+
+ + +
+
+
+
+
+
+
+ + +
+ +
+
+
+ + +
+
+
+
+ + +
+
+ +
+ +
+ +
+
+
+
+
+
+
+ + +
+
+
+
+
+
+ + +
+
+ + +
+
+
+
+
+
+
+ + + +
+ +
+ @if (Image!.Variables.Any()) + { +
+ @foreach (var variable in Image!.Variables) + { +
+ + + +
+ } +
+ } + else + { +
+ No variables found +
+ } +
+
+
+
+
+
+ + Cancel + + + +
+
+
+
+
+
+ +@code +{ + private Image Image = new() + { + Variables = new(), + DockerImages = new(), + InstallScript = "# install script here", + Name = "Name", + TagsJson = "[]", + Description = "Description", + Startup = "", + Uuid = Guid.NewGuid(), + ConfigFiles = "{}", + InstallEntrypoint = "ash", + StartupDetection = "Done", + StopCommand = "^C", + InstallDockerImage = "ghcr.io/pterodactyl/installers:alpine" + }; + + private List Tags; + private string AddTagName = ""; + + private DockerImage NewDockerImage = new(); + private ImageVariable ImageVariable = new(); + + private FileEditor Editor; + + private int DefaultImageIndex + { + get + { + var i = Image.DockerImages.FirstOrDefault(x => x.Default); + return i?.Id ?? -1; + } + set + { + foreach (var image in Image!.DockerImages) + { + image.Default = false; + } + + var i = Image.DockerImages.FirstOrDefault(x => x.Id == value); + + if (i != null) + i.Default = true; + } + } + + private LazyLoader LazyLoader; + + private Task Load(LazyLoader arg) + { + Tags = new(); + + return Task.CompletedTask; + } + + private void AddTag() + { + Tags.Add(AddTagName); + } + + private void RemoveTag(string tag) + { + Tags.Remove(tag); + } + + private void AddDockerImage() + { + Image!.DockerImages.Add(NewDockerImage); + NewDockerImage = new(); + } + + private void RemoveDockerImage(DockerImage image) + { + Image!.DockerImages.Remove(image); + } + + private void AddVariable() + { + Image!.Variables.Add(ImageVariable); + ImageVariable = new(); + } + + private void RemoveVariable(ImageVariable variable) + { + Image!.Variables.Remove(variable); + } + + private async Task Save() + { + if (Image == null) + return; + + Image.TagsJson = JsonConvert.SerializeObject(Tags); + Image.InstallScript = await Editor.GetData(); + + ImageRepository.Add(Image); + + await ToastService.Success(SmartTranslateService.Translate("Successfully added image")); + + NavigationManager.NavigateTo("/admin/servers/images"); + } +} \ No newline at end of file diff --git a/Moonlight/Shared/Views/Server/Index.razor b/Moonlight/Shared/Views/Server/Index.razor index e543650a..fbda4721 100644 --- a/Moonlight/Shared/Views/Server/Index.razor +++ b/Moonlight/Shared/Views/Server/Index.razor @@ -11,6 +11,7 @@ @using Moonlight.App.Services.Sessions @using Moonlight.Shared.Components.Xterm @using Moonlight.Shared.Components.ServerControl +@using Newtonsoft.Json @inject ImageRepository ImageRepository @inject ServerRepository ServerRepository @@ -190,18 +191,11 @@ { await lazyLoader.SetText("Requesting tags"); - var tags = new List(); var image = ImageRepository .Get() - .Include(x => x.Tags) .First(x => x.Id == CurrentServer.Image.Id); - foreach (var tag in image.Tags) - { - tags.Add(tag.Name); - } - - Tags = tags.ToArray(); + Tags = JsonConvert.DeserializeObject(image.TagsJson) ?? Array.Empty(); Image = image; await lazyLoader.SetText("Connecting to console"); diff --git a/Moonlight/resources/lang/de_de.lang b/Moonlight/resources/lang/de_de.lang index 71bcd98d..d53daac4 100644 --- a/Moonlight/resources/lang/de_de.lang +++ b/Moonlight/resources/lang/de_de.lang @@ -330,3 +330,26 @@ Wrong here?;Wrong here? A user with this email can not be found;A user with this email can not be found Passwort reset successfull. Check your mail;Passwort reset successfull. Check your mail Discord bot;Discord bot +New image;New image +Description;Description +Uuid;Uuid +Enter tag name;Enter tag name +Remove;Remove +No tags found;No tags found +Enter docker image name;Enter docker image name +Tags;Tags +Docker images;Docker images +Default image;Default image +Startup command;Startup command +Install container;Install container +Install entry;Install entry +Configuration files;Configuration files +Startup detection;Startup detection +Stop command;Stop command +Successfully saved image;Successfully saved image +No docker images found;No docker images found +Key;Key +Default value;Default value +Allocations;Allocations +No variables found;No variables found +Successfully added image;Successfully added image From 654f6bf3c1620ed2028066a0d051d6594457b831 Mon Sep 17 00:00:00 2001 From: Marcel Baumgartner Date: Tue, 14 Mar 2023 22:53:33 +0100 Subject: [PATCH 2/7] Update de_de.lang --- Moonlight/resources/lang/de_de.lang | 1 + 1 file changed, 1 insertion(+) diff --git a/Moonlight/resources/lang/de_de.lang b/Moonlight/resources/lang/de_de.lang index d53daac4..cd9e6a08 100644 --- a/Moonlight/resources/lang/de_de.lang +++ b/Moonlight/resources/lang/de_de.lang @@ -353,3 +353,4 @@ Default value;Default value Allocations;Allocations No variables found;No variables found Successfully added image;Successfully added image +Password change for;Password change for From 69a38347976f40d87eceb587a76f379768428a7c Mon Sep 17 00:00:00 2001 From: Marcel Baumgartner Date: Sat, 18 Mar 2023 23:59:36 +0100 Subject: [PATCH 3/7] Implemented moonlight report endpoints --- .../Controllers/Api/Remote/DdosController.cs | 41 +++++++++++++++++++ .../App/Http/Requests/Daemon/DdosStatus.cs | 8 ++++ 2 files changed, 49 insertions(+) create mode 100644 Moonlight/App/Http/Controllers/Api/Remote/DdosController.cs create mode 100644 Moonlight/App/Http/Requests/Daemon/DdosStatus.cs diff --git a/Moonlight/App/Http/Controllers/Api/Remote/DdosController.cs b/Moonlight/App/Http/Controllers/Api/Remote/DdosController.cs new file mode 100644 index 00000000..d50d926a --- /dev/null +++ b/Moonlight/App/Http/Controllers/Api/Remote/DdosController.cs @@ -0,0 +1,41 @@ +using Logging.Net; +using Microsoft.AspNetCore.Mvc; +using Moonlight.App.Http.Requests.Daemon; +using Moonlight.App.Repositories; +using Moonlight.App.Services; + +namespace Moonlight.App.Http.Controllers.Api.Remote; + +[ApiController] +[Route("api/remote/ddos")] +public class DdosController : Controller +{ + private readonly NodeRepository NodeRepository; + private readonly MessageService MessageService; + + public DdosController(NodeRepository nodeRepository, MessageService messageService) + { + NodeRepository = nodeRepository; + MessageService = messageService; + } + + [HttpPost("update")] + public async Task Update([FromBody] DdosStatus ddosStatus) + { + var tokenData = Request.Headers.Authorization.ToString().Replace("Bearer ", ""); + var id = tokenData.Split(".")[0]; + var token = tokenData.Split(".")[1]; + + var node = NodeRepository.Get().FirstOrDefault(x => x.TokenId == id); + + if (node == null) + return NotFound(); + + if (token != node.Token) + return Unauthorized(); + + await MessageService.Emit("node.ddos", ddosStatus); + + return Ok(); + } +} \ No newline at end of file diff --git a/Moonlight/App/Http/Requests/Daemon/DdosStatus.cs b/Moonlight/App/Http/Requests/Daemon/DdosStatus.cs new file mode 100644 index 00000000..b08a5762 --- /dev/null +++ b/Moonlight/App/Http/Requests/Daemon/DdosStatus.cs @@ -0,0 +1,8 @@ +namespace Moonlight.App.Http.Requests.Daemon; + +public class DdosStatus +{ + public bool Ongoing { get; set; } + public long Data { get; set; } + public string Ip { get; set; } = ""; +} \ No newline at end of file From d45de8d8702e2704efe256b2ed51426400c40b56 Mon Sep 17 00:00:00 2001 From: Marcel Baumgartner Date: Mon, 20 Mar 2023 15:55:44 +0100 Subject: [PATCH 4/7] Added ml daemon communication and node stats --- Moonlight/App/Helpers/DaemonApiHelper.cs | 53 +++ Moonlight/App/Helpers/Formatter.cs | 21 ++ .../Models/Daemon/Resources/ContainerStats.cs | 15 + .../App/Models/Daemon/Resources/CpuStats.cs | 8 + .../App/Models/Daemon/Resources/DiskStats.cs | 9 + .../Models/Daemon/Resources/MemoryStats.cs | 15 + Moonlight/App/Models/Node/CpuStats.cs | 6 - Moonlight/App/Models/Node/DiskStats.cs | 6 - Moonlight/App/Models/Node/DockerStats.cs | 17 - Moonlight/App/Models/Node/MemoryStats.cs | 8 - Moonlight/App/Services/NodeService.cs | 27 +- Moonlight/Moonlight.csproj | 2 +- Moonlight/Program.cs | 1 + .../Shared/Views/Admin/Nodes/Index.razor | 234 ++++-------- Moonlight/Shared/Views/Admin/Nodes/View.razor | 357 ++++++++++++++++++ .../Shared/Views/Admin/Servers/Index.razor | 6 +- Moonlight/resources/lang/de_de.lang | 12 + 17 files changed, 590 insertions(+), 207 deletions(-) create mode 100644 Moonlight/App/Helpers/DaemonApiHelper.cs create mode 100644 Moonlight/App/Models/Daemon/Resources/ContainerStats.cs create mode 100644 Moonlight/App/Models/Daemon/Resources/CpuStats.cs create mode 100644 Moonlight/App/Models/Daemon/Resources/DiskStats.cs create mode 100644 Moonlight/App/Models/Daemon/Resources/MemoryStats.cs delete mode 100644 Moonlight/App/Models/Node/CpuStats.cs delete mode 100644 Moonlight/App/Models/Node/DiskStats.cs delete mode 100644 Moonlight/App/Models/Node/DockerStats.cs delete mode 100644 Moonlight/App/Models/Node/MemoryStats.cs create mode 100644 Moonlight/Shared/Views/Admin/Nodes/View.razor diff --git a/Moonlight/App/Helpers/DaemonApiHelper.cs b/Moonlight/App/Helpers/DaemonApiHelper.cs new file mode 100644 index 00000000..45d91a29 --- /dev/null +++ b/Moonlight/App/Helpers/DaemonApiHelper.cs @@ -0,0 +1,53 @@ +using Moonlight.App.Database.Entities; +using Moonlight.App.Exceptions; +using Newtonsoft.Json; +using RestSharp; + +namespace Moonlight.App.Helpers; + +public class DaemonApiHelper +{ + private readonly RestClient Client; + + public DaemonApiHelper() + { + Client = new(); + } + + private string GetApiUrl(Node node) + { + if(node.Ssl) + return $"https://{node.Fqdn}:{node.MoonlightDaemonPort}/"; + else + return $"http://{node.Fqdn}:{node.MoonlightDaemonPort}/"; + //return $"https://{node.Fqdn}:{node.HttpPort}/"; + } + + public async Task Get(Node node, string resource) + { + RestRequest request = new(GetApiUrl(node) + resource); + + request.AddHeader("Content-Type", "application/json"); + request.AddHeader("Accept", "application/json"); + request.AddHeader("Authorization", node.Token); + + var response = await Client.GetAsync(request); + + if (!response.IsSuccessful) + { + if (response.StatusCode != 0) + { + throw new WingsException( + $"An error occured: ({response.StatusCode}) {response.Content}", + (int)response.StatusCode + ); + } + else + { + throw new Exception($"An internal error occured: {response.ErrorMessage}"); + } + } + + return JsonConvert.DeserializeObject(response.Content!)!; + } +} \ No newline at end of file diff --git a/Moonlight/App/Helpers/Formatter.cs b/Moonlight/App/Helpers/Formatter.cs index 1725df98..78fbfc55 100644 --- a/Moonlight/App/Helpers/Formatter.cs +++ b/Moonlight/App/Helpers/Formatter.cs @@ -88,4 +88,25 @@ public static class Formatter return $"{i2s(e.Day)}.{i2s(e.Month)}.{e.Year}"; } + + public static string FormatSize(double bytes) + { + var i = Math.Abs(bytes) / 1024D; + if (i < 1) + { + return bytes + " B"; + } + else if (i / 1024D < 1) + { + return i.Round(2) + " KB"; + } + else if (i / (1024D * 1024D) < 1) + { + return (i / 1024D).Round(2) + " MB"; + } + else + { + return (i / (1024D * 1024D)).Round(2) + " GB"; + } + } } \ No newline at end of file diff --git a/Moonlight/App/Models/Daemon/Resources/ContainerStats.cs b/Moonlight/App/Models/Daemon/Resources/ContainerStats.cs new file mode 100644 index 00000000..5c2e0576 --- /dev/null +++ b/Moonlight/App/Models/Daemon/Resources/ContainerStats.cs @@ -0,0 +1,15 @@ +namespace Moonlight.App.Models.Daemon.Resources; + +public class ContainerStats +{ + public List Containers { get; set; } = new(); + + public class Container + { + public Guid Name { get; set; } + public long Memory { get; set; } + public double Cpu { get; set; } + public long NetworkIn { get; set; } + public long NetworkOut { get; set; } + } +} \ No newline at end of file diff --git a/Moonlight/App/Models/Daemon/Resources/CpuStats.cs b/Moonlight/App/Models/Daemon/Resources/CpuStats.cs new file mode 100644 index 00000000..9282627e --- /dev/null +++ b/Moonlight/App/Models/Daemon/Resources/CpuStats.cs @@ -0,0 +1,8 @@ +namespace Moonlight.App.Models.Daemon.Resources; + +public class CpuStats +{ + public double Usage { get; set; } + public int Cores { get; set; } + public string Model { get; set; } = ""; +} \ No newline at end of file diff --git a/Moonlight/App/Models/Daemon/Resources/DiskStats.cs b/Moonlight/App/Models/Daemon/Resources/DiskStats.cs new file mode 100644 index 00000000..ea19ae90 --- /dev/null +++ b/Moonlight/App/Models/Daemon/Resources/DiskStats.cs @@ -0,0 +1,9 @@ +namespace Moonlight.App.Models.Daemon.Resources; + +public class DiskStats +{ + public long FreeBytes { get; set; } + public string DriveFormat { get; set; } + public string Name { get; set; } + public long TotalSize { get; set; } +} \ No newline at end of file diff --git a/Moonlight/App/Models/Daemon/Resources/MemoryStats.cs b/Moonlight/App/Models/Daemon/Resources/MemoryStats.cs new file mode 100644 index 00000000..26823099 --- /dev/null +++ b/Moonlight/App/Models/Daemon/Resources/MemoryStats.cs @@ -0,0 +1,15 @@ +namespace Moonlight.App.Models.Daemon.Resources; + +public class MemoryStats +{ + public List Sticks { get; set; } = new(); + public double Free { get; set; } + public double Used { get; set; } + public double Total { get; set; } + + public class MemoryStick + { + public int Size { get; set; } + public string Type { get; set; } = ""; + } +} \ No newline at end of file diff --git a/Moonlight/App/Models/Node/CpuStats.cs b/Moonlight/App/Models/Node/CpuStats.cs deleted file mode 100644 index 5a12f6ef..00000000 --- a/Moonlight/App/Models/Node/CpuStats.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Moonlight.App.Models.Node; - -public class CpuStats -{ - public double CpuUsage { get; set; } -} \ No newline at end of file diff --git a/Moonlight/App/Models/Node/DiskStats.cs b/Moonlight/App/Models/Node/DiskStats.cs deleted file mode 100644 index d562f98e..00000000 --- a/Moonlight/App/Models/Node/DiskStats.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Moonlight.App.Models.Node; - -public class DiskStats -{ - public long FreeBytes { get; set; } -} \ No newline at end of file diff --git a/Moonlight/App/Models/Node/DockerStats.cs b/Moonlight/App/Models/Node/DockerStats.cs deleted file mode 100644 index 791e4e0b..00000000 --- a/Moonlight/App/Models/Node/DockerStats.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace Moonlight.App.Models.Node; - -public class DockerStats -{ - public ContainerStats[] Containers { get; set; } - public int NodeId { get; set; } -} - -public class ContainerStats -{ - public Guid Name { get; set; } - public long Memory { get; set; } - public double Cpu { get; set; } - public long NetworkIn { get; set; } - public long NetworkOut { get; set; } - public int NodeId { get; set; } -} \ No newline at end of file diff --git a/Moonlight/App/Models/Node/MemoryStats.cs b/Moonlight/App/Models/Node/MemoryStats.cs deleted file mode 100644 index 335c9fe4..00000000 --- a/Moonlight/App/Models/Node/MemoryStats.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Moonlight.App.Models.Node; - -public class MemoryStats -{ - public long Free { get; set; } - public long Used { get; set; } - public long Total { get; set; } -} \ No newline at end of file diff --git a/Moonlight/App/Services/NodeService.cs b/Moonlight/App/Services/NodeService.cs index 4a0afbaa..577214ef 100644 --- a/Moonlight/App/Services/NodeService.cs +++ b/Moonlight/App/Services/NodeService.cs @@ -1,5 +1,6 @@ using Moonlight.App.Database.Entities; using Moonlight.App.Helpers; +using Moonlight.App.Models.Daemon.Resources; using Moonlight.App.Models.Wings.Resources; using Moonlight.App.Repositories; @@ -7,17 +8,37 @@ namespace Moonlight.App.Services; public class NodeService { - private readonly NodeRepository NodeRepository; private readonly WingsApiHelper WingsApiHelper; + private readonly DaemonApiHelper DaemonApiHelper; - public NodeService(NodeRepository nodeRepository, WingsApiHelper wingsApiHelper) + public NodeService(WingsApiHelper wingsApiHelper, DaemonApiHelper daemonApiHelper) { - NodeRepository = nodeRepository; WingsApiHelper = wingsApiHelper; + DaemonApiHelper = daemonApiHelper; } public async Task GetStatus(Node node) { return await WingsApiHelper.Get(node, "api/system"); } + + public async Task GetCpuStats(Node node) + { + return await DaemonApiHelper.Get(node, "stats/cpu"); + } + + public async Task GetMemoryStats(Node node) + { + return await DaemonApiHelper.Get(node, "stats/memory"); + } + + public async Task GetDiskStats(Node node) + { + return await DaemonApiHelper.Get(node, "stats/disk"); + } + + public async Task GetContainerStats(Node node) + { + return await DaemonApiHelper.Get(node, "stats/container"); + } } \ No newline at end of file diff --git a/Moonlight/Moonlight.csproj b/Moonlight/Moonlight.csproj index 57522d5a..fa894e5e 100644 --- a/Moonlight/Moonlight.csproj +++ b/Moonlight/Moonlight.csproj @@ -62,7 +62,7 @@ - + diff --git a/Moonlight/Program.cs b/Moonlight/Program.cs index 64635f14..c43cde53 100644 --- a/Moonlight/Program.cs +++ b/Moonlight/Program.cs @@ -118,6 +118,7 @@ namespace Moonlight builder.Services.AddScoped(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); + builder.Services.AddScoped(); // Background services builder.Services.AddSingleton(); diff --git a/Moonlight/Shared/Views/Admin/Nodes/Index.razor b/Moonlight/Shared/Views/Admin/Nodes/Index.razor index 4b168c4a..e9394476 100644 --- a/Moonlight/Shared/Views/Admin/Nodes/Index.razor +++ b/Moonlight/Shared/Views/Admin/Nodes/Index.razor @@ -1,12 +1,11 @@ @page "/admin/nodes" @using Moonlight.App.Repositories @using Moonlight.App.Database.Entities -@using Moonlight.App.Helpers -@using Moonlight.App.Models.Node @using Moonlight.App.Models.Wings.Resources @using Moonlight.App.Services @using Moonlight.App.Services.Interop @using Logging.Net +@using BlazorTable @inject NodeRepository NodeRepository @inject AlertService AlertService @@ -14,168 +13,85 @@ @inject SmartTranslateService SmartTranslateService -
- @if (!Nodes.Any()) - { -
-
- No nodes found. Start with adding a new node +
+
+

+ + Nodes + +

+
- } - else - { - foreach (var node in Nodes) - { -
-
-
-

- @(node.Name) - (ID: @(node.Id)) -

-
-
-
-
-
- - Status - -
-
-
- - @{ - var ss = StatusCache.ContainsKey(node) ? StatusCache[node] : null; - } +
+ @if (Nodes.Any()) + { +
+ + + + + + + + + + + + + + + + + + + +
-
- } - } + } + else + { +
+ No nodes found +
+ } +
+
@@ -186,17 +102,10 @@ private LazyLoader LazyLoader; - private Dictionary CpuCache = new(); - private Dictionary MemoryCache = new(); - private Dictionary DiskCache = new(); private Dictionary StatusCache = new(); private Task Load(LazyLoader lazyLoader) { - CpuCache.Clear(); - MemoryCache.Clear(); - DiskCache.Clear(); - lock (StatusCache) { StatusCache.Clear(); @@ -222,11 +131,6 @@ catch (Exception e) { Logger.Debug(e.Message); - - lock (StatusCache) - { - StatusCache.Add(node, null); - } } await InvokeAsync(StateHasChanged); diff --git a/Moonlight/Shared/Views/Admin/Nodes/View.razor b/Moonlight/Shared/Views/Admin/Nodes/View.razor new file mode 100644 index 00000000..4b64b6dd --- /dev/null +++ b/Moonlight/Shared/Views/Admin/Nodes/View.razor @@ -0,0 +1,357 @@ +@page "/admin/nodes/view/{id:int}" + +@using Moonlight.App.Repositories +@using Moonlight.App.Database.Entities +@using Moonlight.App.Helpers +@using Moonlight.App.Models.Daemon.Resources +@using Moonlight.App.Models.Wings.Resources +@using Moonlight.App.Services + +@inject NodeRepository NodeRepository +@inject NodeService NodeService + + + +@if (Node == null) +{ +
+ No node with this id found +
+} +else +{ +
+
+
+
+

+ + @(Node.Name) details + +

+
+
+
+
+
+
+ + + +
+
+ + @if (CpuStats == null) + { + + Loading + + } + else + { + + @(CpuStats.Usage)% of @(CpuStats.Cores) Cores used + + } + + + @if (CpuStats == null) + { + + Loading + + } + else + { + @(CpuStats.Model) + } + +
+
+
+
+
+
+ + + +
+
+ + @if (MemoryStats == null) + { + + Loading + + } + else + { + + @(Formatter.FormatSize(MemoryStats.Used * 1024D * 1024D)) of @(Formatter.FormatSize(MemoryStats.Total * 1024D * 1024D)) used + + } + + + @if (MemoryStats == null) + { + + Loading + + } + else + { + if (MemoryStats.Sticks.Any()) + { + foreach (var stick in SortMemorySticks(MemoryStats.Sticks)) + { + @(stick) +
+ } + } + else + { + No memory sticks detected + } + } +
+
+
+
+
+
+
+ + + +
+
+ + @if (DiskStats == null) + { + + Loading + + } + else + { + + @(Formatter.FormatSize(DiskStats.TotalSize - DiskStats.FreeBytes)) of @(Formatter.FormatSize(DiskStats.TotalSize)) used + + } + + + @if (DiskStats == null) + { + + Loading + + } + else + { + @(DiskStats.Name) - @(DiskStats.DriveFormat) + } + +
+
+
+
+
+
+
+
+ + + +
+
+ + @if (SystemStatus == null) + { + + Loading + + } + else + { + + Online + + } + + + @if (SystemStatus == null) + { + + Loading + + } + else + { + @(SystemStatus.Version) + } + +
+
+
+
+
+
+ + + +
+
+ + @if (SystemStatus == null) + { + + Loading + + } + else + { + + @(SystemStatus.KernelVersion) - @(SystemStatus.Architecture) + + } + + + @if (SystemStatus == null) + { + + Loading + + } + else + { + Host system information + } + +
+
+
+
+
+
+ + + +
+
+ + @if (ContainerStats == null) + { + + Loading + + } + else + { + + @(ContainerStats.Containers.Count) + + } + + + @if (ContainerStats == null) + { + + Loading + + } + else + { + Docker containers running + } + +
+
+
+
+
+
+
+ +
+
+
+} +
+
+ +@code +{ + [Parameter] + public int Id { get; set; } + + private Node? Node; + + private CpuStats CpuStats; + private MemoryStats MemoryStats; + private DiskStats DiskStats; + private SystemStatus SystemStatus; + private ContainerStats ContainerStats; + + private async Task Load(LazyLoader arg) + { + Node = NodeRepository + .Get() + .FirstOrDefault(x => x.Id == Id); + + if (Node != null) + { + Task.Run(async () => + { + try + { + SystemStatus = await NodeService.GetStatus(Node); + await InvokeAsync(StateHasChanged); + + CpuStats = await NodeService.GetCpuStats(Node); + await InvokeAsync(StateHasChanged); + + MemoryStats = await NodeService.GetMemoryStats(Node); + await InvokeAsync(StateHasChanged); + + DiskStats = await NodeService.GetDiskStats(Node); + await InvokeAsync(StateHasChanged); + + ContainerStats = await NodeService.GetContainerStats(Node); + await InvokeAsync(StateHasChanged); + } + catch (Exception e) + { + // ignored + } + }); + } + } + + private List SortMemorySticks(List sticks) + { + // Thank you ChatGPT <3 + + var groupedMemory = sticks.GroupBy(memory => new { memory.Type, memory.Size }) + .Select(group => new + { + Type = group.Key.Type, + Size = group.Key.Size, + Count = group.Count() + }); + + var sortedMemory = groupedMemory.OrderBy(memory => memory.Type) + .ThenBy(memory => memory.Size); + + List sortedList = sortedMemory.Select(memory => + { + string sizeString = $"{memory.Size}GB"; + return $"{memory.Count}x {memory.Type} {sizeString}"; + }).ToList(); + + return sortedList; + } +} \ No newline at end of file diff --git a/Moonlight/Shared/Views/Admin/Servers/Index.razor b/Moonlight/Shared/Views/Admin/Servers/Index.razor index f60b9b14..c4da20ad 100644 --- a/Moonlight/Shared/Views/Admin/Servers/Index.razor +++ b/Moonlight/Shared/Views/Admin/Servers/Index.razor @@ -29,7 +29,11 @@
- + + + diff --git a/Moonlight/resources/lang/de_de.lang b/Moonlight/resources/lang/de_de.lang index cd9e6a08..8721e8b1 100644 --- a/Moonlight/resources/lang/de_de.lang +++ b/Moonlight/resources/lang/de_de.lang @@ -354,3 +354,15 @@ Allocations;Allocations No variables found;No variables found Successfully added image;Successfully added image Password change for;Password change for +of;of +New node;New node +Fqdn;Fqdn +Cores used;Cores used +used;used +5.15.90.1-microsoft-standard-WSL2 - amd64;5.15.90.1-microsoft-standard-WSL2 - amd64 +Host system information;Host system information +0;0 +Docker containers running;Docker containers running +details;details +1;1 +2;2 From 7dc4e1675489bf60f7b461e6a67ad3f460fe256d Mon Sep 17 00:00:00 2001 From: Marcel Baumgartner Date: Mon, 20 Mar 2023 17:38:59 +0100 Subject: [PATCH 5/7] Implemented frontend ddos detection view. Added missing translations --- Moonlight/App/Database/DataContext.cs | 1 + Moonlight/App/Database/Entities/DdosAttack.cs | 11 + ...0230320153817_AddedDdosAttacks.Designer.cs | 1066 +++++++++++++++++ .../20230320153817_AddedDdosAttacks.cs | 53 + .../Migrations/DataContextModelSnapshot.cs | 40 + .../Controllers/Api/Remote/DdosController.cs | 17 +- .../App/Repositories/DdosAttackRepository.cs | 39 + Moonlight/Program.cs | 1 + .../Navigations/AdminNodesNavigation.razor | 22 + .../Navigations/AdminSessionNavigation.razor | 4 +- Moonlight/Shared/Views/Admin/Nodes/Ddos.razor | 100 ++ .../Shared/Views/Admin/Nodes/Index.razor | 157 +-- Moonlight/resources/lang/de_de.lang | 8 + 13 files changed, 1437 insertions(+), 82 deletions(-) create mode 100644 Moonlight/App/Database/Entities/DdosAttack.cs create mode 100644 Moonlight/App/Database/Migrations/20230320153817_AddedDdosAttacks.Designer.cs create mode 100644 Moonlight/App/Database/Migrations/20230320153817_AddedDdosAttacks.cs create mode 100644 Moonlight/App/Repositories/DdosAttackRepository.cs create mode 100644 Moonlight/Shared/Components/Navigations/AdminNodesNavigation.razor create mode 100644 Moonlight/Shared/Views/Admin/Nodes/Ddos.razor diff --git a/Moonlight/App/Database/DataContext.cs b/Moonlight/App/Database/DataContext.cs index a303bdf5..c8575c49 100644 --- a/Moonlight/App/Database/DataContext.cs +++ b/Moonlight/App/Database/DataContext.cs @@ -41,6 +41,7 @@ public class DataContext : DbContext public DbSet NotificationActions { get; set; } public DbSet AaPanels { get; set; } public DbSet Websites { get; set; } + public DbSet DdosAttacks { get; set; } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { diff --git a/Moonlight/App/Database/Entities/DdosAttack.cs b/Moonlight/App/Database/Entities/DdosAttack.cs new file mode 100644 index 00000000..1b792e29 --- /dev/null +++ b/Moonlight/App/Database/Entities/DdosAttack.cs @@ -0,0 +1,11 @@ +namespace Moonlight.App.Database.Entities; + +public class DdosAttack +{ + public int Id { get; set; } + public bool Ongoing { get; set; } + public long Data { get; set; } + public string Ip { get; set; } = ""; + public Node Node { get; set; } = null!; + public DateTime CreatedAt { get; set; } = DateTime.UtcNow; +} \ No newline at end of file diff --git a/Moonlight/App/Database/Migrations/20230320153817_AddedDdosAttacks.Designer.cs b/Moonlight/App/Database/Migrations/20230320153817_AddedDdosAttacks.Designer.cs new file mode 100644 index 00000000..45a55a67 --- /dev/null +++ b/Moonlight/App/Database/Migrations/20230320153817_AddedDdosAttacks.Designer.cs @@ -0,0 +1,1066 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Moonlight.App.Database; + +#nullable disable + +namespace Moonlight.App.Database.Migrations +{ + [DbContext(typeof(DataContext))] + [Migration("20230320153817_AddedDdosAttacks")] + partial class AddedDdosAttacks + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "7.0.3") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + modelBuilder.Entity("Moonlight.App.Database.Entities.AaPanel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("BaseDomain") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Key") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Url") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("AaPanels"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Database", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("AaPanelId") + .HasColumnType("int"); + + b.Property("InternalAaPanelId") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("OwnerId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("AaPanelId"); + + b.HasIndex("OwnerId"); + + b.ToTable("Databases"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.DdosAttack", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Data") + .HasColumnType("bigint"); + + b.Property("Ip") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("NodeId") + .HasColumnType("int"); + + b.Property("Ongoing") + .HasColumnType("tinyint(1)"); + + b.HasKey("Id"); + + b.HasIndex("NodeId"); + + b.ToTable("DdosAttacks"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.DockerImage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Default") + .HasColumnType("tinyint(1)"); + + b.Property("ImageId") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("ImageId"); + + b.ToTable("DockerImages"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Domain", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("OwnerId") + .HasColumnType("int"); + + b.Property("SharedDomainId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("OwnerId"); + + b.HasIndex("SharedDomainId"); + + b.ToTable("Domains"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Image", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Allocations") + .HasColumnType("int"); + + b.Property("ConfigFiles") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Description") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("InstallDockerImage") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("InstallEntrypoint") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("InstallScript") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Startup") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("StartupDetection") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("StopCommand") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("TagsJson") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Uuid") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.ToTable("Images"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.ImageTag", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("ImageTags"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.ImageVariable", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("DefaultValue") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ImageId") + .HasColumnType("int"); + + b.Property("Key") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("ImageId"); + + b.ToTable("ImageVariables"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.LoadingMessage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Message") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("LoadingMessages"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.LogsEntries.AuditLogEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Ip") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("JsonData") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("System") + .HasColumnType("tinyint(1)"); + + b.Property("Type") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("AuditLog"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.LogsEntries.ErrorLogEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Class") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Ip") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("JsonData") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Stacktrace") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("System") + .HasColumnType("tinyint(1)"); + + b.HasKey("Id"); + + b.ToTable("ErrorLog"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.LogsEntries.SecurityLogEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Ip") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("JsonData") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("System") + .HasColumnType("tinyint(1)"); + + b.Property("Type") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("SecurityLog"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Node", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Fqdn") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("HttpPort") + .HasColumnType("int"); + + b.Property("MoonlightDaemonPort") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("SftpPort") + .HasColumnType("int"); + + b.Property("Ssl") + .HasColumnType("tinyint(1)"); + + b.Property("Token") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("TokenId") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("Nodes"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.NodeAllocation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + 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("NodeAllocations"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Notification.NotificationAction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Action") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("NotificationClientId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("NotificationClientId"); + + b.ToTable("NotificationActions"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Notification.NotificationClient", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("NotificationClients"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Revoke", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Identifier") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("Revokes"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Server", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Cpu") + .HasColumnType("int"); + + b.Property("Disk") + .HasColumnType("bigint"); + + b.Property("DockerImageIndex") + .HasColumnType("int"); + + b.Property("ImageId") + .HasColumnType("int"); + + b.Property("Installing") + .HasColumnType("tinyint(1)"); + + b.Property("MainAllocationId") + .HasColumnType("int"); + + b.Property("Memory") + .HasColumnType("bigint"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("NodeId") + .HasColumnType("int"); + + b.Property("OverrideStartup") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("OwnerId") + .HasColumnType("int"); + + b.Property("Suspended") + .HasColumnType("tinyint(1)"); + + b.Property("Uuid") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("ImageId"); + + b.HasIndex("MainAllocationId"); + + b.HasIndex("NodeId"); + + b.HasIndex("OwnerId"); + + b.ToTable("Servers"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.ServerBackup", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Bytes") + .HasColumnType("bigint"); + + b.Property("Created") + .HasColumnType("tinyint(1)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ServerId") + .HasColumnType("int"); + + b.Property("Uuid") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("ServerId"); + + b.ToTable("ServerBackups"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.ServerVariable", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + 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"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.SharedDomain", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("CloudflareId") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("SharedDomains"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Subscription", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Description") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Duration") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("SellPassId") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("Subscriptions"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.SubscriptionLimit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Amount") + .HasColumnType("int"); + + b.Property("Cpu") + .HasColumnType("int"); + + b.Property("Disk") + .HasColumnType("int"); + + b.Property("ImageId") + .HasColumnType("int"); + + b.Property("Memory") + .HasColumnType("int"); + + b.Property("SubscriptionId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("ImageId"); + + b.HasIndex("SubscriptionId"); + + b.ToTable("SubscriptionLimits"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.SupportMessage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Answer") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsQuestion") + .HasColumnType("tinyint(1)"); + + b.Property("IsSupport") + .HasColumnType("tinyint(1)"); + + b.Property("IsSystem") + .HasColumnType("tinyint(1)"); + + b.Property("Message") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("RecipientId") + .HasColumnType("int"); + + b.Property("SenderId") + .HasColumnType("int"); + + b.Property("Type") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("RecipientId"); + + b.HasIndex("SenderId"); + + b.ToTable("SupportMessages"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Address") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Admin") + .HasColumnType("tinyint(1)"); + + b.Property("City") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Country") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("DiscordDiscriminator") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("DiscordId") + .HasColumnType("bigint"); + + b.Property("DiscordUsername") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Email") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("FirstName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("LastName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Password") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("State") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("SubscriptionDuration") + .HasColumnType("int"); + + b.Property("SubscriptionId") + .HasColumnType("int"); + + b.Property("SubscriptionSince") + .HasColumnType("datetime(6)"); + + b.Property("SupportPending") + .HasColumnType("tinyint(1)"); + + b.Property("TokenValidTime") + .HasColumnType("datetime(6)"); + + b.Property("TotpEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("TotpSecret") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("SubscriptionId"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Website", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("AaPanelId") + .HasColumnType("int"); + + b.Property("DomainName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("FtpPassword") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("FtpUsername") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("InternalAaPanelId") + .HasColumnType("int"); + + b.Property("OwnerId") + .HasColumnType("int"); + + b.Property("PhpVersion") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("AaPanelId"); + + b.HasIndex("OwnerId"); + + b.ToTable("Websites"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Database", b => + { + b.HasOne("Moonlight.App.Database.Entities.AaPanel", "AaPanel") + .WithMany() + .HasForeignKey("AaPanelId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Moonlight.App.Database.Entities.User", "Owner") + .WithMany() + .HasForeignKey("OwnerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("AaPanel"); + + b.Navigation("Owner"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.DdosAttack", b => + { + b.HasOne("Moonlight.App.Database.Entities.Node", "Node") + .WithMany() + .HasForeignKey("NodeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Node"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.DockerImage", b => + { + b.HasOne("Moonlight.App.Database.Entities.Image", null) + .WithMany("DockerImages") + .HasForeignKey("ImageId"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Domain", b => + { + b.HasOne("Moonlight.App.Database.Entities.User", "Owner") + .WithMany() + .HasForeignKey("OwnerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Moonlight.App.Database.Entities.SharedDomain", "SharedDomain") + .WithMany() + .HasForeignKey("SharedDomainId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Owner"); + + b.Navigation("SharedDomain"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.ImageVariable", b => + { + b.HasOne("Moonlight.App.Database.Entities.Image", null) + .WithMany("Variables") + .HasForeignKey("ImageId"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.NodeAllocation", b => + { + b.HasOne("Moonlight.App.Database.Entities.Node", null) + .WithMany("Allocations") + .HasForeignKey("NodeId"); + + b.HasOne("Moonlight.App.Database.Entities.Server", null) + .WithMany("Allocations") + .HasForeignKey("ServerId"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Notification.NotificationAction", b => + { + b.HasOne("Moonlight.App.Database.Entities.Notification.NotificationClient", "NotificationClient") + .WithMany() + .HasForeignKey("NotificationClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("NotificationClient"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Notification.NotificationClient", b => + { + b.HasOne("Moonlight.App.Database.Entities.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Server", b => + { + b.HasOne("Moonlight.App.Database.Entities.Image", "Image") + .WithMany() + .HasForeignKey("ImageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Moonlight.App.Database.Entities.NodeAllocation", "MainAllocation") + .WithMany() + .HasForeignKey("MainAllocationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Moonlight.App.Database.Entities.Node", "Node") + .WithMany() + .HasForeignKey("NodeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Moonlight.App.Database.Entities.User", "Owner") + .WithMany() + .HasForeignKey("OwnerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Image"); + + b.Navigation("MainAllocation"); + + b.Navigation("Node"); + + b.Navigation("Owner"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.ServerBackup", b => + { + b.HasOne("Moonlight.App.Database.Entities.Server", null) + .WithMany("Backups") + .HasForeignKey("ServerId"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.ServerVariable", b => + { + b.HasOne("Moonlight.App.Database.Entities.Server", null) + .WithMany("Variables") + .HasForeignKey("ServerId"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.SubscriptionLimit", b => + { + b.HasOne("Moonlight.App.Database.Entities.Image", "Image") + .WithMany() + .HasForeignKey("ImageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Moonlight.App.Database.Entities.Subscription", null) + .WithMany("Limits") + .HasForeignKey("SubscriptionId"); + + b.Navigation("Image"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.SupportMessage", b => + { + b.HasOne("Moonlight.App.Database.Entities.User", "Recipient") + .WithMany() + .HasForeignKey("RecipientId"); + + b.HasOne("Moonlight.App.Database.Entities.User", "Sender") + .WithMany() + .HasForeignKey("SenderId"); + + b.Navigation("Recipient"); + + b.Navigation("Sender"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.User", b => + { + b.HasOne("Moonlight.App.Database.Entities.Subscription", "Subscription") + .WithMany() + .HasForeignKey("SubscriptionId"); + + b.Navigation("Subscription"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Website", b => + { + b.HasOne("Moonlight.App.Database.Entities.AaPanel", "AaPanel") + .WithMany() + .HasForeignKey("AaPanelId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Moonlight.App.Database.Entities.User", "Owner") + .WithMany() + .HasForeignKey("OwnerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("AaPanel"); + + b.Navigation("Owner"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Image", b => + { + b.Navigation("DockerImages"); + + b.Navigation("Variables"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Node", b => + { + b.Navigation("Allocations"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Server", b => + { + b.Navigation("Allocations"); + + b.Navigation("Backups"); + + b.Navigation("Variables"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Subscription", b => + { + b.Navigation("Limits"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Moonlight/App/Database/Migrations/20230320153817_AddedDdosAttacks.cs b/Moonlight/App/Database/Migrations/20230320153817_AddedDdosAttacks.cs new file mode 100644 index 00000000..77bb38a4 --- /dev/null +++ b/Moonlight/App/Database/Migrations/20230320153817_AddedDdosAttacks.cs @@ -0,0 +1,53 @@ +using System; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Moonlight.App.Database.Migrations +{ + /// + public partial class AddedDdosAttacks : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "DdosAttacks", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + Ongoing = table.Column(type: "tinyint(1)", nullable: false), + Data = table.Column(type: "bigint", nullable: false), + Ip = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + NodeId = table.Column(type: "int", nullable: false), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_DdosAttacks", x => x.Id); + table.ForeignKey( + name: "FK_DdosAttacks_Nodes_NodeId", + column: x => x.NodeId, + principalTable: "Nodes", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateIndex( + name: "IX_DdosAttacks_NodeId", + table: "DdosAttacks", + column: "NodeId"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "DdosAttacks"); + } + } +} diff --git a/Moonlight/App/Database/Migrations/DataContextModelSnapshot.cs b/Moonlight/App/Database/Migrations/DataContextModelSnapshot.cs index 1b34c852..85cc9205 100644 --- a/Moonlight/App/Database/Migrations/DataContextModelSnapshot.cs +++ b/Moonlight/App/Database/Migrations/DataContextModelSnapshot.cs @@ -70,6 +70,35 @@ namespace Moonlight.App.Database.Migrations b.ToTable("Databases"); }); + modelBuilder.Entity("Moonlight.App.Database.Entities.DdosAttack", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Data") + .HasColumnType("bigint"); + + b.Property("Ip") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("NodeId") + .HasColumnType("int"); + + b.Property("Ongoing") + .HasColumnType("tinyint(1)"); + + b.HasKey("Id"); + + b.HasIndex("NodeId"); + + b.ToTable("DdosAttacks"); + }); + modelBuilder.Entity("Moonlight.App.Database.Entities.DockerImage", b => { b.Property("Id") @@ -819,6 +848,17 @@ namespace Moonlight.App.Database.Migrations b.Navigation("Owner"); }); + modelBuilder.Entity("Moonlight.App.Database.Entities.DdosAttack", b => + { + b.HasOne("Moonlight.App.Database.Entities.Node", "Node") + .WithMany() + .HasForeignKey("NodeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Node"); + }); + modelBuilder.Entity("Moonlight.App.Database.Entities.DockerImage", b => { b.HasOne("Moonlight.App.Database.Entities.Image", null) diff --git a/Moonlight/App/Http/Controllers/Api/Remote/DdosController.cs b/Moonlight/App/Http/Controllers/Api/Remote/DdosController.cs index d50d926a..b1cf361c 100644 --- a/Moonlight/App/Http/Controllers/Api/Remote/DdosController.cs +++ b/Moonlight/App/Http/Controllers/Api/Remote/DdosController.cs @@ -1,5 +1,6 @@ using Logging.Net; using Microsoft.AspNetCore.Mvc; +using Moonlight.App.Database.Entities; using Moonlight.App.Http.Requests.Daemon; using Moonlight.App.Repositories; using Moonlight.App.Services; @@ -12,11 +13,13 @@ public class DdosController : Controller { private readonly NodeRepository NodeRepository; private readonly MessageService MessageService; + private readonly DdosAttackRepository DdosAttackRepository; - public DdosController(NodeRepository nodeRepository, MessageService messageService) + public DdosController(NodeRepository nodeRepository, MessageService messageService, DdosAttackRepository ddosAttackRepository) { NodeRepository = nodeRepository; MessageService = messageService; + DdosAttackRepository = ddosAttackRepository; } [HttpPost("update")] @@ -34,7 +37,17 @@ public class DdosController : Controller if (token != node.Token) return Unauthorized(); - await MessageService.Emit("node.ddos", ddosStatus); + var ddosAttack = new DdosAttack() + { + Ongoing = ddosStatus.Ongoing, + Data = ddosStatus.Data, + Ip = ddosStatus.Ip, + Node = node + }; + + ddosAttack = DdosAttackRepository.Add(ddosAttack); + + await MessageService.Emit("node.ddos", ddosAttack); return Ok(); } diff --git a/Moonlight/App/Repositories/DdosAttackRepository.cs b/Moonlight/App/Repositories/DdosAttackRepository.cs new file mode 100644 index 00000000..2e1138c4 --- /dev/null +++ b/Moonlight/App/Repositories/DdosAttackRepository.cs @@ -0,0 +1,39 @@ +using Microsoft.EntityFrameworkCore; +using Moonlight.App.Database; +using Moonlight.App.Database.Entities; + +namespace Moonlight.App.Repositories; + +public class DdosAttackRepository +{ + private readonly DataContext DataContext; + + public DdosAttackRepository(DataContext dataContext) + { + DataContext = dataContext; + } + + public DbSet Get() + { + return DataContext.DdosAttacks; + } + + public DdosAttack Add(DdosAttack ddosAttack) + { + var x = DataContext.DdosAttacks.Add(ddosAttack); + DataContext.SaveChanges(); + return x.Entity; + } + + public void Update(DdosAttack ddosAttack) + { + DataContext.DdosAttacks.Update(ddosAttack); + DataContext.SaveChanges(); + } + + public void Delete(DdosAttack ddosAttack) + { + DataContext.DdosAttacks.Remove(ddosAttack); + DataContext.SaveChanges(); + } +} \ No newline at end of file diff --git a/Moonlight/Program.cs b/Moonlight/Program.cs index c43cde53..45436097 100644 --- a/Moonlight/Program.cs +++ b/Moonlight/Program.cs @@ -64,6 +64,7 @@ namespace Moonlight builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); + builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); diff --git a/Moonlight/Shared/Components/Navigations/AdminNodesNavigation.razor b/Moonlight/Shared/Components/Navigations/AdminNodesNavigation.razor new file mode 100644 index 00000000..16db4eb5 --- /dev/null +++ b/Moonlight/Shared/Components/Navigations/AdminNodesNavigation.razor @@ -0,0 +1,22 @@ +
+
+ +
+
+ +@code +{ + [Parameter] + public int Index { get; set; } = 0; +} \ No newline at end of file diff --git a/Moonlight/Shared/Components/Navigations/AdminSessionNavigation.razor b/Moonlight/Shared/Components/Navigations/AdminSessionNavigation.razor index 58313635..c16843b8 100644 --- a/Moonlight/Shared/Components/Navigations/AdminSessionNavigation.razor +++ b/Moonlight/Shared/Components/Navigations/AdminSessionNavigation.razor @@ -3,12 +3,12 @@ diff --git a/Moonlight/Shared/Views/Admin/Nodes/Ddos.razor b/Moonlight/Shared/Views/Admin/Nodes/Ddos.razor new file mode 100644 index 00000000..c3b6473f --- /dev/null +++ b/Moonlight/Shared/Views/Admin/Nodes/Ddos.razor @@ -0,0 +1,100 @@ +@page "/admin/nodes/ddos" + +@using Moonlight.Shared.Components.Navigations +@using Moonlight.App.Repositories +@using BlazorTable +@using Microsoft.EntityFrameworkCore +@using Moonlight.App.Database.Entities +@using Moonlight.App.Helpers +@using Moonlight.App.Services + +@implements IDisposable + +@inject DdosAttackRepository DdosAttackRepository +@inject SmartTranslateService SmartTranslateService +@inject MessageService MessageService + + + + + +
+
+
+
+ + + + + + + + + + + + + + + +
+
+
+
+ + + +@code +{ + private DdosAttack[] DdosAttacks; + private LazyLoader LazyLoader; + + protected override Task OnAfterRenderAsync(bool firstRender) + { + MessageService.Subscribe("node.ddos", this, async attack => { Task.Run(async () => await LazyLoader.Reload()); }); + + return Task.CompletedTask; + } + + private Task Load(LazyLoader arg) + { + DdosAttacks = DdosAttackRepository + .Get() + .Include(x => x.Node) + .OrderByDescending(x => x.CreatedAt) + .ToArray(); + + return Task.CompletedTask; + } + + public void Dispose() + { + MessageService.Unsubscribe("node.ddos", this); + } +} \ No newline at end of file diff --git a/Moonlight/Shared/Views/Admin/Nodes/Index.razor b/Moonlight/Shared/Views/Admin/Nodes/Index.razor index e9394476..c18205b6 100644 --- a/Moonlight/Shared/Views/Admin/Nodes/Index.razor +++ b/Moonlight/Shared/Views/Admin/Nodes/Index.razor @@ -2,6 +2,7 @@ @using Moonlight.App.Repositories @using Moonlight.App.Database.Entities @using Moonlight.App.Models.Wings.Resources +@using Moonlight.Shared.Components.Navigations @using Moonlight.App.Services @using Moonlight.App.Services.Interop @using Logging.Net @@ -13,87 +14,87 @@ @inject SmartTranslateService SmartTranslateService -
- -
-
-

- - Nodes - -

- -
-
- @if (Nodes.Any()) - { -
- - - - - - - - - - - - - - - - - - - -
-
- } - else - { -
- No nodes found -
- } + +
+
+

+ + Nodes + +

+
- -
+
+ @if (Nodes.Any()) + { +
+ + + + + + + + + + + + + + + + + + + +
+
+ } + else + { +
+ No nodes found +
+ } +
+
+ @code diff --git a/Moonlight/resources/lang/de_de.lang b/Moonlight/resources/lang/de_de.lang index 8721e8b1..f6a2dfee 100644 --- a/Moonlight/resources/lang/de_de.lang +++ b/Moonlight/resources/lang/de_de.lang @@ -366,3 +366,11 @@ Docker containers running;Docker containers running details;details 1;1 2;2 +DDos;DDos +No ddos attacks found;No ddos attacks found +Node;Node +Date;Date +DDos attack started;DDos attack started +packets;packets +DDos attack stopped;DDos attack stopped + packets; packets From 30bc1c20f469f6b6bbeccafe6df7234cc3159c86 Mon Sep 17 00:00:00 2001 From: Marcel Baumgartner Date: Mon, 20 Mar 2023 18:23:49 +0100 Subject: [PATCH 6/7] Implemented server manager --- .../Models/Daemon/Resources/ContainerStats.cs | 2 +- .../Shared/Views/Admin/Servers/Manager.razor | 158 ++++++++++++++++++ Moonlight/resources/lang/de_de.lang | 10 ++ 3 files changed, 169 insertions(+), 1 deletion(-) create mode 100644 Moonlight/Shared/Views/Admin/Servers/Manager.razor diff --git a/Moonlight/App/Models/Daemon/Resources/ContainerStats.cs b/Moonlight/App/Models/Daemon/Resources/ContainerStats.cs index 5c2e0576..db1a2c8a 100644 --- a/Moonlight/App/Models/Daemon/Resources/ContainerStats.cs +++ b/Moonlight/App/Models/Daemon/Resources/ContainerStats.cs @@ -6,7 +6,7 @@ public class ContainerStats public class Container { - public Guid Name { get; set; } + public string Name { get; set; } public long Memory { get; set; } public double Cpu { get; set; } public long NetworkIn { get; set; } diff --git a/Moonlight/Shared/Views/Admin/Servers/Manager.razor b/Moonlight/Shared/Views/Admin/Servers/Manager.razor new file mode 100644 index 00000000..06aebefb --- /dev/null +++ b/Moonlight/Shared/Views/Admin/Servers/Manager.razor @@ -0,0 +1,158 @@ +@page "/admin/servers/manager" +@using Moonlight.App.Repositories +@using Moonlight.App.Repositories.Servers +@using Moonlight.App.Services +@using Moonlight.App.Services.Interop +@using Moonlight.App.Database.Entities +@using Moonlight.App.Models.Daemon.Resources +@using Moonlight.App.Models.Wings +@using BlazorTable +@using Microsoft.EntityFrameworkCore +@using Moonlight.App.Helpers + +@inject NodeRepository NodeRepository +@inject NodeService NodeService +@inject ServerRepository ServerRepository +@inject SmartTranslateService SmartTranslateService +@inject AlertService AlertService +@inject ServerService ServerService + + +
+
+ + + + + + +
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + +
+
+
+
+
+ +@code +{ + private LazyLoader LazyLoader; + + private Dictionary Containers = new(); + + private async Task Load(LazyLoader lazyLoader) + { + Containers.Clear(); + + foreach (var node in NodeRepository.Get().ToArray()) + { + await lazyLoader.SetText(node.Name); + + var containerStats = await NodeService.GetContainerStats(node); + + foreach (var container in containerStats.Containers) + { + if (Guid.TryParse(container.Name, out Guid uuid)) + { + var server = ServerRepository + .Get() + .Include(x => x.Owner) + .FirstOrDefault(x => x.Uuid == uuid); + + if (server != null) + { + Containers.Add(server, container); + } + } + } + } + } + + private async Task StopAll() + { + var b = await AlertService.YesNo( + SmartTranslateService.Translate("Stop all servers"), + SmartTranslateService.Translate("Do you really want to stop all running servers?"), + SmartTranslateService.Translate("Yes"), + SmartTranslateService.Translate("No") + ); + + if (b) + { + foreach (var containerData in Containers) + { + await ServerService.SetPowerState(containerData.Key, PowerSignal.Stop); + } + } + } + + private async Task KillAll() + { + var b = await AlertService.YesNo( + SmartTranslateService.Translate("Kill all servers"), + SmartTranslateService.Translate("Do you really want to kill all running servers?"), + SmartTranslateService.Translate("Yes"), + SmartTranslateService.Translate("No") + ); + + if (b) + { + foreach (var containerData in Containers) + { + await ServerService.SetPowerState(containerData.Key, PowerSignal.Kill); + } + } + } +} \ No newline at end of file diff --git a/Moonlight/resources/lang/de_de.lang b/Moonlight/resources/lang/de_de.lang index f6a2dfee..fe066311 100644 --- a/Moonlight/resources/lang/de_de.lang +++ b/Moonlight/resources/lang/de_de.lang @@ -374,3 +374,13 @@ DDos attack started;DDos attack started packets;packets DDos attack stopped;DDos attack stopped packets; packets +Stop all;Stop all +Kill all;Kill all +Network in;Network in +Network out;Network out +Kill all servers;Kill all servers +Do you really want to kill all running servers?;Do you really want to kill all running servers? +Change power state for;Change power state for +to;to +Stop all servers;Stop all servers +Do you really want to stop all running servers?;Do you really want to stop all running servers? From 69cfeace82243d810c653f6573f0eda2c85556cc Mon Sep 17 00:00:00 2001 From: Marcel Baumgartner Date: Tue, 21 Mar 2023 18:25:01 +0100 Subject: [PATCH 7/7] Added user edit form. Fixed edit link --- Moonlight/Shared/Views/Admin/Users/Edit.razor | 187 ++++++++++++++++++ .../Shared/Views/Admin/Users/Index.razor | 2 +- Moonlight/resources/lang/de_de.lang | 9 + 3 files changed, 197 insertions(+), 1 deletion(-) create mode 100644 Moonlight/Shared/Views/Admin/Users/Edit.razor diff --git a/Moonlight/Shared/Views/Admin/Users/Edit.razor b/Moonlight/Shared/Views/Admin/Users/Edit.razor new file mode 100644 index 00000000..581400f6 --- /dev/null +++ b/Moonlight/Shared/Views/Admin/Users/Edit.razor @@ -0,0 +1,187 @@ +@page "/admin/users/edit/{Id:int}" +@using Moonlight.App.Repositories +@using Moonlight.App.Database.Entities +@using Moonlight.App.Models.Misc +@using Moonlight.App.Services +@using Moonlight.App.Services.Interop +@using Moonlight.App.Services.Sessions + +@inject UserRepository UserRepository +@inject SessionService SessionService +@inject ToastService ToastService +@inject SmartTranslateService SmartTranslateService + + + + @if (User == null) + { +
+ No user with this id found +
+ } + else + { +
+
+

+ + Manage user @(User.Email) + +

+
+
+ +
+
+
+
+ + +
+
+ + +
+
+ + +
+
+
+
+ + + +
+
+
+
+ + Cancel + + + +
+
+
+
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+
+ + +
+
+ + +
+
+ + +
+
+
+
+ } +
+
+ +@code +{ + [Parameter] + public int Id { get; set; } + + private User? User; + + private Task Load(LazyLoader arg) + { + User = UserRepository.Get().FirstOrDefault(x => x.Id == Id); + + return Task.CompletedTask; + } + + private async Task UpdateStatus() + { + var user = UserRepository.Get().FirstOrDefault(x => x.Id == User!.Id)!; + user.Status = User!.Status; + UserRepository.Update(user); + + SessionService.ReloadUserSessions(User); + + await ToastService.Success(SmartTranslateService.Translate("Successfully updated user")); + } + + private async Task Update() + { + UserRepository.Update(User!); + + await ToastService.Success(SmartTranslateService.Translate("Successfully updated user")); + } +} \ No newline at end of file diff --git a/Moonlight/Shared/Views/Admin/Users/Index.razor b/Moonlight/Shared/Views/Admin/Users/Index.razor index b034f6a2..b4d93243 100644 --- a/Moonlight/Shared/Views/Admin/Users/Index.razor +++ b/Moonlight/Shared/Views/Admin/Users/Index.razor @@ -41,7 +41,7 @@ diff --git a/Moonlight/resources/lang/de_de.lang b/Moonlight/resources/lang/de_de.lang index fe066311..485272ea 100644 --- a/Moonlight/resources/lang/de_de.lang +++ b/Moonlight/resources/lang/de_de.lang @@ -384,3 +384,12 @@ Change power state for;Change power state for to;to Stop all servers;Stop all servers Do you really want to stop all running servers?;Do you really want to stop all running servers? +Manage ;Manage +Manage user ;Manage user +Reloading;Reloading +Update;Update +Updating;Updating +Successfully updated user;Successfully updated user +Discord id;Discord id +Discord username;Discord username +Discord discriminator;Discord discriminator