From dfda44495655cbcd3753c5e486b93f43bde3f83c Mon Sep 17 00:00:00 2001 From: Daniel Balk <67603460+Daniel-Balk@users.noreply.github.com> Date: Sun, 2 Apr 2023 22:51:27 +0200 Subject: [PATCH 1/4] cleanup service --- Moonlight/App/Database/DataContext.cs | 2 + ...4329_AddCleanupExceptionsTable.Designer.cs | 1082 +++++++++++++++++ ...0230402204329_AddCleanupExceptionsTable.cs | 170 +++ .../Migrations/DataContextModelSnapshot.cs | 36 +- Moonlight/App/Models/Misc/CleanupException.cs | 8 + .../CleanupExceptionRepository.cs | 44 + Moonlight/App/Services/CleanupService.cs | 370 ++++++ Moonlight/Program.cs | 5 + 8 files changed, 1711 insertions(+), 6 deletions(-) create mode 100644 Moonlight/App/Database/Migrations/20230402204329_AddCleanupExceptionsTable.Designer.cs create mode 100644 Moonlight/App/Database/Migrations/20230402204329_AddCleanupExceptionsTable.cs create mode 100644 Moonlight/App/Models/Misc/CleanupException.cs create mode 100644 Moonlight/App/Repositories/CleanupExceptionRepository.cs create mode 100644 Moonlight/App/Services/CleanupService.cs diff --git a/Moonlight/App/Database/DataContext.cs b/Moonlight/App/Database/DataContext.cs index c8575c49..30802299 100644 --- a/Moonlight/App/Database/DataContext.cs +++ b/Moonlight/App/Database/DataContext.cs @@ -2,6 +2,7 @@ using Moonlight.App.Database.Entities; using Moonlight.App.Database.Entities.LogsEntries; using Moonlight.App.Database.Entities.Notification; +using Moonlight.App.Models.Misc; using Moonlight.App.Services; namespace Moonlight.App.Database; @@ -42,6 +43,7 @@ public class DataContext : DbContext public DbSet AaPanels { get; set; } public DbSet Websites { get; set; } public DbSet DdosAttacks { get; set; } + public DbSet CleanupExceptions { get; set; } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { diff --git a/Moonlight/App/Database/Migrations/20230402204329_AddCleanupExceptionsTable.Designer.cs b/Moonlight/App/Database/Migrations/20230402204329_AddCleanupExceptionsTable.Designer.cs new file mode 100644 index 00000000..7ae61058 --- /dev/null +++ b/Moonlight/App/Database/Migrations/20230402204329_AddCleanupExceptionsTable.Designer.cs @@ -0,0 +1,1082 @@ +// +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("20230402204329_AddCleanupExceptionsTable")] + partial class AddCleanupExceptionsTable + { + /// + 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() + .HasMaxLength(128) + .HasColumnType("varchar(128)"); + + b.Property("Admin") + .HasColumnType("tinyint(1)"); + + b.Property("City") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("varchar(128)"); + + b.Property("Country") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("varchar(64)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("DiscordId") + .HasColumnType("bigint"); + + b.Property("Email") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("FirstName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("varchar(64)"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("varchar(64)"); + + b.Property("Password") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("State") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("varchar(64)"); + + 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.Models.Misc.CleanupException", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Note") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ServerId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("CleanupExceptions"); + }); + + 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/20230402204329_AddCleanupExceptionsTable.cs b/Moonlight/App/Database/Migrations/20230402204329_AddCleanupExceptionsTable.cs new file mode 100644 index 00000000..3c0f8e18 --- /dev/null +++ b/Moonlight/App/Database/Migrations/20230402204329_AddCleanupExceptionsTable.cs @@ -0,0 +1,170 @@ +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Moonlight.App.Database.Migrations +{ + /// + public partial class AddCleanupExceptionsTable : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "State", + table: "Users", + type: "varchar(64)", + maxLength: 64, + nullable: false, + oldClrType: typeof(string), + oldType: "longtext") + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.AlterColumn( + name: "LastName", + table: "Users", + type: "varchar(64)", + maxLength: 64, + nullable: false, + oldClrType: typeof(string), + oldType: "longtext") + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.AlterColumn( + name: "FirstName", + table: "Users", + type: "varchar(64)", + maxLength: 64, + nullable: false, + oldClrType: typeof(string), + oldType: "longtext") + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.AlterColumn( + name: "Country", + table: "Users", + type: "varchar(64)", + maxLength: 64, + nullable: false, + oldClrType: typeof(string), + oldType: "longtext") + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.AlterColumn( + name: "City", + table: "Users", + type: "varchar(128)", + maxLength: 128, + nullable: false, + oldClrType: typeof(string), + oldType: "longtext") + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.AlterColumn( + name: "Address", + table: "Users", + type: "varchar(128)", + maxLength: 128, + nullable: false, + oldClrType: typeof(string), + oldType: "longtext") + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "CleanupExceptions", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + ServerId = table.Column(type: "int", nullable: false), + Note = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK_CleanupExceptions", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "CleanupExceptions"); + + migrationBuilder.AlterColumn( + name: "State", + table: "Users", + type: "longtext", + nullable: false, + oldClrType: typeof(string), + oldType: "varchar(64)", + oldMaxLength: 64) + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.AlterColumn( + name: "LastName", + table: "Users", + type: "longtext", + nullable: false, + oldClrType: typeof(string), + oldType: "varchar(64)", + oldMaxLength: 64) + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.AlterColumn( + name: "FirstName", + table: "Users", + type: "longtext", + nullable: false, + oldClrType: typeof(string), + oldType: "varchar(64)", + oldMaxLength: 64) + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.AlterColumn( + name: "Country", + table: "Users", + type: "longtext", + nullable: false, + oldClrType: typeof(string), + oldType: "varchar(64)", + oldMaxLength: 64) + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.AlterColumn( + name: "City", + table: "Users", + type: "longtext", + nullable: false, + oldClrType: typeof(string), + oldType: "varchar(128)", + oldMaxLength: 128) + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.AlterColumn( + name: "Address", + table: "Users", + type: "longtext", + nullable: false, + oldClrType: typeof(string), + oldType: "varchar(128)", + oldMaxLength: 128) + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + } + } +} diff --git a/Moonlight/App/Database/Migrations/DataContextModelSnapshot.cs b/Moonlight/App/Database/Migrations/DataContextModelSnapshot.cs index 566e4e67..ec2d7883 100644 --- a/Moonlight/App/Database/Migrations/DataContextModelSnapshot.cs +++ b/Moonlight/App/Database/Migrations/DataContextModelSnapshot.cs @@ -707,18 +707,21 @@ namespace Moonlight.App.Database.Migrations b.Property("Address") .IsRequired() - .HasColumnType("longtext"); + .HasMaxLength(128) + .HasColumnType("varchar(128)"); b.Property("Admin") .HasColumnType("tinyint(1)"); b.Property("City") .IsRequired() - .HasColumnType("longtext"); + .HasMaxLength(128) + .HasColumnType("varchar(128)"); b.Property("Country") .IsRequired() - .HasColumnType("longtext"); + .HasMaxLength(64) + .HasColumnType("varchar(64)"); b.Property("CreatedAt") .HasColumnType("datetime(6)"); @@ -732,11 +735,13 @@ namespace Moonlight.App.Database.Migrations b.Property("FirstName") .IsRequired() - .HasColumnType("longtext"); + .HasMaxLength(64) + .HasColumnType("varchar(64)"); b.Property("LastName") .IsRequired() - .HasColumnType("longtext"); + .HasMaxLength(64) + .HasColumnType("varchar(64)"); b.Property("Password") .IsRequired() @@ -744,7 +749,8 @@ namespace Moonlight.App.Database.Migrations b.Property("State") .IsRequired() - .HasColumnType("longtext"); + .HasMaxLength(64) + .HasColumnType("varchar(64)"); b.Property("Status") .HasColumnType("int"); @@ -821,6 +827,24 @@ namespace Moonlight.App.Database.Migrations b.ToTable("Websites"); }); + modelBuilder.Entity("Moonlight.App.Models.Misc.CleanupException", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Note") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ServerId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("CleanupExceptions"); + }); + modelBuilder.Entity("Moonlight.App.Database.Entities.Database", b => { b.HasOne("Moonlight.App.Database.Entities.AaPanel", "AaPanel") diff --git a/Moonlight/App/Models/Misc/CleanupException.cs b/Moonlight/App/Models/Misc/CleanupException.cs new file mode 100644 index 00000000..83dcba77 --- /dev/null +++ b/Moonlight/App/Models/Misc/CleanupException.cs @@ -0,0 +1,8 @@ +namespace Moonlight.App.Models.Misc; + +public class CleanupException +{ + public int Id { get; set; } + public int ServerId { get; set; } + public string Note { get; set; } +} \ No newline at end of file diff --git a/Moonlight/App/Repositories/CleanupExceptionRepository.cs b/Moonlight/App/Repositories/CleanupExceptionRepository.cs new file mode 100644 index 00000000..9cfd0b28 --- /dev/null +++ b/Moonlight/App/Repositories/CleanupExceptionRepository.cs @@ -0,0 +1,44 @@ +using Microsoft.EntityFrameworkCore; +using Moonlight.App.Database; +using Moonlight.App.Models.Misc; + +namespace Moonlight.App.Repositories; + +public class CleanupExceptionRepository : IDisposable +{ + private readonly DataContext DataContext; + + public CleanupExceptionRepository(DataContext dataContext) + { + DataContext = dataContext; + } + + public DbSet Get() + { + return DataContext.CleanupExceptions; + } + + public CleanupException Add(CleanupException cleanupException) + { + var x = DataContext.CleanupExceptions.Add(cleanupException).Entity; + DataContext.SaveChanges(); + return x; + } + + public void Update(CleanupException cleanupException) + { + DataContext.CleanupExceptions.Update(cleanupException); + DataContext.SaveChanges(); + } + + public void Delete(CleanupException cleanupException) + { + DataContext.CleanupExceptions.Remove(cleanupException); + DataContext.SaveChanges(); + } + + public void Dispose() + { + DataContext.Dispose(); + } +} \ No newline at end of file diff --git a/Moonlight/App/Services/CleanupService.cs b/Moonlight/App/Services/CleanupService.cs new file mode 100644 index 00000000..d4c6ff8c --- /dev/null +++ b/Moonlight/App/Services/CleanupService.cs @@ -0,0 +1,370 @@ +using System.Diagnostics; +using Microsoft.EntityFrameworkCore; +using MineStatLib; +using Moonlight.App.Database.Entities; +using Moonlight.App.Models.Daemon.Resources; +using Moonlight.App.Models.Wings; +using Moonlight.App.Repositories; +using Moonlight.App.Repositories.Servers; +using Logging.Net; +using Newtonsoft.Json; + +namespace Moonlight.App.Services; + +public class CleanupService +{ + public DateTime StartedAt { get; private set; } + public DateTime CompletedAt { get; private set; } + public int ServersCleaned { get; private set; } + public int CleanupsPerformed { get; private set; } + public int ServersRunning { get; private set; } + public bool IsRunning { get; private set; } + public bool Activated { get; set; } + public int PercentProgress { get; private set; } = 100; + public string Status { get; private set; } = "N/A"; + + private Task PerformTask; + private readonly ConfigService ConfigService; + private readonly IServiceScopeFactory ServiceScopeFactory; + + private int RequiredCpu; + private long RequiredMemory; + private int WaitTime; + + public EventHandler OnUpdated; + + public CleanupService(ConfigService configService, IServiceScopeFactory serviceScopeFactory) + { + ServiceScopeFactory = serviceScopeFactory; + ConfigService = configService; + + IConfiguration configuration = new ConfigurationBuilder() + .AddJsonFile("appsettings.json") + .Build(); + + var config = configuration.GetSection("Cleanup"); + + RequiredCpu = config.GetValue("Cpu"); + RequiredMemory = config.GetValue("Memory"); + WaitTime = config.GetValue("Wait"); + + if (!ConfigService.DebugMode) + Task.Run(Start); + } + + private void Start() + { + StartedAt = DateTime.Now; + CompletedAt = DateTime.Now; + IsRunning = false; + Activated = true; + + DoWaiting(); + } + + private async void DoWaiting() + { + while (true) + { + if (Activated) + await Perform(); + + try + { + await Task.Delay((int)TimeSpan.FromMinutes(WaitTime).TotalMilliseconds); + } + catch (Exception) + { + } + } + } + + public async Task TriggerPerform() + { + if (IsRunning) + return; + + PerformTask = new Task(async () => await Perform()); + PerformTask.Start(); + } + + private async Task Perform() + { + if (IsRunning) + return; + + IsRunning = true; + StartedAt = DateTime.Now; + ServersRunning = 0; + + OnUpdated?.Invoke(this, null); + + using (var scope = ServiceScopeFactory.CreateScope()) + { + // Setup time measure + var watch = new Stopwatch(); + watch.Start(); + + // Get repos from dependency injection + var serverRepository = scope.ServiceProvider.GetRequiredService(); + var nodeRepository = scope.ServiceProvider.GetRequiredService(); + var cleanupExceptionRepository = scope.ServiceProvider.GetRequiredService(); + var nodeService = scope.ServiceProvider.GetRequiredService(); + var imageRepo = scope.ServiceProvider.GetRequiredService(); + var serverService = scope.ServiceProvider.GetRequiredService(); + + // Fetching data from mysql + var servers = serverRepository.Get() + .Include(x => x.Image) + .ToArray(); + var nodes = nodeRepository.Get().ToArray(); + var exceptions = cleanupExceptionRepository.Get().ToArray(); + var images = imageRepo.Get().ToArray(); + + var nodeCount = nodes.Count(); + + // We use this counter for the foreach loops + int counter = 0; + PercentProgress = 0; + + // Fetching data from nodes so we know what nodes to scan + var nodeContainers = new Dictionary(); + + Status = "Checking Nodes"; + counter = 0; + PercentProgress = 0; + OnUpdated?.Invoke(this, null); + + foreach (var node in nodes) + { + try + { + var cpu = await nodeService.GetCpuStats(node); + var freeMemory = await nodeService.GetMemoryStats(node); + + if (cpu.Usage > RequiredCpu || freeMemory.Free < RequiredMemory) + { + var c = await nodeService.GetContainerStats(node); + var containers = c.Containers; + nodeContainers.Add(node, containers.ToArray()); + } + } + catch (Exception e) + { + Logger.Error($"Error fetching cleanup data from node {node.Id}"); + Logger.Error(e); + } + + counter++; + CalculateAndUpdateProgress(counter, nodeCount); + OnUpdated?.Invoke(this, null); + } + + // Searching for servers we can actually stop because they have the cleanup tag + // and determine which servers we have to check for an illegal mc server + var serversToCheck = new List(); + var serversToCheckForMc = new List(); + + Status = "Checking found servers"; + counter = 0; + PercentProgress = 0; + OnUpdated?.Invoke(this, null); + + // Count every container for progress calculation + var allContainers = 0; + foreach (var array in nodeContainers) + { + allContainers += array.Value.Length; + } + + foreach (var nodeContainer in nodeContainers) + { + try + { + foreach (var container in nodeContainer.Value) + { + var server = servers.First(x => x.Uuid.ToString() == container.Name); + var tagsJson = imageRepo + .Get() + .First(x => x.Id == server.Image.Id).TagsJson; + + var tags = JsonConvert.DeserializeObject(tagsJson) ?? Array.Empty(); + + if (tags.FirstOrDefault(x => x == "cleanup") != null) + { + serversToCheck.Add(server); + } + + if (tags.FirstOrDefault(x => x == "illegalmc") != null) + { + serversToCheckForMc.Add(server); + } + } + } + catch (Exception e) + { + Logger.Error($"Error processing cleanup data from node {nodeContainer.Key.Id}"); + Logger.Error(e); + } + + counter++; + CalculateAndUpdateProgress(counter, allContainers); + OnUpdated?.Invoke(this, null); + } + + // Now we gonna scan every tagged server + Status = "Scanning servers"; + counter = 0; + PercentProgress = 0; + OnUpdated?.Invoke(this, null); + + foreach (var server in serversToCheck) + { + try + { + var serverData = serverRepository + .Get() + .Include(x => x.MainAllocation) + .Include(x => x.Node) + .Include(x => x.Variables) + .First(x => x.Id == server.Id); + + var players = GetPlayers(serverData.Node, serverData.MainAllocation); + var stats = await serverService.GetDetails(server); + + var exception = exceptions.FirstOrDefault(x => x.ServerId == server.Id) != null; + + if (stats != null) + { + if (exception) + { + if (players == 0 && stats.Utilization.Uptime > TimeSpan.FromHours(6).TotalMilliseconds) + { + await serverService.SetPowerState(server, PowerSignal.Restart); + ServersCleaned++; + OnUpdated?.Invoke(this, null); + } + else + { + ServersRunning++; + OnUpdated?.Invoke(this, null); + } + } + else + { + if (players == 0 && stats.Utilization.Uptime > TimeSpan.FromMinutes(10).TotalMilliseconds) + { + var cleanupVar = serverData.Variables.FirstOrDefault(x => x.Key == "J2S"); + + if (cleanupVar == null) + { + await serverService.SetPowerState(server, PowerSignal.Stop); + ServersCleaned++; + OnUpdated?.Invoke(this, null); + } + else + { + if (cleanupVar.Value == "1") + { + await serverService.SetPowerState(server, PowerSignal.Restart); + ServersCleaned++; + OnUpdated?.Invoke(this, null); + } + else + { + await serverService.SetPowerState(server, PowerSignal.Stop); + ServersCleaned++; + OnUpdated?.Invoke(this, null); + } + } + } + else + { + ServersRunning++; + OnUpdated?.Invoke(this, null); + } + } + } + } + catch (Exception e) + { + Logger.Error($"Error scanning {server.Name}"); + Logger.Error(e); + } + + counter++; + CalculateAndUpdateProgress(counter, serversToCheck.Count); + OnUpdated?.Invoke(this, null); + } + + // Finally we have to check all code container allocations + // for illegal hosted mc servers + + Status = "Scanning code containers"; + counter = 0; + PercentProgress = 0; + OnUpdated?.Invoke(this, null); + + foreach (var server in serversToCheckForMc) + { + try + { + var serverData = serverRepository + .Get() + .Include(x => x.Allocations) + .Include(x => x.Node) + .First(x => x.Id == server.Id); + + foreach (var allocation in serverData.Allocations) + { + if (GetPlayers(server.Node, allocation) != -1) + { + // TODO: Suspend server + Logger.Warn("Found CC running mc: https://moonlight.endelon-hosting.de/server/" + + server.Uuid + "/"); + } + } + } + catch (Exception e) + { + Logger.Error($"Error scanning (cc) {server.Name}"); + Logger.Error(e); + } + + counter++; + CalculateAndUpdateProgress(counter, serversToCheckForMc.Count); + OnUpdated?.Invoke(this, null); + } + + watch.Stop(); + + Status = $"Cleanup finifhed. Duration: {Math.Round(TimeSpan.FromMilliseconds(watch.ElapsedMilliseconds).TotalMinutes, 2)} Minuten"; + PercentProgress = 100; + OnUpdated?.Invoke(this, null); + } + + IsRunning = false; + CompletedAt = DateTime.Now; + CleanupsPerformed++; + OnUpdated?.Invoke(this, null); + } + + private int GetPlayers(Node node, NodeAllocation allocation) + { + var ms = new MineStat(node.Fqdn, (ushort)allocation.Port); + + if (ms.ServerUp) + { + return ms.CurrentPlayersInt; + } + else + { + return -1; + } + } + + private void CalculateAndUpdateProgress(int now, int all) + { + PercentProgress = (int)Math.Round((now / (double)all) * 100); + } +} \ No newline at end of file diff --git a/Moonlight/Program.cs b/Moonlight/Program.cs index 45436097..8dc94299 100644 --- a/Moonlight/Program.cs +++ b/Moonlight/Program.cs @@ -97,6 +97,8 @@ namespace Moonlight builder.Services.AddScoped(); builder.Services.AddScoped(); + builder.Services.AddSingleton(); + // Loggers builder.Services.AddScoped(); builder.Services.AddScoped(); @@ -153,6 +155,9 @@ namespace Moonlight // Support service var supportServerService = app.Services.GetRequiredService(); + // cleanup service + _ = app.Services.GetRequiredService(); + // Discord bot service //var discordBotService = app.Services.GetRequiredService(); From 708ae080a3c4ef2504b95c580f2ceed342fd69cb Mon Sep 17 00:00:00 2001 From: Daniel Balk <67603460+Daniel-Balk@users.noreply.github.com> Date: Sun, 2 Apr 2023 23:17:09 +0200 Subject: [PATCH 2/4] cleanup ui (part 1) + stupidity fix --- Moonlight/App/Models/Misc/AuditLogType.cs | 5 +- Moonlight/App/Services/CleanupService.cs | 12 +- Moonlight/Program.cs | 1 + .../Shared/Views/Admin/Servers/Cleanup.razor | 185 ++++++++++++++++++ Moonlight/resources/lang/de_de.lang | 22 ++- 5 files changed, 215 insertions(+), 10 deletions(-) create mode 100644 Moonlight/Shared/Views/Admin/Servers/Cleanup.razor diff --git a/Moonlight/App/Models/Misc/AuditLogType.cs b/Moonlight/App/Models/Misc/AuditLogType.cs index d505deb9..13fafa1c 100644 --- a/Moonlight/App/Models/Misc/AuditLogType.cs +++ b/Moonlight/App/Models/Misc/AuditLogType.cs @@ -19,5 +19,8 @@ public enum AuditLogType AddDomainRecord, UpdateDomainRecord, DeleteDomainRecord, - PasswordReset + PasswordReset, + CleanupEnabled, + CleanupDisabled, + CleanupTriggered, } \ No newline at end of file diff --git a/Moonlight/App/Services/CleanupService.cs b/Moonlight/App/Services/CleanupService.cs index d4c6ff8c..ef500550 100644 --- a/Moonlight/App/Services/CleanupService.cs +++ b/Moonlight/App/Services/CleanupService.cs @@ -37,12 +37,8 @@ public class CleanupService { ServiceScopeFactory = serviceScopeFactory; ConfigService = configService; - - IConfiguration configuration = new ConfigurationBuilder() - .AddJsonFile("appsettings.json") - .Build(); - var config = configuration.GetSection("Cleanup"); + var config = configService.GetSection("Moonlight").GetSection("Cleanup"); RequiredCpu = config.GetValue("Cpu"); RequiredMemory = config.GetValue("Memory"); @@ -71,9 +67,9 @@ public class CleanupService try { - await Task.Delay((int)TimeSpan.FromMinutes(WaitTime).TotalMilliseconds); + await Task.Delay((int) TimeSpan.FromMinutes(WaitTime).TotalMilliseconds); } - catch (Exception) + catch (Exception ex) { } } @@ -338,7 +334,7 @@ public class CleanupService watch.Stop(); - Status = $"Cleanup finifhed. Duration: {Math.Round(TimeSpan.FromMilliseconds(watch.ElapsedMilliseconds).TotalMinutes, 2)} Minuten"; + Status = $"Cleanup finished. Duration: {Math.Round(TimeSpan.FromMilliseconds(watch.ElapsedMilliseconds).TotalMinutes, 2)} Minutes"; PercentProgress = 100; OnUpdated?.Invoke(this, null); } diff --git a/Moonlight/Program.cs b/Moonlight/Program.cs index 8dc94299..f7fa1df6 100644 --- a/Moonlight/Program.cs +++ b/Moonlight/Program.cs @@ -65,6 +65,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/Views/Admin/Servers/Cleanup.razor b/Moonlight/Shared/Views/Admin/Servers/Cleanup.razor new file mode 100644 index 00000000..5215224c --- /dev/null +++ b/Moonlight/Shared/Views/Admin/Servers/Cleanup.razor @@ -0,0 +1,185 @@ +@page "/admin/servers/cleanup" + +@using Moonlight.App.Services +@using Logging.Net +@using Moonlight.App.Models.Misc +@using Moonlight.App.Services.LogServices + +@inject CleanupService CleanupService +@inject AuditLogService AuditLogService + +@implements IDisposable + +Cleanup + + +
+
+
+
+
+ +
+
+
+
+ @(CleanupService.ServersCleaned) +
+ Servers + stopped +
+
+
+
+
+
+
+
+
+ +
+
+
+
+ @(CleanupService.CleanupsPerformed) +
+ Cleanups + executed +
+
+
+
+
+
+
+
+
+ +
+
+
+
+ @(CleanupService.ServersRunning) +
+ Used clanup + Servers +
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+ @if (CleanupService.IsRunning) + { + + } + else + { + + } +
+
+ @if (CleanupService.Activated) + { + + } + else + { + + } +
+
+ @if (CleanupService.Activated) + { + + } + else + { + + } +
+
+
+
+
+
+
+
+ + + + +@code +{ + protected async override Task OnAfterRenderAsync(bool firstRender) + { + if (firstRender) + { + CleanupService.OnUpdated += OnUpdated; + } + } + + private void OnUpdated(object? sender, EventArgs e) + { + Logger.Debug(CleanupService.PercentProgress); + + InvokeAsync(StateHasChanged); + } + + public void Dispose() + { + CleanupService.OnUpdated -= OnUpdated; + } + + private void Enable() + { + CleanupService.Activated = true; + AuditLogService.Log(AuditLogType.CleanupEnabled, "The automatic cleanup has been activated"); + InvokeAsync(StateHasChanged); + } + + private void Disabled() + { + CleanupService.Activated = false; + AuditLogService.Log(AuditLogType.CleanupDisabled, "The automatic cleanup has been disabled"); + InvokeAsync(StateHasChanged); + } + + private async void Trigger() + { + await CleanupService.TriggerPerform(); + await AuditLogService.Log(AuditLogType.CleanupTriggered, "The automatic cleanup has been manually triggered"); + await InvokeAsync(StateHasChanged); + } +} \ No newline at end of file diff --git a/Moonlight/resources/lang/de_de.lang b/Moonlight/resources/lang/de_de.lang index 8f280dab..7e426f46 100644 --- a/Moonlight/resources/lang/de_de.lang +++ b/Moonlight/resources/lang/de_de.lang @@ -406,4 +406,24 @@ The City field is required.;The City field is required. The State field is required.;The State field is required. The Country field is required.;The Country field is required. Street and house number requered;Street and house number requered -Max lenght reached;Max lenght reached \ No newline at end of file +Max lenght reached;Max lenght reached +stopped;stopped +Cleanups;Cleanups +executed;executed +Used clanup;Used clanup +Checking Nodes;Checking Nodes +Enable;Enable +Disabble;Disabble +Checking found servers;Checking found servers +Scanning servers;Scanning servers +Scanning code containers;Scanning code containers +Cleanup finifhed. Duration: 0.08 Minuten;Cleanup finifhed. Duration: 0.08 Minuten +Disable;Disable +Cleanup finished. Duration: 0.08 Minuten;Cleanup finished. Duration: 0.08 Minuten +Cleanup finished. Duration: 0.08 Minutes;Cleanup finished. Duration: 0.08 Minutes +Cleanup finished. Duration: 0.13 Minutes;Cleanup finished. Duration: 0.13 Minutes +Cleanup finished. Duration: 0.23 Minutes;Cleanup finished. Duration: 0.23 Minutes +Cleanup finished. Duration: 0.14 Minutes;Cleanup finished. Duration: 0.14 Minutes +Cleanup finished. Duration: 0.09 Minutes;Cleanup finished. Duration: 0.09 Minutes +Cleanup finished. Duration: 0.1 Minutes;Cleanup finished. Duration: 0.1 Minutes +Cleanup finished. Duration: 0.11 Minutes;Cleanup finished. Duration: 0.11 Minutes From 7b6528b03f4db90a6f935f32cf786965f4e49607 Mon Sep 17 00:00:00 2001 From: Daniel Balk <67603460+Daniel-Balk@users.noreply.github.com> Date: Mon, 3 Apr 2023 00:15:12 +0200 Subject: [PATCH 3/4] cleanup exceptions (hopefully) --- Moonlight/App/Database/DataContext.cs | 1 - Moonlight/App/Database/Entities/Server.cs | 1 + ...5_ChengedCleanupExceptionModel.Designer.cs | 1098 +++++++++++++++++ ...0402215155_ChengedCleanupExceptionModel.cs | 51 + ...nupExceptionChangedServerModel.Designer.cs | 1067 ++++++++++++++++ ...movedCleanupExceptionChangedServerModel.cs | 62 + .../Migrations/DataContextModelSnapshot.cs | 21 +- Moonlight/App/Models/Misc/CleanupException.cs | 8 - .../CleanupExceptionRepository.cs | 44 - Moonlight/App/Services/CleanupService.cs | 5 +- Moonlight/Moonlight.csproj | 2 + Moonlight/Program.cs | 1 - .../{Cleanup.razor => Cleanup/Index.razor} | 29 +- .../Shared/Views/Admin/Servers/Edit.razor | 4 + Moonlight/resources/lang/de_de.lang | 2 + 15 files changed, 2314 insertions(+), 82 deletions(-) create mode 100644 Moonlight/App/Database/Migrations/20230402215155_ChengedCleanupExceptionModel.Designer.cs create mode 100644 Moonlight/App/Database/Migrations/20230402215155_ChengedCleanupExceptionModel.cs create mode 100644 Moonlight/App/Database/Migrations/20230402220651_RemovedCleanupExceptionChangedServerModel.Designer.cs create mode 100644 Moonlight/App/Database/Migrations/20230402220651_RemovedCleanupExceptionChangedServerModel.cs delete mode 100644 Moonlight/App/Models/Misc/CleanupException.cs delete mode 100644 Moonlight/App/Repositories/CleanupExceptionRepository.cs rename Moonlight/Shared/Views/Admin/Servers/{Cleanup.razor => Cleanup/Index.razor} (90%) diff --git a/Moonlight/App/Database/DataContext.cs b/Moonlight/App/Database/DataContext.cs index 30802299..23b93734 100644 --- a/Moonlight/App/Database/DataContext.cs +++ b/Moonlight/App/Database/DataContext.cs @@ -43,7 +43,6 @@ public class DataContext : DbContext public DbSet AaPanels { get; set; } public DbSet Websites { get; set; } public DbSet DdosAttacks { get; set; } - public DbSet CleanupExceptions { get; set; } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { diff --git a/Moonlight/App/Database/Entities/Server.cs b/Moonlight/App/Database/Entities/Server.cs index 11388d90..cad9ced9 100644 --- a/Moonlight/App/Database/Entities/Server.cs +++ b/Moonlight/App/Database/Entities/Server.cs @@ -20,4 +20,5 @@ public class Server public NodeAllocation MainAllocation { get; set; } = null!; public Node Node { get; set; } = null!; public User Owner { get; set; } = null!; + public bool IsCleanupException { get; set; } = false; } \ No newline at end of file diff --git a/Moonlight/App/Database/Migrations/20230402215155_ChengedCleanupExceptionModel.Designer.cs b/Moonlight/App/Database/Migrations/20230402215155_ChengedCleanupExceptionModel.Designer.cs new file mode 100644 index 00000000..6dd7b206 --- /dev/null +++ b/Moonlight/App/Database/Migrations/20230402215155_ChengedCleanupExceptionModel.Designer.cs @@ -0,0 +1,1098 @@ +// +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("20230402215155_ChengedCleanupExceptionModel")] + partial class ChengedCleanupExceptionModel + { + /// + 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() + .HasMaxLength(128) + .HasColumnType("varchar(128)"); + + b.Property("Admin") + .HasColumnType("tinyint(1)"); + + b.Property("City") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("varchar(128)"); + + b.Property("Country") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("varchar(64)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("DiscordId") + .HasColumnType("bigint"); + + b.Property("Email") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("FirstName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("varchar(64)"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("varchar(64)"); + + b.Property("Password") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("State") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("varchar(64)"); + + 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.Models.Misc.CleanupException", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Note") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ServerId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("ServerId"); + + b.ToTable("CleanupExceptions"); + }); + + 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.Models.Misc.CleanupException", b => + { + b.HasOne("Moonlight.App.Database.Entities.Server", "Server") + .WithMany() + .HasForeignKey("ServerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Server"); + }); + + 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/20230402215155_ChengedCleanupExceptionModel.cs b/Moonlight/App/Database/Migrations/20230402215155_ChengedCleanupExceptionModel.cs new file mode 100644 index 00000000..15e5bcbc --- /dev/null +++ b/Moonlight/App/Database/Migrations/20230402215155_ChengedCleanupExceptionModel.cs @@ -0,0 +1,51 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Moonlight.App.Database.Migrations +{ + /// + public partial class ChengedCleanupExceptionModel : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "CreatedAt", + table: "CleanupExceptions", + type: "datetime(6)", + nullable: false, + defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)); + + migrationBuilder.CreateIndex( + name: "IX_CleanupExceptions_ServerId", + table: "CleanupExceptions", + column: "ServerId"); + + migrationBuilder.AddForeignKey( + name: "FK_CleanupExceptions_Servers_ServerId", + table: "CleanupExceptions", + column: "ServerId", + principalTable: "Servers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_CleanupExceptions_Servers_ServerId", + table: "CleanupExceptions"); + + migrationBuilder.DropIndex( + name: "IX_CleanupExceptions_ServerId", + table: "CleanupExceptions"); + + migrationBuilder.DropColumn( + name: "CreatedAt", + table: "CleanupExceptions"); + } + } +} diff --git a/Moonlight/App/Database/Migrations/20230402220651_RemovedCleanupExceptionChangedServerModel.Designer.cs b/Moonlight/App/Database/Migrations/20230402220651_RemovedCleanupExceptionChangedServerModel.Designer.cs new file mode 100644 index 00000000..59b5c123 --- /dev/null +++ b/Moonlight/App/Database/Migrations/20230402220651_RemovedCleanupExceptionChangedServerModel.Designer.cs @@ -0,0 +1,1067 @@ +// +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("20230402220651_RemovedCleanupExceptionChangedServerModel")] + partial class RemovedCleanupExceptionChangedServerModel + { + /// + 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("IsCleanupException") + .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() + .HasMaxLength(128) + .HasColumnType("varchar(128)"); + + b.Property("Admin") + .HasColumnType("tinyint(1)"); + + b.Property("City") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("varchar(128)"); + + b.Property("Country") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("varchar(64)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("DiscordId") + .HasColumnType("bigint"); + + b.Property("Email") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("FirstName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("varchar(64)"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("varchar(64)"); + + b.Property("Password") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("State") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("varchar(64)"); + + 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/20230402220651_RemovedCleanupExceptionChangedServerModel.cs b/Moonlight/App/Database/Migrations/20230402220651_RemovedCleanupExceptionChangedServerModel.cs new file mode 100644 index 00000000..4f1a3c19 --- /dev/null +++ b/Moonlight/App/Database/Migrations/20230402220651_RemovedCleanupExceptionChangedServerModel.cs @@ -0,0 +1,62 @@ +using System; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Moonlight.App.Database.Migrations +{ + /// + public partial class RemovedCleanupExceptionChangedServerModel : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "CleanupExceptions"); + + migrationBuilder.AddColumn( + name: "IsCleanupException", + table: "Servers", + type: "tinyint(1)", + nullable: false, + defaultValue: false); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "IsCleanupException", + table: "Servers"); + + migrationBuilder.CreateTable( + name: "CleanupExceptions", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + ServerId = table.Column(type: "int", nullable: false), + CreatedAt = table.Column(type: "datetime(6)", nullable: false), + Note = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK_CleanupExceptions", x => x.Id); + table.ForeignKey( + name: "FK_CleanupExceptions_Servers_ServerId", + column: x => x.ServerId, + principalTable: "Servers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateIndex( + name: "IX_CleanupExceptions_ServerId", + table: "CleanupExceptions", + column: "ServerId"); + } + } +} diff --git a/Moonlight/App/Database/Migrations/DataContextModelSnapshot.cs b/Moonlight/App/Database/Migrations/DataContextModelSnapshot.cs index ec2d7883..dba0d962 100644 --- a/Moonlight/App/Database/Migrations/DataContextModelSnapshot.cs +++ b/Moonlight/App/Database/Migrations/DataContextModelSnapshot.cs @@ -482,6 +482,9 @@ namespace Moonlight.App.Database.Migrations b.Property("Installing") .HasColumnType("tinyint(1)"); + b.Property("IsCleanupException") + .HasColumnType("tinyint(1)"); + b.Property("MainAllocationId") .HasColumnType("int"); @@ -827,24 +830,6 @@ namespace Moonlight.App.Database.Migrations b.ToTable("Websites"); }); - modelBuilder.Entity("Moonlight.App.Models.Misc.CleanupException", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - b.Property("Note") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("ServerId") - .HasColumnType("int"); - - b.HasKey("Id"); - - b.ToTable("CleanupExceptions"); - }); - modelBuilder.Entity("Moonlight.App.Database.Entities.Database", b => { b.HasOne("Moonlight.App.Database.Entities.AaPanel", "AaPanel") diff --git a/Moonlight/App/Models/Misc/CleanupException.cs b/Moonlight/App/Models/Misc/CleanupException.cs deleted file mode 100644 index 83dcba77..00000000 --- a/Moonlight/App/Models/Misc/CleanupException.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Moonlight.App.Models.Misc; - -public class CleanupException -{ - public int Id { get; set; } - public int ServerId { get; set; } - public string Note { get; set; } -} \ No newline at end of file diff --git a/Moonlight/App/Repositories/CleanupExceptionRepository.cs b/Moonlight/App/Repositories/CleanupExceptionRepository.cs deleted file mode 100644 index 9cfd0b28..00000000 --- a/Moonlight/App/Repositories/CleanupExceptionRepository.cs +++ /dev/null @@ -1,44 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using Moonlight.App.Database; -using Moonlight.App.Models.Misc; - -namespace Moonlight.App.Repositories; - -public class CleanupExceptionRepository : IDisposable -{ - private readonly DataContext DataContext; - - public CleanupExceptionRepository(DataContext dataContext) - { - DataContext = dataContext; - } - - public DbSet Get() - { - return DataContext.CleanupExceptions; - } - - public CleanupException Add(CleanupException cleanupException) - { - var x = DataContext.CleanupExceptions.Add(cleanupException).Entity; - DataContext.SaveChanges(); - return x; - } - - public void Update(CleanupException cleanupException) - { - DataContext.CleanupExceptions.Update(cleanupException); - DataContext.SaveChanges(); - } - - public void Delete(CleanupException cleanupException) - { - DataContext.CleanupExceptions.Remove(cleanupException); - DataContext.SaveChanges(); - } - - public void Dispose() - { - DataContext.Dispose(); - } -} \ No newline at end of file diff --git a/Moonlight/App/Services/CleanupService.cs b/Moonlight/App/Services/CleanupService.cs index ef500550..692ddf7b 100644 --- a/Moonlight/App/Services/CleanupService.cs +++ b/Moonlight/App/Services/CleanupService.cs @@ -104,7 +104,6 @@ public class CleanupService // Get repos from dependency injection var serverRepository = scope.ServiceProvider.GetRequiredService(); var nodeRepository = scope.ServiceProvider.GetRequiredService(); - var cleanupExceptionRepository = scope.ServiceProvider.GetRequiredService(); var nodeService = scope.ServiceProvider.GetRequiredService(); var imageRepo = scope.ServiceProvider.GetRequiredService(); var serverService = scope.ServiceProvider.GetRequiredService(); @@ -114,7 +113,7 @@ public class CleanupService .Include(x => x.Image) .ToArray(); var nodes = nodeRepository.Get().ToArray(); - var exceptions = cleanupExceptionRepository.Get().ToArray(); + var exceptions = servers.Where(x => x.IsCleanupException); var images = imageRepo.Get().ToArray(); var nodeCount = nodes.Count(); @@ -228,7 +227,7 @@ public class CleanupService var players = GetPlayers(serverData.Node, serverData.MainAllocation); var stats = await serverService.GetDetails(server); - var exception = exceptions.FirstOrDefault(x => x.ServerId == server.Id) != null; + var exception = exceptions.FirstOrDefault(x => x.Id == server.Id) != null; if (stats != null) { diff --git a/Moonlight/Moonlight.csproj b/Moonlight/Moonlight.csproj index fa894e5e..07c3a9f1 100644 --- a/Moonlight/Moonlight.csproj +++ b/Moonlight/Moonlight.csproj @@ -58,6 +58,8 @@ <_ContentIncludedByDefault Remove="wwwroot\css\site.css" /> <_ContentIncludedByDefault Remove="Shared\Components\Tables\Column.razor" /> <_ContentIncludedByDefault Remove="Shared\Components\Tables\Table.razor" /> + <_ContentIncludedByDefault Remove="Shared\Views\Admin\Servers\Cleanup\Exceptions\Add.razor" /> + <_ContentIncludedByDefault Remove="Shared\Views\Admin\Servers\Cleanup\Exceptions\Edit.razor" /> diff --git a/Moonlight/Program.cs b/Moonlight/Program.cs index f7fa1df6..8dc94299 100644 --- a/Moonlight/Program.cs +++ b/Moonlight/Program.cs @@ -65,7 +65,6 @@ 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/Views/Admin/Servers/Cleanup.razor b/Moonlight/Shared/Views/Admin/Servers/Cleanup/Index.razor similarity index 90% rename from Moonlight/Shared/Views/Admin/Servers/Cleanup.razor rename to Moonlight/Shared/Views/Admin/Servers/Cleanup/Index.razor index 5215224c..99468918 100644 --- a/Moonlight/Shared/Views/Admin/Servers/Cleanup.razor +++ b/Moonlight/Shared/Views/Admin/Servers/Cleanup/Index.razor @@ -10,7 +10,9 @@ @implements IDisposable -Cleanup + + Cleanup +
@@ -25,8 +27,12 @@
@(CleanupService.ServersCleaned)
- Servers - stopped + + Servers + + + stopped +
@@ -43,8 +49,12 @@
@(CleanupService.CleanupsPerformed)
- Cleanups - executed + + Cleanups + + + executed +
@@ -61,8 +71,12 @@
@(CleanupService.ServersRunning)
- Used clanup - Servers + + Used clanup + + + Servers +
@@ -142,6 +156,7 @@ @code { + protected async override Task OnAfterRenderAsync(bool firstRender) { if (firstRender) diff --git a/Moonlight/Shared/Views/Admin/Servers/Edit.razor b/Moonlight/Shared/Views/Admin/Servers/Edit.razor index 9309b419..b3fc0b9a 100644 --- a/Moonlight/Shared/Views/Admin/Servers/Edit.razor +++ b/Moonlight/Shared/Views/Admin/Servers/Edit.razor @@ -111,6 +111,10 @@ } + +
diff --git a/Moonlight/resources/lang/de_de.lang b/Moonlight/resources/lang/de_de.lang index 7e426f46..5844d449 100644 --- a/Moonlight/resources/lang/de_de.lang +++ b/Moonlight/resources/lang/de_de.lang @@ -427,3 +427,5 @@ Cleanup finished. Duration: 0.14 Minutes;Cleanup finished. Duration: 0.14 Minute Cleanup finished. Duration: 0.09 Minutes;Cleanup finished. Duration: 0.09 Minutes Cleanup finished. Duration: 0.1 Minutes;Cleanup finished. Duration: 0.1 Minutes Cleanup finished. Duration: 0.11 Minutes;Cleanup finished. Duration: 0.11 Minutes +Cleanup Exceptions;Cleanup Exceptions +Server not found;Server not found From 1989f832ccc9fdb4eee5fa6d5167694b64e247b4 Mon Sep 17 00:00:00 2001 From: Daniel Balk <67603460+Daniel-Balk@users.noreply.github.com> Date: Mon, 3 Apr 2023 00:16:30 +0200 Subject: [PATCH 4/4] Update Index.razor --- Moonlight/Shared/Views/Admin/Servers/Cleanup/Index.razor | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moonlight/Shared/Views/Admin/Servers/Cleanup/Index.razor b/Moonlight/Shared/Views/Admin/Servers/Cleanup/Index.razor index 99468918..501917bd 100644 --- a/Moonlight/Shared/Views/Admin/Servers/Cleanup/Index.razor +++ b/Moonlight/Shared/Views/Admin/Servers/Cleanup/Index.razor @@ -87,7 +87,7 @@
- +