diff --git a/.gitignore b/.gitignore index 4ec99a8..7487adb 100644 --- a/.gitignore +++ b/.gitignore @@ -38,4 +38,6 @@ _UpgradeReport_Files/ Thumbs.db Desktop.ini .DS_Store -.idea/.idea.Moonlight/.idea/discord.xml \ No newline at end of file +.idea/.idea.Moonlight/.idea/discord.xml + +storage/ \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Moonlight/App/Database/DataContext.cs b/Moonlight/App/Database/DataContext.cs index 906ce00..dc3e77d 100644 --- a/Moonlight/App/Database/DataContext.cs +++ b/Moonlight/App/Database/DataContext.cs @@ -18,7 +18,6 @@ public class DataContext : DbContext public DbSet DockerImages { get; set; } public DbSet Images { get; set; } - public DbSet ImageTags { get; set; } public DbSet ImageVariables { get; set; } public DbSet Nodes { get; set; } public DbSet NodeAllocations { get; set; } @@ -39,6 +38,10 @@ public class DataContext : DbContext public DbSet NotificationActions { get; set; } public DbSet DdosAttacks { get; set; } public DbSet Subscriptions { get; set; } + public DbSet PleskServers { get; set; } + public DbSet Websites { get; set; } + public DbSet Statistics { get; set; } + public DbSet NewsEntries { get; set; } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { diff --git a/Moonlight/App/Database/Entities/NewsEntry.cs b/Moonlight/App/Database/Entities/NewsEntry.cs new file mode 100644 index 0000000..d40fb26 --- /dev/null +++ b/Moonlight/App/Database/Entities/NewsEntry.cs @@ -0,0 +1,10 @@ +namespace Moonlight.App.Database.Entities; + +public class NewsEntry +{ + public int Id { get; set; } + + public DateTime Date { get; set; } + public string Title { get; set; } + public string Markdown { get; set; } +} \ No newline at end of file diff --git a/Moonlight/App/Database/Entities/ImageTag.cs b/Moonlight/App/Database/Entities/PleskServer.cs similarity index 51% rename from Moonlight/App/Database/Entities/ImageTag.cs rename to Moonlight/App/Database/Entities/PleskServer.cs index 7f3063f..a4cd630 100644 --- a/Moonlight/App/Database/Entities/ImageTag.cs +++ b/Moonlight/App/Database/Entities/PleskServer.cs @@ -1,7 +1,9 @@ namespace Moonlight.App.Database.Entities; -public class ImageTag +public class PleskServer { public int Id { get; set; } public string Name { get; set; } = ""; + public string ApiUrl { get; set; } = ""; + public string ApiKey { get; set; } = ""; } \ No newline at end of file diff --git a/Moonlight/App/Database/Entities/Server.cs b/Moonlight/App/Database/Entities/Server.cs index cad9ced..aa55d81 100644 --- a/Moonlight/App/Database/Entities/Server.cs +++ b/Moonlight/App/Database/Entities/Server.cs @@ -17,7 +17,7 @@ public class Server public List Variables { get; set; } = new(); public List Backups { get; set; } = new(); public List Allocations { get; set; } = new(); - public NodeAllocation MainAllocation { get; set; } = null!; + public NodeAllocation? MainAllocation { get; set; } = null; public Node Node { get; set; } = null!; public User Owner { get; set; } = null!; public bool IsCleanupException { get; set; } = false; diff --git a/Moonlight/App/Database/Entities/StatisticsData.cs b/Moonlight/App/Database/Entities/StatisticsData.cs new file mode 100644 index 0000000..64f8ea7 --- /dev/null +++ b/Moonlight/App/Database/Entities/StatisticsData.cs @@ -0,0 +1,12 @@ +namespace Moonlight.App.Database.Entities; + +public class StatisticsData +{ + public int Id { get; set; } + + public string Chart { get; set; } + + public double Value { get; set; } + + public DateTime Date { get; set; } +} \ No newline at end of file diff --git a/Moonlight/App/Database/Entities/Website.cs b/Moonlight/App/Database/Entities/Website.cs new file mode 100644 index 0000000..56e9fd1 --- /dev/null +++ b/Moonlight/App/Database/Entities/Website.cs @@ -0,0 +1,12 @@ +namespace Moonlight.App.Database.Entities; + +public class Website +{ + public int Id { get; set; } + public string BaseDomain { get; set; } = ""; + public int PleskId { get; set; } + public PleskServer PleskServer { get; set; } + public User Owner { get; set; } + public string FtpLogin { get; set; } = ""; + public string FtpPassword { get; set; } = ""; +} \ No newline at end of file diff --git a/Moonlight/App/Database/Migrations/20230404181522_AddedPleskAndWebsiteModels.Designer.cs b/Moonlight/App/Database/Migrations/20230404181522_AddedPleskAndWebsiteModels.Designer.cs new file mode 100644 index 0000000..c835576 --- /dev/null +++ b/Moonlight/App/Database/Migrations/20230404181522_AddedPleskAndWebsiteModels.Designer.cs @@ -0,0 +1,946 @@ +// +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("20230404181522_AddedPleskAndWebsiteModels")] + partial class AddedPleskAndWebsiteModels + { + /// + 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.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.PleskServer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("ApiKey") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ApiUrl") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("PleskServers"); + }); + + 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("LimitsJson") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("Subscriptions"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.SupportMessage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Answer") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsQuestion") + .HasColumnType("tinyint(1)"); + + b.Property("IsSupport") + .HasColumnType("tinyint(1)"); + + b.Property("IsSystem") + .HasColumnType("tinyint(1)"); + + b.Property("Message") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("RecipientId") + .HasColumnType("int"); + + b.Property("SenderId") + .HasColumnType("int"); + + b.Property("Type") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("RecipientId"); + + b.HasIndex("SenderId"); + + b.ToTable("SupportMessages"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Address") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Admin") + .HasColumnType("tinyint(1)"); + + b.Property("City") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Country") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CurrentSubscriptionId") + .HasColumnType("int"); + + b.Property("DiscordId") + .HasColumnType("bigint"); + + b.Property("Email") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("FirstName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("LastName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Password") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("State") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("SubscriptionDuration") + .HasColumnType("int"); + + b.Property("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("CurrentSubscriptionId"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Website", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("BaseDomain") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("OwnerId") + .HasColumnType("int"); + + b.Property("PleskId") + .HasColumnType("int"); + + b.Property("PleskServerId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("OwnerId"); + + b.HasIndex("PleskServerId"); + + b.ToTable("Websites"); + }); + + 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.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", "CurrentSubscription") + .WithMany() + .HasForeignKey("CurrentSubscriptionId"); + + b.Navigation("CurrentSubscription"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Website", b => + { + b.HasOne("Moonlight.App.Database.Entities.User", "Owner") + .WithMany() + .HasForeignKey("OwnerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Moonlight.App.Database.Entities.PleskServer", "PleskServer") + .WithMany() + .HasForeignKey("PleskServerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Owner"); + + b.Navigation("PleskServer"); + }); + + 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"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Moonlight/App/Database/Migrations/20230404181522_AddedPleskAndWebsiteModels.cs b/Moonlight/App/Database/Migrations/20230404181522_AddedPleskAndWebsiteModels.cs new file mode 100644 index 0000000..4c8834f --- /dev/null +++ b/Moonlight/App/Database/Migrations/20230404181522_AddedPleskAndWebsiteModels.cs @@ -0,0 +1,84 @@ +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Moonlight.App.Database.Migrations +{ + /// + public partial class AddedPleskAndWebsiteModels : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "PleskServers", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + Name = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ApiUrl = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ApiKey = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK_PleskServers", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "Websites", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + BaseDomain = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + PleskId = table.Column(type: "int", nullable: false), + PleskServerId = table.Column(type: "int", nullable: false), + OwnerId = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Websites", x => x.Id); + table.ForeignKey( + name: "FK_Websites_PleskServers_PleskServerId", + column: x => x.PleskServerId, + principalTable: "PleskServers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_Websites_Users_OwnerId", + column: x => x.OwnerId, + principalTable: "Users", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateIndex( + name: "IX_Websites_OwnerId", + table: "Websites", + column: "OwnerId"); + + migrationBuilder.CreateIndex( + name: "IX_Websites_PleskServerId", + table: "Websites", + column: "PleskServerId"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "Websites"); + + migrationBuilder.DropTable( + name: "PleskServers"); + } + } +} diff --git a/Moonlight/App/Database/Migrations/20230405162507_UpdatedWebsiteModel.Designer.cs b/Moonlight/App/Database/Migrations/20230405162507_UpdatedWebsiteModel.Designer.cs new file mode 100644 index 0000000..5d2125b --- /dev/null +++ b/Moonlight/App/Database/Migrations/20230405162507_UpdatedWebsiteModel.Designer.cs @@ -0,0 +1,954 @@ +// +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("20230405162507_UpdatedWebsiteModel")] + partial class UpdatedWebsiteModel + { + /// + 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.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.PleskServer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("ApiKey") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ApiUrl") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("PleskServers"); + }); + + 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("LimitsJson") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("Subscriptions"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.SupportMessage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Answer") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsQuestion") + .HasColumnType("tinyint(1)"); + + b.Property("IsSupport") + .HasColumnType("tinyint(1)"); + + b.Property("IsSystem") + .HasColumnType("tinyint(1)"); + + b.Property("Message") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("RecipientId") + .HasColumnType("int"); + + b.Property("SenderId") + .HasColumnType("int"); + + b.Property("Type") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("RecipientId"); + + b.HasIndex("SenderId"); + + b.ToTable("SupportMessages"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Address") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Admin") + .HasColumnType("tinyint(1)"); + + b.Property("City") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Country") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CurrentSubscriptionId") + .HasColumnType("int"); + + b.Property("DiscordId") + .HasColumnType("bigint"); + + b.Property("Email") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("FirstName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("LastName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Password") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("State") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("SubscriptionDuration") + .HasColumnType("int"); + + b.Property("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("CurrentSubscriptionId"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Website", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("BaseDomain") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("FtpLogin") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("FtpPassword") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("OwnerId") + .HasColumnType("int"); + + b.Property("PleskId") + .HasColumnType("int"); + + b.Property("PleskServerId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("OwnerId"); + + b.HasIndex("PleskServerId"); + + b.ToTable("Websites"); + }); + + 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.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", "CurrentSubscription") + .WithMany() + .HasForeignKey("CurrentSubscriptionId"); + + b.Navigation("CurrentSubscription"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Website", b => + { + b.HasOne("Moonlight.App.Database.Entities.User", "Owner") + .WithMany() + .HasForeignKey("OwnerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Moonlight.App.Database.Entities.PleskServer", "PleskServer") + .WithMany() + .HasForeignKey("PleskServerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Owner"); + + b.Navigation("PleskServer"); + }); + + 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"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Moonlight/App/Database/Migrations/20230405162507_UpdatedWebsiteModel.cs b/Moonlight/App/Database/Migrations/20230405162507_UpdatedWebsiteModel.cs new file mode 100644 index 0000000..6085df0 --- /dev/null +++ b/Moonlight/App/Database/Migrations/20230405162507_UpdatedWebsiteModel.cs @@ -0,0 +1,40 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Moonlight.App.Database.Migrations +{ + /// + public partial class UpdatedWebsiteModel : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "FtpLogin", + table: "Websites", + type: "longtext", + nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.AddColumn( + name: "FtpPassword", + table: "Websites", + type: "longtext", + nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "FtpLogin", + table: "Websites"); + + migrationBuilder.DropColumn( + name: "FtpPassword", + table: "Websites"); + } + } +} diff --git a/Moonlight/App/Database/Migrations/20230406161820_AddedStatistics.Designer.cs b/Moonlight/App/Database/Migrations/20230406161820_AddedStatistics.Designer.cs new file mode 100644 index 0000000..6345ccb --- /dev/null +++ b/Moonlight/App/Database/Migrations/20230406161820_AddedStatistics.Designer.cs @@ -0,0 +1,894 @@ +// +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("20230406161820_AddedStatistics")] + partial class AddedStatistics + { + /// + 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.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.StatisticsData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Chart") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Value") + .HasColumnType("double"); + + b.HasKey("Id"); + + b.ToTable("Statistics"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Subscription", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Description") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("LimitsJson") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("Subscriptions"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.SupportMessage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Answer") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsQuestion") + .HasColumnType("tinyint(1)"); + + b.Property("IsSupport") + .HasColumnType("tinyint(1)"); + + b.Property("IsSystem") + .HasColumnType("tinyint(1)"); + + b.Property("Message") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("RecipientId") + .HasColumnType("int"); + + b.Property("SenderId") + .HasColumnType("int"); + + b.Property("Type") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("RecipientId"); + + b.HasIndex("SenderId"); + + b.ToTable("SupportMessages"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Address") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Admin") + .HasColumnType("tinyint(1)"); + + b.Property("City") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Country") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CurrentSubscriptionId") + .HasColumnType("int"); + + b.Property("DiscordId") + .HasColumnType("bigint"); + + b.Property("Email") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("FirstName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("LastName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Password") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("State") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("SubscriptionDuration") + .HasColumnType("int"); + + b.Property("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("CurrentSubscriptionId"); + + b.ToTable("Users"); + }); + + 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.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", "CurrentSubscription") + .WithMany() + .HasForeignKey("CurrentSubscriptionId"); + + b.Navigation("CurrentSubscription"); + }); + + 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"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Moonlight/App/Database/Migrations/20230406161820_AddedStatistics.cs b/Moonlight/App/Database/Migrations/20230406161820_AddedStatistics.cs new file mode 100644 index 0000000..b00cb9c --- /dev/null +++ b/Moonlight/App/Database/Migrations/20230406161820_AddedStatistics.cs @@ -0,0 +1,38 @@ +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Moonlight.App.Database.Migrations +{ + /// + public partial class AddedStatistics : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "Statistics", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + Chart = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Value = table.Column(type: "double", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Statistics", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "Statistics"); + } + } +} diff --git a/Moonlight/App/Database/Migrations/20230406182554_ChangedStatisticsModel.Designer.cs b/Moonlight/App/Database/Migrations/20230406182554_ChangedStatisticsModel.Designer.cs new file mode 100644 index 0000000..73544a6 --- /dev/null +++ b/Moonlight/App/Database/Migrations/20230406182554_ChangedStatisticsModel.Designer.cs @@ -0,0 +1,975 @@ +// +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("20230406182554_ChangedStatisticsModel")] + partial class ChangedStatisticsModel + { + /// + 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.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.PleskServer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("ApiKey") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ApiUrl") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("PleskServers"); + }); + + 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.StatisticsData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Chart") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Date") + .HasColumnType("datetime(6)"); + + b.Property("Value") + .HasColumnType("double"); + + b.HasKey("Id"); + + b.ToTable("Statistics"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Subscription", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Description") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("LimitsJson") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("Subscriptions"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.SupportMessage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Answer") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsQuestion") + .HasColumnType("tinyint(1)"); + + b.Property("IsSupport") + .HasColumnType("tinyint(1)"); + + b.Property("IsSystem") + .HasColumnType("tinyint(1)"); + + b.Property("Message") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("RecipientId") + .HasColumnType("int"); + + b.Property("SenderId") + .HasColumnType("int"); + + b.Property("Type") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("RecipientId"); + + b.HasIndex("SenderId"); + + b.ToTable("SupportMessages"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Address") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Admin") + .HasColumnType("tinyint(1)"); + + b.Property("City") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Country") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CurrentSubscriptionId") + .HasColumnType("int"); + + b.Property("DiscordId") + .HasColumnType("bigint"); + + b.Property("Email") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("FirstName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("LastName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Password") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("State") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("SubscriptionDuration") + .HasColumnType("int"); + + b.Property("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("CurrentSubscriptionId"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Website", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("BaseDomain") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("FtpLogin") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("FtpPassword") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("OwnerId") + .HasColumnType("int"); + + b.Property("PleskId") + .HasColumnType("int"); + + b.Property("PleskServerId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("OwnerId"); + + b.HasIndex("PleskServerId"); + + b.ToTable("Websites"); + }); + + 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.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", "CurrentSubscription") + .WithMany() + .HasForeignKey("CurrentSubscriptionId"); + + b.Navigation("CurrentSubscription"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Website", b => + { + b.HasOne("Moonlight.App.Database.Entities.User", "Owner") + .WithMany() + .HasForeignKey("OwnerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Moonlight.App.Database.Entities.PleskServer", "PleskServer") + .WithMany() + .HasForeignKey("PleskServerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Owner"); + + b.Navigation("PleskServer"); + }); + + 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"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Moonlight/App/Database/Migrations/20230406182554_ChangedStatisticsModel.cs b/Moonlight/App/Database/Migrations/20230406182554_ChangedStatisticsModel.cs new file mode 100644 index 0000000..24d619e --- /dev/null +++ b/Moonlight/App/Database/Migrations/20230406182554_ChangedStatisticsModel.cs @@ -0,0 +1,30 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Moonlight.App.Database.Migrations +{ + /// + public partial class ChangedStatisticsModel : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "Date", + table: "Statistics", + type: "datetime(6)", + nullable: false, + defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "Date", + table: "Statistics"); + } + } +} diff --git a/Moonlight/App/Database/Migrations/20230412162710_NewsEntriesTableAdded.Designer.cs b/Moonlight/App/Database/Migrations/20230412162710_NewsEntriesTableAdded.Designer.cs new file mode 100644 index 0000000..bf04412 --- /dev/null +++ b/Moonlight/App/Database/Migrations/20230412162710_NewsEntriesTableAdded.Designer.cs @@ -0,0 +1,997 @@ +// +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("20230412162710_NewsEntriesTableAdded")] + partial class NewsEntriesTableAdded + { + /// + 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.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.NewsEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Date") + .HasColumnType("datetime(6)"); + + b.Property("Markdown") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Title") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("NewsEntries"); + }); + + 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.PleskServer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("ApiKey") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ApiUrl") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("PleskServers"); + }); + + 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.StatisticsData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Chart") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Date") + .HasColumnType("datetime(6)"); + + b.Property("Value") + .HasColumnType("double"); + + b.HasKey("Id"); + + b.ToTable("Statistics"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Subscription", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Description") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("LimitsJson") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("Subscriptions"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.SupportMessage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Answer") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsQuestion") + .HasColumnType("tinyint(1)"); + + b.Property("IsSupport") + .HasColumnType("tinyint(1)"); + + b.Property("IsSystem") + .HasColumnType("tinyint(1)"); + + b.Property("Message") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("RecipientId") + .HasColumnType("int"); + + b.Property("SenderId") + .HasColumnType("int"); + + b.Property("Type") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("RecipientId"); + + b.HasIndex("SenderId"); + + b.ToTable("SupportMessages"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Address") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Admin") + .HasColumnType("tinyint(1)"); + + b.Property("City") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Country") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CurrentSubscriptionId") + .HasColumnType("int"); + + b.Property("DiscordId") + .HasColumnType("bigint unsigned"); + + b.Property("Email") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("FirstName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("LastName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Password") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("State") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("SubscriptionDuration") + .HasColumnType("int"); + + b.Property("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("CurrentSubscriptionId"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Website", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("BaseDomain") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("FtpLogin") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("FtpPassword") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("OwnerId") + .HasColumnType("int"); + + b.Property("PleskId") + .HasColumnType("int"); + + b.Property("PleskServerId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("OwnerId"); + + b.HasIndex("PleskServerId"); + + b.ToTable("Websites"); + }); + + 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.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", "CurrentSubscription") + .WithMany() + .HasForeignKey("CurrentSubscriptionId"); + + b.Navigation("CurrentSubscription"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Website", b => + { + b.HasOne("Moonlight.App.Database.Entities.User", "Owner") + .WithMany() + .HasForeignKey("OwnerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Moonlight.App.Database.Entities.PleskServer", "PleskServer") + .WithMany() + .HasForeignKey("PleskServerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Owner"); + + b.Navigation("PleskServer"); + }); + + 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"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Moonlight/App/Database/Migrations/20230412162710_NewsEntriesTableAdded.cs b/Moonlight/App/Database/Migrations/20230412162710_NewsEntriesTableAdded.cs new file mode 100644 index 0000000..b35646a --- /dev/null +++ b/Moonlight/App/Database/Migrations/20230412162710_NewsEntriesTableAdded.cs @@ -0,0 +1,57 @@ +using System; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Moonlight.App.Database.Migrations +{ + /// + public partial class NewsEntriesTableAdded : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "DiscordId", + table: "Users", + type: "bigint unsigned", + nullable: false, + oldClrType: typeof(long), + oldType: "bigint"); + + migrationBuilder.CreateTable( + name: "NewsEntries", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + Date = table.Column(type: "datetime(6)", nullable: false), + Title = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Markdown = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK_NewsEntries", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "NewsEntries"); + + migrationBuilder.AlterColumn( + name: "DiscordId", + table: "Users", + type: "bigint", + nullable: false, + oldClrType: typeof(ulong), + oldType: "bigint unsigned"); + } + } +} diff --git a/Moonlight/App/Database/Migrations/20230413150426_MadeMainAllocationNullable.Designer.cs b/Moonlight/App/Database/Migrations/20230413150426_MadeMainAllocationNullable.Designer.cs new file mode 100644 index 0000000..0eb5220 --- /dev/null +++ b/Moonlight/App/Database/Migrations/20230413150426_MadeMainAllocationNullable.Designer.cs @@ -0,0 +1,995 @@ +// +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("20230413150426_MadeMainAllocationNullable")] + partial class MadeMainAllocationNullable + { + /// + 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.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.NewsEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Date") + .HasColumnType("datetime(6)"); + + b.Property("Markdown") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Title") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("NewsEntries"); + }); + + 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.PleskServer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("ApiKey") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ApiUrl") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("PleskServers"); + }); + + 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.StatisticsData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Chart") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Date") + .HasColumnType("datetime(6)"); + + b.Property("Value") + .HasColumnType("double"); + + b.HasKey("Id"); + + b.ToTable("Statistics"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Subscription", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Description") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("LimitsJson") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("Subscriptions"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.SupportMessage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Answer") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsQuestion") + .HasColumnType("tinyint(1)"); + + b.Property("IsSupport") + .HasColumnType("tinyint(1)"); + + b.Property("IsSystem") + .HasColumnType("tinyint(1)"); + + b.Property("Message") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("RecipientId") + .HasColumnType("int"); + + b.Property("SenderId") + .HasColumnType("int"); + + b.Property("Type") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("RecipientId"); + + b.HasIndex("SenderId"); + + b.ToTable("SupportMessages"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Address") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Admin") + .HasColumnType("tinyint(1)"); + + b.Property("City") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Country") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CurrentSubscriptionId") + .HasColumnType("int"); + + b.Property("DiscordId") + .HasColumnType("bigint unsigned"); + + b.Property("Email") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("FirstName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("LastName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Password") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("State") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("SubscriptionDuration") + .HasColumnType("int"); + + b.Property("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("CurrentSubscriptionId"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Website", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("BaseDomain") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("FtpLogin") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("FtpPassword") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("OwnerId") + .HasColumnType("int"); + + b.Property("PleskId") + .HasColumnType("int"); + + b.Property("PleskServerId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("OwnerId"); + + b.HasIndex("PleskServerId"); + + b.ToTable("Websites"); + }); + + 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"); + + 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.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", "CurrentSubscription") + .WithMany() + .HasForeignKey("CurrentSubscriptionId"); + + b.Navigation("CurrentSubscription"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Website", b => + { + b.HasOne("Moonlight.App.Database.Entities.User", "Owner") + .WithMany() + .HasForeignKey("OwnerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Moonlight.App.Database.Entities.PleskServer", "PleskServer") + .WithMany() + .HasForeignKey("PleskServerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Owner"); + + b.Navigation("PleskServer"); + }); + + 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"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Moonlight/App/Database/Migrations/20230413150426_MadeMainAllocationNullable.cs b/Moonlight/App/Database/Migrations/20230413150426_MadeMainAllocationNullable.cs new file mode 100644 index 0000000..e6d79d8 --- /dev/null +++ b/Moonlight/App/Database/Migrations/20230413150426_MadeMainAllocationNullable.cs @@ -0,0 +1,59 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Moonlight.App.Database.Migrations +{ + /// + public partial class MadeMainAllocationNullable : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_Servers_NodeAllocations_MainAllocationId", + table: "Servers"); + + migrationBuilder.AlterColumn( + name: "MainAllocationId", + table: "Servers", + type: "int", + nullable: true, + oldClrType: typeof(int), + oldType: "int"); + + migrationBuilder.AddForeignKey( + name: "FK_Servers_NodeAllocations_MainAllocationId", + table: "Servers", + column: "MainAllocationId", + principalTable: "NodeAllocations", + principalColumn: "Id"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_Servers_NodeAllocations_MainAllocationId", + table: "Servers"); + + migrationBuilder.AlterColumn( + name: "MainAllocationId", + table: "Servers", + type: "int", + nullable: false, + defaultValue: 0, + oldClrType: typeof(int), + oldType: "int", + oldNullable: true); + + migrationBuilder.AddForeignKey( + name: "FK_Servers_NodeAllocations_MainAllocationId", + table: "Servers", + column: "MainAllocationId", + principalTable: "NodeAllocations", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + } + } +} diff --git a/Moonlight/App/Database/Migrations/20230414110513_RemovedImageTags.Designer.cs b/Moonlight/App/Database/Migrations/20230414110513_RemovedImageTags.Designer.cs new file mode 100644 index 0000000..fafeeaf --- /dev/null +++ b/Moonlight/App/Database/Migrations/20230414110513_RemovedImageTags.Designer.cs @@ -0,0 +1,980 @@ +// +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("20230414110513_RemovedImageTags")] + partial class RemovedImageTags + { + /// + 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.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.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.NewsEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Date") + .HasColumnType("datetime(6)"); + + b.Property("Markdown") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Title") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("NewsEntries"); + }); + + 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.PleskServer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("ApiKey") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ApiUrl") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("PleskServers"); + }); + + 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.StatisticsData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Chart") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Date") + .HasColumnType("datetime(6)"); + + b.Property("Value") + .HasColumnType("double"); + + b.HasKey("Id"); + + b.ToTable("Statistics"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Subscription", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Description") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("LimitsJson") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("Subscriptions"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.SupportMessage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Answer") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsQuestion") + .HasColumnType("tinyint(1)"); + + b.Property("IsSupport") + .HasColumnType("tinyint(1)"); + + b.Property("IsSystem") + .HasColumnType("tinyint(1)"); + + b.Property("Message") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("RecipientId") + .HasColumnType("int"); + + b.Property("SenderId") + .HasColumnType("int"); + + b.Property("Type") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("RecipientId"); + + b.HasIndex("SenderId"); + + b.ToTable("SupportMessages"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Address") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Admin") + .HasColumnType("tinyint(1)"); + + b.Property("City") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Country") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CurrentSubscriptionId") + .HasColumnType("int"); + + b.Property("DiscordId") + .HasColumnType("bigint unsigned"); + + b.Property("Email") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("FirstName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("LastName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Password") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("State") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("SubscriptionDuration") + .HasColumnType("int"); + + b.Property("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("CurrentSubscriptionId"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Website", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("BaseDomain") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("FtpLogin") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("FtpPassword") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("OwnerId") + .HasColumnType("int"); + + b.Property("PleskId") + .HasColumnType("int"); + + b.Property("PleskServerId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("OwnerId"); + + b.HasIndex("PleskServerId"); + + b.ToTable("Websites"); + }); + + 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"); + + 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.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", "CurrentSubscription") + .WithMany() + .HasForeignKey("CurrentSubscriptionId"); + + b.Navigation("CurrentSubscription"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Website", b => + { + b.HasOne("Moonlight.App.Database.Entities.User", "Owner") + .WithMany() + .HasForeignKey("OwnerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Moonlight.App.Database.Entities.PleskServer", "PleskServer") + .WithMany() + .HasForeignKey("PleskServerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Owner"); + + b.Navigation("PleskServer"); + }); + + 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"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Moonlight/App/Database/Migrations/20230414110513_RemovedImageTags.cs b/Moonlight/App/Database/Migrations/20230414110513_RemovedImageTags.cs new file mode 100644 index 0000000..2001c98 --- /dev/null +++ b/Moonlight/App/Database/Migrations/20230414110513_RemovedImageTags.cs @@ -0,0 +1,37 @@ +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Moonlight.App.Database.Migrations +{ + /// + public partial class RemovedImageTags : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "ImageTags"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "ImageTags", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + Name = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK_ImageTags", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + } + } +} diff --git a/Moonlight/App/Database/Migrations/DataContextModelSnapshot.cs b/Moonlight/App/Database/Migrations/DataContextModelSnapshot.cs index 65062e1..7a0da1a 100644 --- a/Moonlight/App/Database/Migrations/DataContextModelSnapshot.cs +++ b/Moonlight/App/Database/Migrations/DataContextModelSnapshot.cs @@ -153,21 +153,6 @@ namespace Moonlight.App.Database.Migrations 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") @@ -296,6 +281,28 @@ namespace Moonlight.App.Database.Migrations b.ToTable("SecurityLog"); }); + modelBuilder.Entity("Moonlight.App.Database.Entities.NewsEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Date") + .HasColumnType("datetime(6)"); + + b.Property("Markdown") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Title") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("NewsEntries"); + }); + modelBuilder.Entity("Moonlight.App.Database.Entities.Node", b => { b.Property("Id") @@ -395,6 +402,29 @@ namespace Moonlight.App.Database.Migrations b.ToTable("NotificationClients"); }); + modelBuilder.Entity("Moonlight.App.Database.Entities.PleskServer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("ApiKey") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ApiUrl") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("PleskServers"); + }); + modelBuilder.Entity("Moonlight.App.Database.Entities.Revoke", b => { b.Property("Id") @@ -434,7 +464,7 @@ namespace Moonlight.App.Database.Migrations b.Property("IsCleanupException") .HasColumnType("tinyint(1)"); - b.Property("MainAllocationId") + b.Property("MainAllocationId") .HasColumnType("int"); b.Property("Memory") @@ -548,6 +578,27 @@ namespace Moonlight.App.Database.Migrations b.ToTable("SharedDomains"); }); + modelBuilder.Entity("Moonlight.App.Database.Entities.StatisticsData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Chart") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Date") + .HasColumnType("datetime(6)"); + + b.Property("Value") + .HasColumnType("double"); + + b.HasKey("Id"); + + b.ToTable("Statistics"); + }); + modelBuilder.Entity("Moonlight.App.Database.Entities.Subscription", b => { b.Property("Id") @@ -642,8 +693,8 @@ namespace Moonlight.App.Database.Migrations b.Property("CurrentSubscriptionId") .HasColumnType("int"); - b.Property("DiscordId") - .HasColumnType("bigint"); + b.Property("DiscordId") + .HasColumnType("bigint unsigned"); b.Property("Email") .IsRequired() @@ -697,6 +748,42 @@ namespace Moonlight.App.Database.Migrations b.ToTable("Users"); }); + modelBuilder.Entity("Moonlight.App.Database.Entities.Website", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("BaseDomain") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("FtpLogin") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("FtpPassword") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("OwnerId") + .HasColumnType("int"); + + b.Property("PleskId") + .HasColumnType("int"); + + b.Property("PleskServerId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("OwnerId"); + + b.HasIndex("PleskServerId"); + + b.ToTable("Websites"); + }); + modelBuilder.Entity("Moonlight.App.Database.Entities.DdosAttack", b => { b.HasOne("Moonlight.App.Database.Entities.Node", "Node") @@ -784,9 +871,7 @@ namespace Moonlight.App.Database.Migrations b.HasOne("Moonlight.App.Database.Entities.NodeAllocation", "MainAllocation") .WithMany() - .HasForeignKey("MainAllocationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); + .HasForeignKey("MainAllocationId"); b.HasOne("Moonlight.App.Database.Entities.Node", "Node") .WithMany() @@ -847,6 +932,25 @@ namespace Moonlight.App.Database.Migrations b.Navigation("CurrentSubscription"); }); + modelBuilder.Entity("Moonlight.App.Database.Entities.Website", b => + { + b.HasOne("Moonlight.App.Database.Entities.User", "Owner") + .WithMany() + .HasForeignKey("OwnerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Moonlight.App.Database.Entities.PleskServer", "PleskServer") + .WithMany() + .HasForeignKey("PleskServerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Owner"); + + b.Navigation("PleskServer"); + }); + modelBuilder.Entity("Moonlight.App.Database.Entities.Image", b => { b.Navigation("DockerImages"); diff --git a/Moonlight/App/Exceptions/DaemonException.cs b/Moonlight/App/Exceptions/DaemonException.cs new file mode 100644 index 0000000..05070d4 --- /dev/null +++ b/Moonlight/App/Exceptions/DaemonException.cs @@ -0,0 +1,32 @@ +using System.Runtime.Serialization; + +namespace Moonlight.App.Exceptions; + +[Serializable] +public class DaemonException : Exception +{ + public int StatusCode { private get; set; } + + public DaemonException() + { + } + + public DaemonException(string message, int statusCode) : base(message) + { + StatusCode = statusCode; + } + + public DaemonException(string message) : base(message) + { + } + + public DaemonException(string message, Exception inner) : base(message, inner) + { + } + + protected DaemonException( + SerializationInfo info, + StreamingContext context) : base(info, context) + { + } +} \ No newline at end of file diff --git a/Moonlight/App/Exceptions/PleskException.cs b/Moonlight/App/Exceptions/PleskException.cs new file mode 100644 index 0000000..e29676f --- /dev/null +++ b/Moonlight/App/Exceptions/PleskException.cs @@ -0,0 +1,32 @@ +using System.Runtime.Serialization; + +namespace Moonlight.App.Exceptions; + +[Serializable] +public class PleskException : Exception +{ + public int StatusCode { private get; set; } + + public PleskException() + { + } + + public PleskException(string message, int statusCode) : base(message) + { + StatusCode = statusCode; + } + + public PleskException(string message) : base(message) + { + } + + public PleskException(string message, Exception inner) : base(message, inner) + { + } + + protected PleskException( + SerializationInfo info, + StreamingContext context) : base(info, context) + { + } +} \ No newline at end of file diff --git a/Moonlight/App/Helpers/DaemonApiHelper.cs b/Moonlight/App/Helpers/DaemonApiHelper.cs index 45d91a2..b9ffdcc 100644 --- a/Moonlight/App/Helpers/DaemonApiHelper.cs +++ b/Moonlight/App/Helpers/DaemonApiHelper.cs @@ -16,11 +16,13 @@ public class DaemonApiHelper private string GetApiUrl(Node node) { + /* SSL not implemented in moonlight daemon if(node.Ssl) return $"https://{node.Fqdn}:{node.MoonlightDaemonPort}/"; else - return $"http://{node.Fqdn}:{node.MoonlightDaemonPort}/"; - //return $"https://{node.Fqdn}:{node.HttpPort}/"; + return $"http://{node.Fqdn}:{node.MoonlightDaemonPort}/";*/ + + return $"http://{node.Fqdn}:{node.MoonlightDaemonPort}/"; } public async Task Get(Node node, string resource) @@ -37,7 +39,7 @@ public class DaemonApiHelper { if (response.StatusCode != 0) { - throw new WingsException( + throw new DaemonException( $"An error occured: ({response.StatusCode}) {response.Content}", (int)response.StatusCode ); diff --git a/Moonlight/App/Helpers/Files/FtpFileAccess.cs b/Moonlight/App/Helpers/Files/FtpFileAccess.cs new file mode 100644 index 0000000..df17025 --- /dev/null +++ b/Moonlight/App/Helpers/Files/FtpFileAccess.cs @@ -0,0 +1,182 @@ +using System.Net; +using System.Text; +using FluentFTP; +using Moonlight.App.Exceptions; + +namespace Moonlight.App.Helpers.Files; + +public class FtpFileAccess : FileAccess +{ + private string FtpHost, FtpUser, FtpPassword; + private int FtpPort; + + private AsyncFtpClient Client; + + public FtpFileAccess(string ftpHost, int ftpPort, string ftpUser, string ftpPassword) + { + FtpHost = ftpHost; + FtpPort = ftpPort; + FtpUser = ftpUser; + FtpPassword = ftpPassword; + + Client = new AsyncFtpClient(FtpHost, FtpUser, FtpPassword, FtpPort); + } + + private async Task EnsureConnect() + { + if (!Client.IsConnected) + await Client.AutoConnect(); + } + + public override async Task Ls() + { + await EnsureConnect(); + + var x = new List(); + + foreach (FtpListItem item in (await Client.GetListing(CurrentPath)).OrderBy(x => x.Type + " " + x.Name)) + { + long size = 0; + + if (item.Type == FtpObjectType.File) + { + size = await Client.GetFileSize(item.FullName); + } + + x.Add(new() + { + Name = item.Name, + Size = size, + IsFile = item.Type == FtpObjectType.File, + }); + } + + return x.ToArray(); + } + + public override Task Cd(string dir) + { + var x = Path.Combine(CurrentPath, dir).Replace("\\", "/") + "/"; + x = x.Replace("//", "/"); + CurrentPath = x; + + return Task.CompletedTask; + } + + public override Task Up() + { + CurrentPath = Path.GetFullPath(Path.Combine(CurrentPath, "..")).Replace("\\", "/").Replace("C:", ""); + return Task.CompletedTask; + } + + public override Task SetDir(string dir) + { + CurrentPath = dir; + return Task.CompletedTask; + } + + public override async Task Read(FileData fileData) + { + await EnsureConnect(); + + var s = new MemoryStream(8 * 1024 * 1204); //TODO: Add config option + await Client.DownloadStream(s, CurrentPath.TrimEnd('/') + "/" + fileData.Name); + var data = s.ToArray(); + await s.DisposeAsync(); + var str = Encoding.UTF8.GetString(data); + return str; + } + + public override async Task Write(FileData fileData, string content) + { + await EnsureConnect(); + + var s = new MemoryStream(8 * 1024 * 1204); //TODO: Add config option + s.Write(Encoding.UTF8.GetBytes(content)); + s.Position = 0; + await Client.UploadStream(s, CurrentPath.TrimEnd('/') + "/" + fileData.Name, FtpRemoteExists.Overwrite); + await s.DisposeAsync(); + } + + public override async Task Upload(string name, Stream dataStream, Action? progressUpdated = null) + { + await EnsureConnect(); + + IProgress progress = new Progress(x => + { + progressUpdated?.Invoke((int)x.Progress); + }); + await Client.UploadStream(dataStream, CurrentPath.TrimEnd('/') + "/" + name, FtpRemoteExists.Overwrite, false, progress); + } + + public override async Task MkDir(string name) + { + await EnsureConnect(); + + await Client.CreateDirectory(CurrentPath.TrimEnd('/') + "/" + name + "/"); + } + + public override Task Pwd() + { + return Task.FromResult(CurrentPath); + } + + public override Task DownloadUrl(FileData fileData) + { + throw new NotImplementedException(); + } + + public override async Task DownloadStream(FileData fileData) + { + await EnsureConnect(); + + var s = new MemoryStream(8 * 1024 * 1204); //TODO: Add config option + var downloaded = await Client.DownloadStream(s, CurrentPath.TrimEnd('/') + "/" + fileData.Name); + + if (!downloaded) + throw new DisplayException("Unable to download file"); + + return s; + } + + public override async Task Delete(FileData fileData) + { + await EnsureConnect(); + + if (fileData.IsFile) + await Client.DeleteFile(CurrentPath.TrimEnd('/') + "/" + fileData.Name); + else + await Client.DeleteDirectory(CurrentPath.TrimEnd('/') + "/" + fileData.Name); + } + + public override async Task Move(FileData fileData, string newPath) + { + await EnsureConnect(); + + if (fileData.IsFile) + await Client.MoveFile(CurrentPath.TrimEnd('/') + "/" + fileData.Name, newPath); + else + await Client.MoveDirectory(CurrentPath.TrimEnd('/') + "/" + fileData.Name, newPath); + } + + public override Task Compress(params FileData[] files) + { + throw new NotImplementedException(); + } + + public override Task Decompress(FileData fileData) + { + throw new NotImplementedException(); + } + + public override Task GetLaunchUrl() + { + return Task.FromResult( + $"ftp://{FtpUser}:{FtpPassword}@{FtpHost}:{FtpPort}/"); + } + + public override object Clone() + { + return new FtpFileAccess(FtpHost, FtpPort, FtpUser, FtpPassword); + } +} \ No newline at end of file diff --git a/Moonlight/App/Helpers/Files/HostFileAccess.cs b/Moonlight/App/Helpers/Files/HostFileAccess.cs new file mode 100644 index 0000000..0c425a8 --- /dev/null +++ b/Moonlight/App/Helpers/Files/HostFileAccess.cs @@ -0,0 +1,143 @@ +using System.IO.Compression; + +namespace Moonlight.App.Helpers.Files; + +public class HostFileAccess : FileAccess +{ + private string BasePath; + + public HostFileAccess(string basePath) + { + BasePath = basePath; + } + private string currhp => BasePath.TrimEnd('/') + "/" + CurrentPath.TrimStart('/').TrimEnd('/') + "/"; + public override async Task Ls() + { + var x = new List(); + + foreach (var dir in Directory.GetDirectories(currhp)) + { + x.Add(new() + { + Name = dir.Remove(0, currhp.Length), + Size = 0, + IsFile = false, + }); + } + + foreach (var fn in Directory.GetFiles(currhp)) + { + x.Add(new() + { + Name = fn.Remove(0, currhp.Length), + Size = new FileInfo(fn).Length, + IsFile = true, + }); + } + + return x.ToArray(); + } + + public override Task Cd(string dir) + { + var x = Path.Combine(CurrentPath, dir).Replace("\\", "/") + "/"; + x = x.Replace("//", "/"); + CurrentPath = x; + + return Task.CompletedTask; + } + + public override Task Up() + { + CurrentPath = Path.GetFullPath(Path.Combine(CurrentPath, "..")).Replace("\\", "/").Replace("C:", ""); + return Task.CompletedTask; + } + + public override Task SetDir(string dir) + { + CurrentPath = dir; + return Task.CompletedTask; + } + + public override async Task Read(FileData fileData) + { + return await File.ReadAllTextAsync(currhp + fileData.Name); + } + + public override async Task Write(FileData fileData, string content) + { + await File.WriteAllTextAsync(currhp + fileData.Name, content); + } + + public override async Task Upload(string name, Stream dataStream, Action? progressUpdated = null) + { + var ms = new MemoryStream(); + await dataStream.CopyToAsync(ms); + var data = ms.ToArray(); + ms.Dispose(); + dataStream.Dispose(); + + await File.WriteAllBytesAsync(currhp + name, data); + } + + public override async Task MkDir(string name) + { + Directory.CreateDirectory(currhp + name + "/"); + } + + public override Task Pwd() + { + return Task.FromResult(CurrentPath); + } + + public override Task DownloadUrl(FileData fileData) + { + throw new NotImplementedException(); + } + + public override async Task DownloadStream(FileData fileData) + { + var s = new MemoryStream(8 * 1024 * 1204); //TODO: Add config option + + s.Write(File.ReadAllBytes(currhp + fileData.Name)); + s.Position = 0; + + return s; + } + + public override async Task Delete(FileData fileData) + { + if (fileData.IsFile) + File.Delete(currhp + fileData.Name); + else + Directory.Delete(currhp + fileData.Name); + } + + public override async Task Move(FileData fileData, string newPath) + { + if (fileData.IsFile) + File.Move(currhp + fileData.Name, newPath); + else + Directory.Move(currhp + fileData.Name, newPath); + } + + public override Task Compress(params FileData[] files) + { + throw new NotImplementedException(); + } + + public override Task Decompress(FileData fileData) + { + throw new NotImplementedException(); + } + + public override Task GetLaunchUrl() + { + throw new NotImplementedException(); + } + + public override object Clone() + { + return new HostFileAccess(BasePath); + } +} \ No newline at end of file diff --git a/Moonlight/App/Helpers/PathBuilder.cs b/Moonlight/App/Helpers/PathBuilder.cs new file mode 100644 index 0000000..0b3e98b --- /dev/null +++ b/Moonlight/App/Helpers/PathBuilder.cs @@ -0,0 +1,28 @@ +namespace Moonlight.App.Helpers; + +public class PathBuilder +{ + public static string Dir(params string[] parts) + { + var res = ""; + + foreach (var part in parts) + { + res += part + Path.DirectorySeparatorChar; + } + + return res; + } + + public static string File(params string[] parts) + { + var res = ""; + + foreach (var part in parts) + { + res += part + (part == parts.Last() ? "" : Path.DirectorySeparatorChar); + } + + return res; + } +} \ No newline at end of file diff --git a/Moonlight/App/Helpers/PleskApiHelper.cs b/Moonlight/App/Helpers/PleskApiHelper.cs new file mode 100644 index 0000000..87ea987 --- /dev/null +++ b/Moonlight/App/Helpers/PleskApiHelper.cs @@ -0,0 +1,220 @@ +using System.Text; +using Moonlight.App.Database.Entities; +using Moonlight.App.Exceptions; +using Newtonsoft.Json; +using RestSharp; + +namespace Moonlight.App.Helpers; + +public class PleskApiHelper +{ + private readonly RestClient Client; + + public PleskApiHelper() + { + Client = new(); + } + + public async Task Get(PleskServer server, string resource) + { + var request = CreateRequest(server, resource); + + request.Method = Method.Get; + + var response = await Client.ExecuteAsync(request); + + if (!response.IsSuccessful) + { + if (response.StatusCode != 0) + { + throw new PleskException( + $"An error occured: ({response.StatusCode}) {response.Content}", + (int)response.StatusCode + ); + } + else + { + throw new Exception($"An internal error occured: {response.ErrorMessage}"); + } + } + + return JsonConvert.DeserializeObject(response.Content!)!; + } + + public async Task GetRaw(PleskServer server, string resource) + { + var request = CreateRequest(server, resource); + + request.Method = Method.Get; + + var response = await Client.ExecuteAsync(request); + + if (!response.IsSuccessful) + { + if (response.StatusCode != 0) + { + throw new PleskException( + $"An error occured: ({response.StatusCode}) {response.Content}", + (int)response.StatusCode + ); + } + else + { + throw new Exception($"An internal error occured: {response.ErrorMessage}"); + } + } + + return response.Content!; + } + + public async Task Post(PleskServer server, string resource, object? body) + { + var request = CreateRequest(server, resource); + + request.Method = Method.Post; + + request.AddParameter("text/plain", + JsonConvert.SerializeObject(body), + ParameterType.RequestBody + ); + + var response = await Client.ExecuteAsync(request); + + if (!response.IsSuccessful) + { + if (response.StatusCode != 0) + { + throw new PleskException( + $"An error occured: ({response.StatusCode}) {response.Content}", + (int)response.StatusCode + ); + } + else + { + throw new Exception($"An internal error occured: {response.ErrorMessage}"); + } + } + + return JsonConvert.DeserializeObject(response.Content!)!; + } + + public async Task Post(PleskServer server, string resource, object? body) + { + var request = CreateRequest(server, resource); + + request.Method = Method.Post; + + if(body != null) + request.AddParameter("text/plain", JsonConvert.SerializeObject(body), ParameterType.RequestBody); + + var response = await Client.ExecuteAsync(request); + + if (!response.IsSuccessful) + { + if (response.StatusCode != 0) + { + throw new PleskException( + $"An error occured: ({response.StatusCode}) {response.Content}", + (int)response.StatusCode + ); + } + else + { + throw new Exception($"An internal error occured: {response.ErrorMessage}"); + } + } + } + + public async Task PostRaw(PleskServer server, string resource, object body) + { + var request = CreateRequest(server, resource); + + request.Method = Method.Post; + + request.AddParameter("text/plain", body, ParameterType.RequestBody); + + var response = await Client.ExecuteAsync(request); + + if (!response.IsSuccessful) + { + if (response.StatusCode != 0) + { + throw new PleskException( + $"An error occured: ({response.StatusCode}) {response.Content}", + (int)response.StatusCode + ); + } + else + { + throw new Exception($"An internal error occured: {response.ErrorMessage}"); + } + } + } + + public async Task Delete(PleskServer server, string resource, object? body) + { + var request = CreateRequest(server, resource); + + request.Method = Method.Delete; + + if(body != null) + request.AddParameter("text/plain", JsonConvert.SerializeObject(body), ParameterType.RequestBody); + + var response = await Client.ExecuteAsync(request); + + if (!response.IsSuccessful) + { + if (response.StatusCode != 0) + { + throw new PleskException( + $"An error occured: ({response.StatusCode}) {response.Content}", + (int)response.StatusCode + ); + } + else + { + throw new Exception($"An internal error occured: {response.ErrorMessage}"); + } + } + } + + public async Task Put(PleskServer server, string resource, object? body) + { + var request = CreateRequest(server, resource); + + request.Method = Method.Put; + + request.AddParameter("text/plain", JsonConvert.SerializeObject(body), ParameterType.RequestBody); + + var response = await Client.ExecuteAsync(request); + + if (!response.IsSuccessful) + { + if (response.StatusCode != 0) + { + throw new PleskException( + $"An error occured: ({response.StatusCode}) {response.Content}", + (int)response.StatusCode + ); + } + else + { + throw new Exception($"An internal error occured: {response.ErrorMessage}"); + } + } + } + + private RestRequest CreateRequest(PleskServer pleskServer, string resource) + { + var url = $"{pleskServer.ApiUrl}/" + resource; + + var request = new RestRequest(url); + var ba = Convert.ToBase64String(Encoding.UTF8.GetBytes(pleskServer.ApiKey)); + + request.AddHeader("Content-Type", "application/json"); + request.AddHeader("Accept", "application/json"); + request.AddHeader("Authorization", "Basic " + ba); + + return request; + } +} \ No newline at end of file diff --git a/Moonlight/App/Helpers/SmartTranslateHelper.cs b/Moonlight/App/Helpers/SmartTranslateHelper.cs index 96b4599..6ce8d65 100644 --- a/Moonlight/App/Helpers/SmartTranslateHelper.cs +++ b/Moonlight/App/Helpers/SmartTranslateHelper.cs @@ -8,7 +8,7 @@ public class SmartTranslateHelper { Languages = new(); - foreach (var file in Directory.GetFiles("resources/lang")) + foreach (var file in Directory.GetFiles(PathBuilder.Dir("storage", "resources", "lang"))) { if (Path.GetExtension(file) == ".lang") { @@ -21,7 +21,9 @@ public class SmartTranslateHelper foreach (var line in lines) { var parts = line.Split(";"); - content.Add(parts[0], parts[1]); + + if(!content.ContainsKey(parts[0])) + content.Add(parts[0], parts[1]); } Languages.Add(langKey, content); @@ -38,7 +40,7 @@ public class SmartTranslateHelper { Languages[langKey].Add(content, content); - File.WriteAllLines($"resources/lang/{langKey}.lang", GenerateData(Languages[langKey])); + File.WriteAllLines(PathBuilder.File("storage", "resources", "lang", $"{langKey}.lang"), GenerateData(Languages[langKey])); } return Languages[langKey][content]; diff --git a/Moonlight/App/Helpers/WingsServerConverter.cs b/Moonlight/App/Helpers/WingsServerConverter.cs index acc7ed1..c7375e3 100644 --- a/Moonlight/App/Helpers/WingsServerConverter.cs +++ b/Moonlight/App/Helpers/WingsServerConverter.cs @@ -46,13 +46,14 @@ public class WingsServerConverter } // Build - wingsServer.Settings.Build.Swap = server.Memory * 2; + wingsServer.Settings.Build.Swap = server.Memory * 2; //TODO: Add config option wingsServer.Settings.Build.Threads = null!; wingsServer.Settings.Build.Cpu_Limit = server.Cpu; wingsServer.Settings.Build.Disk_Space = server.Disk; wingsServer.Settings.Build.Io_Weight = 500; wingsServer.Settings.Build.Memory_Limit = server.Memory; wingsServer.Settings.Build.Oom_Disabled = true; + wingsServer.Settings.Build.Oom_Killer = false; var image = ImageRepository .Get() @@ -117,12 +118,15 @@ public class WingsServerConverter foreach (var section in child.GetSection("find").GetChildren()) { - replaces.Add(new() + if (section.Value != null) { - Match = section.Key, - Replace_With = section.Value - .Replace("{{server.build.default.port}}", def.Port.ToString()) - }); + replaces.Add(new() + { + Match = section.Key, + Replace_With = section.Value + .Replace("{{server.build.default.port}}", def.Port.ToString()) + }); + } } wingsServer.Process_Configuration.Configs.Add(new() diff --git a/Moonlight/App/Http/Controllers/Api/Moonlight/OAuth2Controller.cs b/Moonlight/App/Http/Controllers/Api/Moonlight/OAuth2Controller.cs index 7452ce4..3d1abb4 100644 --- a/Moonlight/App/Http/Controllers/Api/Moonlight/OAuth2Controller.cs +++ b/Moonlight/App/Http/Controllers/Api/Moonlight/OAuth2Controller.cs @@ -58,7 +58,7 @@ public class OAuth2Controller : Controller } else { - token = await UserService.GenerateToken(user); + token = await UserService.GenerateToken(user, true); } Response.Cookies.Append("token", token, new () @@ -116,7 +116,7 @@ public class OAuth2Controller : Controller } else { - token = await UserService.GenerateToken(user); + token = await UserService.GenerateToken(user, true); } Response.Cookies.Append("token", token, new () diff --git a/Moonlight/App/Http/Controllers/Api/Moonlight/ResourcesController.cs b/Moonlight/App/Http/Controllers/Api/Moonlight/ResourcesController.cs index 52750c4..254a0b5 100644 --- a/Moonlight/App/Http/Controllers/Api/Moonlight/ResourcesController.cs +++ b/Moonlight/App/Http/Controllers/Api/Moonlight/ResourcesController.cs @@ -1,5 +1,6 @@ using Logging.Net; using Microsoft.AspNetCore.Mvc; +using Moonlight.App.Helpers; using Moonlight.App.Models.Misc; using Moonlight.App.Services.LogServices; @@ -28,14 +29,13 @@ public class ResourcesController : Controller return NotFound(); } - if (System.IO.File.Exists($"resources/public/images/{name}")) + if (System.IO.File.Exists(PathBuilder.File("storage", "resources", "public", "images", name))) { - var fs = new FileStream($"resources/public/images/{name}", FileMode.Open); + var fs = new FileStream(PathBuilder.File("storage", "resources", "public", "images", name), FileMode.Open); return File(fs, MimeTypes.GetMimeType(name), name); } - - Logger.Debug("404 on resources"); + return NotFound(); } } \ No newline at end of file diff --git a/Moonlight/App/Http/Controllers/Api/Remote/ServersController.cs b/Moonlight/App/Http/Controllers/Api/Remote/ServersController.cs index 9ea1e8f..1136bca 100644 --- a/Moonlight/App/Http/Controllers/Api/Remote/ServersController.cs +++ b/Moonlight/App/Http/Controllers/Api/Remote/ServersController.cs @@ -138,7 +138,15 @@ public class ServersController : Controller await MessageService.Emit($"wings.{node.Id}.serverfetch", server); - return Converter.FromServer(server); + try //TODO: Remove + { + return Converter.FromServer(server); + } + catch (Exception e) + { + Console.WriteLine(e); + throw; + } } [HttpGet("{uuid}/install")] diff --git a/Moonlight/App/Http/Resources/Wings/WingsServer.cs b/Moonlight/App/Http/Resources/Wings/WingsServer.cs index d55a81e..8dafcd7 100644 --- a/Moonlight/App/Http/Resources/Wings/WingsServer.cs +++ b/Moonlight/App/Http/Resources/Wings/WingsServer.cs @@ -130,6 +130,9 @@ public class WingsServer [JsonPropertyName("oom_disabled")] public bool Oom_Disabled { get; set; } + + [JsonPropertyName("oom_killer")] + public bool Oom_Killer { get; set; } } public class WingsServerContainer diff --git a/Moonlight/App/Models/Files/Accesses/HostFileAccess.cs b/Moonlight/App/Models/Files/Accesses/HostFileAccess.cs deleted file mode 100644 index ef0a66e..0000000 --- a/Moonlight/App/Models/Files/Accesses/HostFileAccess.cs +++ /dev/null @@ -1,145 +0,0 @@ -using Logging.Net; -using Moonlight.App.Exceptions; -using Moonlight.App.Helpers; - -namespace Moonlight.App.Models.Files.Accesses; - -public class HostFileAccess : IFileAccess -{ - private readonly string BasePath; - private string RealPath => BasePath + Path; - private string Path = "/"; - - public HostFileAccess(string bp) - { - BasePath = bp; - } - - public Task GetDirectoryContent() - { - var x = new List(); - - foreach (var directory in Directory.EnumerateDirectories(RealPath)) - { - x.Add(new () - { - Name = System.IO.Path.GetFileName(directory)!, - CreatedAt = DateTime.UtcNow, - UpdatedAt = DateTime.UtcNow, - Size = 0, - IsFile = false - }); - } - - foreach (var file in Directory.GetFiles(RealPath)) - { - x.Add(new () - { - Name = System.IO.Path.GetFileName(file)!, - CreatedAt = File.GetCreationTimeUtc(file), - UpdatedAt = File.GetLastWriteTimeUtc(file), - Size = new System.IO.FileInfo(file).Length, - IsFile = File.Exists(file) - }); - } - - return Task.FromResult(x.ToArray()); - } - - public Task ChangeDirectory(string s) - { - var x = System.IO.Path.Combine(Path, s).Replace("\\", "/") + "/"; - x = x.Replace("//", "/"); - Path = x; - - return Task.CompletedTask; - } - - public Task SetDirectory(string s) - { - Path = s; - return Task.CompletedTask; - } - - public Task GoUp() - { - Path = System.IO.Path.GetFullPath(System.IO.Path.Combine(Path, "..")).Replace("\\", "/").Replace("C:", ""); - return Task.CompletedTask; - } - - public Task ReadFile(FileManagerObject fileManagerObject) - { - return Task.FromResult(File.ReadAllText(RealPath + fileManagerObject.Name)); - } - - public Task WriteFile(FileManagerObject fileManagerObject, string content) - { - File.WriteAllText(RealPath + fileManagerObject.Name, content); - return Task.CompletedTask; - } - - public async Task UploadFile(string name, Stream stream, Action? progressUpdated = null) - { - var fs = File.OpenWrite(RealPath + name); - - var dataStream = new StreamProgressHelper(stream) - { - Progress = i => - { - if (progressUpdated != null) - progressUpdated.Invoke(i); - } - }; - - await dataStream.CopyToAsync(fs); - await fs.FlushAsync(); - fs.Close(); - } - - public Task CreateDirectory(string name) - { - Directory.CreateDirectory(RealPath + name); - return Task.CompletedTask; - } - - public Task GetCurrentPath() - { - return Task.FromResult(Path); - } - - public Task GetDownloadStream(FileManagerObject managerObject) - { - var stream = new FileStream(RealPath + managerObject.Name, FileMode.Open, FileAccess.Read); - return Task.FromResult(stream); - } - - public Task GetDownloadUrl(FileManagerObject managerObject) - { - throw new NotImplementedException(); - } - - public Task Delete(FileManagerObject managerObject) - { - if(managerObject.IsFile) - File.Delete(RealPath + managerObject.Name); - else - Directory.Delete(RealPath + managerObject.Name, true); - - return Task.CompletedTask; - } - - public Task Move(FileManagerObject managerObject, string newPath) - { - if(managerObject.IsFile) - File.Move(RealPath + managerObject.Name, BasePath + newPath); - else - Directory.Move(RealPath + managerObject.Name, BasePath + newPath); - - return Task.CompletedTask; - } - - public Task GetLaunchUrl() - { - throw new DisplayException("WinSCP cannot be launched here"); - } -} \ No newline at end of file diff --git a/Moonlight/App/Models/Files/Accesses/WingsFileAccess.cs b/Moonlight/App/Models/Files/Accesses/WingsFileAccess.cs deleted file mode 100644 index 1d64ab8..0000000 --- a/Moonlight/App/Models/Files/Accesses/WingsFileAccess.cs +++ /dev/null @@ -1,205 +0,0 @@ -using System.Security.Cryptography; -using System.Text; -using System.Web; -using JWT.Algorithms; -using JWT.Builder; -using Moonlight.App.Database.Entities; -using Moonlight.App.Helpers; -using Moonlight.App.Models.Wings.Requests; -using Moonlight.App.Models.Wings.Resources; -using RestSharp; - -namespace Moonlight.App.Models.Files.Accesses; - -public class WingsFileAccess : IFileAccess -{ - private readonly WingsApiHelper WingsApiHelper; - private readonly WingsJwtHelper WingsJwtHelper; - private readonly Database.Entities.Node Node; - private readonly Server Server; - private readonly User User; - private readonly string AppUrl; - - private string Path { get; set; } = "/"; - - public WingsFileAccess( - WingsApiHelper wingsApiHelper, - Server server, - User user, - WingsJwtHelper wingsJwtHelper, - string appUrl) - { - WingsApiHelper = wingsApiHelper; - Node = server.Node; - Server = server; - User = user; - WingsJwtHelper = wingsJwtHelper; - AppUrl = appUrl; - } - - public async Task GetDirectoryContent() - { - var res = await WingsApiHelper.Get(Node, - $"api/servers/{Server.Uuid}/files/list-directory?directory={Path}"); - - var x = new List(); - - foreach (var response in res) - { - x.Add(new() - { - Name = response.Name, - Size = response.File ? response.Size : 0, - CreatedAt = response.Created.Date, - IsFile = response.File, - UpdatedAt = response.Modified.Date - }); - } - - return x.ToArray(); - } - - public Task ChangeDirectory(string s) - { - var x = System.IO.Path.Combine(Path, s).Replace("\\", "/") + "/"; - x = x.Replace("//", "/"); - Path = x; - - return Task.CompletedTask; - } - - public Task SetDirectory(string s) - { - Path = s; - return Task.CompletedTask; - } - - public Task GoUp() - { - Path = System.IO.Path.GetFullPath(System.IO.Path.Combine(Path, "..")).Replace("\\", "/").Replace("C:", ""); - return Task.CompletedTask; - } - - public async Task ReadFile(FileManagerObject fileManagerObject) - { - return await WingsApiHelper.GetRaw(Node, - $"api/servers/{Server.Uuid}/files/contents?file={Path}{fileManagerObject.Name}"); - } - - public async Task WriteFile(FileManagerObject fileManagerObject, string content) - { - await WingsApiHelper.PostRaw(Node, - $"api/servers/{Server.Uuid}/files/write?file={Path}{fileManagerObject.Name}", content); - } - - public async Task UploadFile(string name, Stream dataStream, Action onProgress) - { - var token = WingsJwtHelper.Generate(Node.Token, - claims => { claims.Add("server_uuid", Server.Uuid.ToString()); }); - - var client = new RestClient(); - var request = new RestRequest(); - - if (Node.Ssl) - request.Resource = $"https://{Node.Fqdn}:{Node.HttpPort}/upload/file?token={token}&directory={Path}"; - else - request.Resource = $"http://{Node.Fqdn}:{Node.HttpPort}/upload/file?token={token}&directory={Path}"; - - request.AddParameter("name", "files"); - request.AddParameter("filename", name); - request.AddHeader("Content-Type", "multipart/form-data"); - request.AddHeader("Origin", AppUrl); - request.AddFile("files", () => - { - return new StreamProgressHelper(dataStream) - { - Progress = i => - { - if (onProgress != null) - onProgress.Invoke(i); - } - }; - }, name); - - await client.ExecutePostAsync(request); - - client.Dispose(); - dataStream.Close(); - } - - public async Task CreateDirectory(string name) - { - await WingsApiHelper.Post(Node, $"api/servers/{Server.Uuid}/files/create-directory", - new CreateDirectory() - { - Name = name, - Path = Path - }); - } - - public Task GetCurrentPath() - { - return Task.FromResult(Path); - } - - public Task GetDownloadStream(FileManagerObject managerObject) - { - throw new NotImplementedException(); - } - - public Task GetDownloadUrl(FileManagerObject managerObject) - { - var token = WingsJwtHelper.Generate(Node.Token, claims => - { - claims.Add("server_uuid", Server.Uuid.ToString()); - claims.Add("file_path", HttpUtility.UrlDecode(Path + "/" + managerObject.Name)); - }); - - if (Node.Ssl) - { - return Task.FromResult( - $"https://{Node.Fqdn}:{Node.HttpPort}/download/file?token={token}" - ); - } - else - { - return Task.FromResult( - $"http://{Node.Fqdn}:{Node.HttpPort}/download/file?token={token}" - ); - } - } - - public async Task Delete(FileManagerObject managerObject) - { - await WingsApiHelper.Post(Node, $"api/servers/{Server.Uuid}/files/delete", new DeleteFiles() - { - Root = Path, - Files = new() - { - managerObject.Name - } - }); - } - - public async Task Move(FileManagerObject managerObject, string newPath) - { - await WingsApiHelper.Put(Node, $"api/servers/{Server.Uuid}/files/rename", new RenameFiles() - { - Root = "/", - Files = new[] - { - new RenameFilesData() - { - From = Path + managerObject.Name, - To = newPath - } - } - }); - } - - public Task GetLaunchUrl() - { - return Task.FromResult( - $"sftp://{User.Id}.{StringHelper.IntToStringWithLeadingZeros(Server.Id, 8)}@{Node.Fqdn}:{Node.SftpPort}"); - } -} \ No newline at end of file diff --git a/Moonlight/App/Models/Files/FileContextAction.cs b/Moonlight/App/Models/Files/FileContextAction.cs deleted file mode 100644 index 7570013..0000000 --- a/Moonlight/App/Models/Files/FileContextAction.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Moonlight.App.Models.Files; - -public class FileContextAction -{ - public string Id { get; set; } - public string Name { get; set; } - public Action Action { get; set; } -} \ No newline at end of file diff --git a/Moonlight/App/Models/Files/FileManagerObject.cs b/Moonlight/App/Models/Files/FileManagerObject.cs deleted file mode 100644 index 063d556..0000000 --- a/Moonlight/App/Models/Files/FileManagerObject.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Moonlight.App.Models.Files; - -public class FileManagerObject -{ - public string Name { get; set; } - public bool IsFile { get; set; } - public long Size { get; set; } - public DateTime CreatedAt { get; set; } - public DateTime UpdatedAt { get; set; } -} \ No newline at end of file diff --git a/Moonlight/App/Models/Files/IFileAccess.cs b/Moonlight/App/Models/Files/IFileAccess.cs deleted file mode 100644 index 6792d40..0000000 --- a/Moonlight/App/Models/Files/IFileAccess.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace Moonlight.App.Models.Files; - -public interface IFileAccess -{ - public Task GetDirectoryContent(); - public Task ChangeDirectory(string s); - public Task SetDirectory(string s); - public Task GoUp(); - public Task ReadFile(FileManagerObject fileManagerObject); - public Task WriteFile(FileManagerObject fileManagerObject, string content); - public Task UploadFile(string name, Stream stream, Action progressUpdated); - public Task CreateDirectory(string name); - public Task GetCurrentPath(); - public Task GetDownloadStream(FileManagerObject managerObject); - public Task GetDownloadUrl(FileManagerObject managerObject); - public Task Delete(FileManagerObject managerObject); - public Task Move(FileManagerObject managerObject, string newPath); - public Task GetLaunchUrl(); -} \ No newline at end of file diff --git a/Moonlight/App/Models/Forms/DatabaseDataModel.cs b/Moonlight/App/Models/Forms/DatabaseDataModel.cs new file mode 100644 index 0000000..b298512 --- /dev/null +++ b/Moonlight/App/Models/Forms/DatabaseDataModel.cs @@ -0,0 +1,17 @@ +using System.ComponentModel.DataAnnotations; + +namespace Moonlight.App.Models.Forms; + +public class DatabaseDataModel +{ + [Required(ErrorMessage = "You need to enter a name")] + [MinLength(8, ErrorMessage = "The name should be at least 8 characters long")] + [MaxLength(32, ErrorMessage = "The database name should be maximal 32 characters")] + [RegularExpression(@"^[a-z0-9]+$", ErrorMessage = "The name should only contain of lower case characters and numbers")] + public string Name { get; set; } = ""; + + [Required(ErrorMessage = "You need to enter a password")] + [MinLength(8, ErrorMessage = "The password should be at least 8 characters long")] + [MaxLength(32, ErrorMessage = "The password name should be maximal 32 characters")] + public string Password { get; set; } = ""; +} \ No newline at end of file diff --git a/Moonlight/App/Models/Forms/DomainDataModel.cs b/Moonlight/App/Models/Forms/DomainDataModel.cs new file mode 100644 index 0000000..ce1bbc5 --- /dev/null +++ b/Moonlight/App/Models/Forms/DomainDataModel.cs @@ -0,0 +1,18 @@ +using System.ComponentModel.DataAnnotations; +using Moonlight.App.Database.Entities; + +namespace Moonlight.App.Models.Forms; + +public class DomainDataModel +{ + [Required(ErrorMessage = "You need to specify a name")] + [MaxLength(32, ErrorMessage = "The max lenght for the name is 32 characters")] + [RegularExpression(@"^[a-z]+$", ErrorMessage = "The name should only consist of lower case characters")] + public string Name { get; set; } = ""; + + [Required(ErrorMessage = "You need to specify a shared domain")] + public SharedDomain SharedDomain { get; set; } + + [Required(ErrorMessage = "You need to specify a owner")] + public User Owner { get; set; } +} \ No newline at end of file diff --git a/Moonlight/App/Models/Forms/DomainOrderDataModel.cs b/Moonlight/App/Models/Forms/DomainOrderDataModel.cs new file mode 100644 index 0000000..c49dd8e --- /dev/null +++ b/Moonlight/App/Models/Forms/DomainOrderDataModel.cs @@ -0,0 +1,15 @@ +using System.ComponentModel.DataAnnotations; +using Moonlight.App.Database.Entities; + +namespace Moonlight.App.Models.Forms; + +public class DomainOrderDataModel +{ + [Required(ErrorMessage = "You need to specify a name")] + [MaxLength(32, ErrorMessage = "The max lenght for the name is 32 characters")] + [RegularExpression(@"^[a-z]+$", ErrorMessage = "The name should only consist of lower case characters")] + public string Name { get; set; } = ""; + + [Required(ErrorMessage = "You need to specify a shared domain")] + public SharedDomain SharedDomain { get; set; } +} \ No newline at end of file diff --git a/Moonlight/App/Models/Forms/PleskServerDataModel.cs b/Moonlight/App/Models/Forms/PleskServerDataModel.cs new file mode 100644 index 0000000..78e4920 --- /dev/null +++ b/Moonlight/App/Models/Forms/PleskServerDataModel.cs @@ -0,0 +1,16 @@ +using System.ComponentModel.DataAnnotations; + +namespace Moonlight.App.Models.Forms; + +public class PleskServerDataModel +{ + [Required(ErrorMessage = "You have to enter a name")] + [MaxLength(32, ErrorMessage = "The name should not be longer than 32 characters")] + public string Name { get; set; } + + [Required(ErrorMessage = "You need to enter an api url")] + public string ApiUrl { get; set; } + + [Required(ErrorMessage = "You need to enter an api key")] + public string ApiKey { get; set; } +} \ No newline at end of file diff --git a/Moonlight/App/Models/Forms/ServerDataModel.cs b/Moonlight/App/Models/Forms/ServerDataModel.cs new file mode 100644 index 0000000..1b8c500 --- /dev/null +++ b/Moonlight/App/Models/Forms/ServerDataModel.cs @@ -0,0 +1,30 @@ +using System.ComponentModel.DataAnnotations; +using Moonlight.App.Database.Entities; + +namespace Moonlight.App.Models.Forms; + +public class ServerDataModel +{ + [Required(ErrorMessage = "You need to enter a name")] + [MaxLength(32, ErrorMessage = "The name cannot be longer that 32 characters")] + public string Name { get; set; } + + [Required(ErrorMessage = "You need to specify a owner")] + public User Owner { get; set; } + + [Required(ErrorMessage = "You need to specify cpu amount")] + public int Cpu { get; set; } = 100; + + [Required(ErrorMessage = "You need to specify a memory amount")] + public int Memory { get; set; } = 1024; + + [Required(ErrorMessage = "You need to specify a disk amount")] + public int Disk { get; set; } = 1024; + + [Required(ErrorMessage = "You need to specify a image")] + public Image Image { get; set; } + + public string OverrideStartup { get; set; } = ""; + + public int DockerImageIndex { get; set; } +} \ No newline at end of file diff --git a/Moonlight/App/Models/Forms/TestDataModel.cs b/Moonlight/App/Models/Forms/TestDataModel.cs new file mode 100644 index 0000000..0fecab7 --- /dev/null +++ b/Moonlight/App/Models/Forms/TestDataModel.cs @@ -0,0 +1,10 @@ +using System.ComponentModel.DataAnnotations; +using Moonlight.App.Database.Entities; + +namespace Moonlight.App.Models.Forms; + +public class TestDataModel +{ + [Required] + public User User { get; set; } +} \ No newline at end of file diff --git a/Moonlight/App/Models/Forms/UserDataModel.cs b/Moonlight/App/Models/Forms/UserDataModel.cs new file mode 100644 index 0000000..bc2ff8e --- /dev/null +++ b/Moonlight/App/Models/Forms/UserDataModel.cs @@ -0,0 +1,27 @@ +using System.ComponentModel.DataAnnotations; + +namespace Moonlight.App.Models.Forms; + +public class UserDataModel +{ + [Required] + public string FirstName { get; set; } = ""; + + [Required] + public string LastName { get; set; } = ""; + + [Required] + public string Email { get; set; } = ""; + + [Required] + public string Address { get; set; } = ""; + + [Required] + public string City { get; set; } = ""; + + [Required] + public string State { get; set; } = ""; + + [Required] + public string Country { get; set; } = ""; +} \ No newline at end of file diff --git a/Moonlight/App/Models/Forms/UserRegisterModel.cs b/Moonlight/App/Models/Forms/UserRegisterModel.cs new file mode 100644 index 0000000..399ddb7 --- /dev/null +++ b/Moonlight/App/Models/Forms/UserRegisterModel.cs @@ -0,0 +1,21 @@ +using System.ComponentModel.DataAnnotations; + +namespace Moonlight.App.Models.Forms; + +public class UserRegisterModel +{ + [Required, EmailAddress] + public string Email { get; set; } + + [Required, MinLength(3)] + public string FirstName { get; set; } + + [Required, MinLength(3)] + public string LastName { get; set; } + + [Required] + public string Password { get; set; } + + [Required] + public string ConfirmPassword { get; set; } +} \ No newline at end of file diff --git a/Moonlight/App/Models/Forms/WebsiteAdminDataModel.cs b/Moonlight/App/Models/Forms/WebsiteAdminDataModel.cs new file mode 100644 index 0000000..f3884d0 --- /dev/null +++ b/Moonlight/App/Models/Forms/WebsiteAdminDataModel.cs @@ -0,0 +1,14 @@ +using System.ComponentModel.DataAnnotations; +using Moonlight.App.Database.Entities; + +namespace Moonlight.App.Models.Forms; + +public class WebsiteAdminDataModel +{ + [Required(ErrorMessage = "You need a domain")] + [RegularExpression(@"([a-z0-9|-]+\.)*[a-z0-9|-]+\.[a-z]+", ErrorMessage = "You need to enter a valid domain")] + public string BaseDomain { get; set; } = ""; + + [Required(ErrorMessage = "You need to specify a owner")] + public User User { get; set; } +} \ No newline at end of file diff --git a/Moonlight/App/Models/Forms/WebsiteDataModel.cs b/Moonlight/App/Models/Forms/WebsiteDataModel.cs new file mode 100644 index 0000000..9bc9d2d --- /dev/null +++ b/Moonlight/App/Models/Forms/WebsiteDataModel.cs @@ -0,0 +1,10 @@ +using System.ComponentModel.DataAnnotations; + +namespace Moonlight.App.Models.Forms; + +public class WebsiteDataModel +{ + [Required(ErrorMessage = "You need to enter a domain")] + [RegularExpression(@"([a-z0-9|-]+\.)*[a-z0-9|-]+\.[a-z]+", ErrorMessage = "You need to enter a valid domain")] + public string BaseDomain { get; set; } = ""; +} \ No newline at end of file diff --git a/Moonlight/App/Models/Forms/WebsiteOrderDataModel.cs b/Moonlight/App/Models/Forms/WebsiteOrderDataModel.cs new file mode 100644 index 0000000..0c5a388 --- /dev/null +++ b/Moonlight/App/Models/Forms/WebsiteOrderDataModel.cs @@ -0,0 +1,10 @@ +using System.ComponentModel.DataAnnotations; + +namespace Moonlight.App.Models.Forms; + +public class WebsiteOrderDataModel +{ + [Required(ErrorMessage = "You need to enter a domain")] + [RegularExpression(@"([a-z0-9|-]+\.)*[a-z0-9|-]+\.[a-z]+", ErrorMessage = "You need to enter a valid domain")] + public string BaseDomain { get; set; } = ""; +} \ No newline at end of file diff --git a/Moonlight/App/Models/Misc/StatisticsTimeSpan.cs b/Moonlight/App/Models/Misc/StatisticsTimeSpan.cs new file mode 100644 index 0000000..6341be6 --- /dev/null +++ b/Moonlight/App/Models/Misc/StatisticsTimeSpan.cs @@ -0,0 +1,10 @@ +namespace Moonlight.App.Models.Misc; + +public enum StatisticsTimeSpan +{ + Hour = 1, + Day = 24, + Month = Day * 31, + Year = 365 * Day, + AllTime = Year * 99 +} \ No newline at end of file diff --git a/Moonlight/App/Models/Plesk/Requests/CliCall.cs b/Moonlight/App/Models/Plesk/Requests/CliCall.cs new file mode 100644 index 0000000..c6fb06e --- /dev/null +++ b/Moonlight/App/Models/Plesk/Requests/CliCall.cs @@ -0,0 +1,10 @@ +using Newtonsoft.Json; + +namespace Moonlight.App.Models.Plesk.Requests; + +public class CliCall +{ + [JsonProperty("params")] public List Params { get; set; } = new(); + + [JsonProperty("env")] public Dictionary Env { get; set; } = new(); +} \ No newline at end of file diff --git a/Moonlight/App/Models/Plesk/Requests/CreateDatabase.cs b/Moonlight/App/Models/Plesk/Requests/CreateDatabase.cs new file mode 100644 index 0000000..11c9a0c --- /dev/null +++ b/Moonlight/App/Models/Plesk/Requests/CreateDatabase.cs @@ -0,0 +1,23 @@ +using Newtonsoft.Json; + +namespace Moonlight.App.Models.Plesk.Requests; + +public class CreateDatabase +{ + [JsonProperty("name")] + public string Name { get; set; } + + [JsonProperty("type")] + public string Type { get; set; } + + [JsonProperty("parent_domain")] public ParentDomainModel ParentDomain { get; set; } = new(); + + [JsonProperty("server_id")] + public int ServerId { get; set; } + + public class ParentDomainModel + { + [JsonProperty("name")] + public string Name { get; set; } + } +} \ No newline at end of file diff --git a/Moonlight/App/Models/Plesk/Requests/CreateDatabaseUser.cs b/Moonlight/App/Models/Plesk/Requests/CreateDatabaseUser.cs new file mode 100644 index 0000000..eca5dd3 --- /dev/null +++ b/Moonlight/App/Models/Plesk/Requests/CreateDatabaseUser.cs @@ -0,0 +1,15 @@ +using Newtonsoft.Json; + +namespace Moonlight.App.Models.Plesk.Requests; + +public class CreateDatabaseUser +{ + [JsonProperty("login")] + public string Login { get; set; } + + [JsonProperty("password")] + public string Password { get; set; } + + [JsonProperty("database_id")] + public int DatabaseId { get; set; } +} \ No newline at end of file diff --git a/Moonlight/App/Models/Plesk/Requests/CreateDomain.cs b/Moonlight/App/Models/Plesk/Requests/CreateDomain.cs new file mode 100644 index 0000000..1ef11ca --- /dev/null +++ b/Moonlight/App/Models/Plesk/Requests/CreateDomain.cs @@ -0,0 +1,45 @@ +using Newtonsoft.Json; + +namespace Moonlight.App.Models.Plesk.Requests; + +public class CreateDomain +{ + [JsonProperty("name")] + public string Name { get; set; } + + [JsonProperty("description")] + public string Description { get; set; } + + [JsonProperty("hosting_type")] + public string HostingType { get; set; } + + [JsonProperty("hosting_settings")] + public HostingSettingsModel HostingSettings { get; set; } + + [JsonProperty("owner_client")] + public OwnerClientModel OwnerClient { get; set; } + + [JsonProperty("plan")] + public PlanModel Plan { get; set; } + + public partial class HostingSettingsModel + { + [JsonProperty("ftp_login")] + public string FtpLogin { get; set; } + + [JsonProperty("ftp_password")] + public string FtpPassword { get; set; } + } + + public partial class OwnerClientModel + { + [JsonProperty("id")] + public long Id { get; set; } + } + + public partial class PlanModel + { + [JsonProperty("name")] + public string Name { get; set; } + } +} \ No newline at end of file diff --git a/Moonlight/App/Models/Plesk/Resources/CliResult.cs b/Moonlight/App/Models/Plesk/Resources/CliResult.cs new file mode 100644 index 0000000..255ff8d --- /dev/null +++ b/Moonlight/App/Models/Plesk/Resources/CliResult.cs @@ -0,0 +1,13 @@ +using Newtonsoft.Json; + +namespace Moonlight.App.Models.Plesk.Resources; + +public class CliResult +{ + [JsonProperty("code")] + public int Code { get; set; } + + [JsonProperty("stdout")] public string Stdout { get; set; } = ""; + + [JsonProperty("stderr")] public string Stderr { get; set; } = ""; +} \ No newline at end of file diff --git a/Moonlight/App/Models/Plesk/Resources/Client.cs b/Moonlight/App/Models/Plesk/Resources/Client.cs new file mode 100644 index 0000000..8bf96ae --- /dev/null +++ b/Moonlight/App/Models/Plesk/Resources/Client.cs @@ -0,0 +1,45 @@ +using Newtonsoft.Json; + +namespace Moonlight.App.Models.Plesk.Resources; + +public class Client +{ + [JsonProperty("id")] + public int Id { get; set; } + + [JsonProperty("created")] + public DateTimeOffset Created { get; set; } + + [JsonProperty("name")] + public string Name { get; set; } + + [JsonProperty("company")] + public string Company { get; set; } + + [JsonProperty("login")] + public string Login { get; set; } + + [JsonProperty("status")] + public long Status { get; set; } + + [JsonProperty("email")] + public string Email { get; set; } + + [JsonProperty("locale")] + public string Locale { get; set; } + + [JsonProperty("guid")] + public Guid Guid { get; set; } + + [JsonProperty("owner_login")] + public string OwnerLogin { get; set; } + + [JsonProperty("external_id")] + public string ExternalId { get; set; } + + [JsonProperty("description")] + public string Description { get; set; } + + [JsonProperty("type")] + public string Type { get; set; } +} \ No newline at end of file diff --git a/Moonlight/App/Models/Plesk/Resources/CreateResult.cs b/Moonlight/App/Models/Plesk/Resources/CreateResult.cs new file mode 100644 index 0000000..54415ae --- /dev/null +++ b/Moonlight/App/Models/Plesk/Resources/CreateResult.cs @@ -0,0 +1,12 @@ +using Newtonsoft.Json; + +namespace Moonlight.App.Models.Plesk.Resources; + +public class CreateResult +{ + [JsonProperty("id")] + public int Id { get; set; } + + [JsonProperty("guid")] + public Guid Guid { get; set; } +} \ No newline at end of file diff --git a/Moonlight/App/Models/Plesk/Resources/Database.cs b/Moonlight/App/Models/Plesk/Resources/Database.cs new file mode 100644 index 0000000..27a7899 --- /dev/null +++ b/Moonlight/App/Models/Plesk/Resources/Database.cs @@ -0,0 +1,15 @@ +using Newtonsoft.Json; + +namespace Moonlight.App.Models.Plesk.Resources; + +public class Database +{ + [JsonProperty("id")] + public int Id { get; set; } + + [JsonProperty("name")] + public string Name { get; set; } + + [JsonProperty("type")] + public string Type { get; set; } +} \ No newline at end of file diff --git a/Moonlight/App/Models/Plesk/Resources/DatabaseServer.cs b/Moonlight/App/Models/Plesk/Resources/DatabaseServer.cs new file mode 100644 index 0000000..a16ceab --- /dev/null +++ b/Moonlight/App/Models/Plesk/Resources/DatabaseServer.cs @@ -0,0 +1,30 @@ +using Newtonsoft.Json; + +namespace Moonlight.App.Models.Plesk.Resources; + +public class DatabaseServer +{ + [JsonProperty("id")] + public int Id { get; set; } + + [JsonProperty("host")] + public string Host { get; set; } + + [JsonProperty("port")] + public int Port { get; set; } + + [JsonProperty("type")] + public string Type { get; set; } + + [JsonProperty("status")] + public string Status { get; set; } + + [JsonProperty("db_count")] + public int DbCount { get; set; } + + [JsonProperty("is_default")] + public bool IsDefault { get; set; } + + [JsonProperty("is_local")] + public bool IsLocal { get; set; } +} \ No newline at end of file diff --git a/Moonlight/App/Models/Plesk/Resources/DatabaseUser.cs b/Moonlight/App/Models/Plesk/Resources/DatabaseUser.cs new file mode 100644 index 0000000..033a763 --- /dev/null +++ b/Moonlight/App/Models/Plesk/Resources/DatabaseUser.cs @@ -0,0 +1,15 @@ +using Newtonsoft.Json; + +namespace Moonlight.App.Models.Plesk.Resources; + +public class DatabaseUser +{ + [JsonProperty("id")] + public int Id { get; set; } + + [JsonProperty("login")] + public string Login { get; set; } + + [JsonProperty("database_id")] + public int DatabaseId { get; set; } +} \ No newline at end of file diff --git a/Moonlight/App/Models/Plesk/Resources/ServerStatus.cs b/Moonlight/App/Models/Plesk/Resources/ServerStatus.cs new file mode 100644 index 0000000..32962db --- /dev/null +++ b/Moonlight/App/Models/Plesk/Resources/ServerStatus.cs @@ -0,0 +1,18 @@ +using Newtonsoft.Json; + +namespace Moonlight.App.Models.Plesk.Resources; + +public class ServerStatus +{ + [JsonProperty("platform")] + public string Platform { get; set; } + + [JsonProperty("hostname")] + public string Hostname { get; set; } + + [JsonProperty("guid")] + public Guid Guid { get; set; } + + [JsonProperty("panel_version")] + public string PanelVersion { get; set; } +} \ No newline at end of file diff --git a/Moonlight/App/Repositories/NewsEntryRepository.cs b/Moonlight/App/Repositories/NewsEntryRepository.cs new file mode 100644 index 0000000..edd0118 --- /dev/null +++ b/Moonlight/App/Repositories/NewsEntryRepository.cs @@ -0,0 +1,44 @@ +using Microsoft.EntityFrameworkCore; +using Moonlight.App.Database; +using Moonlight.App.Database.Entities; + +namespace Moonlight.App.Repositories; + +public class NewsEntryRepository +{ + private readonly DataContext DataContext; + + public NewsEntryRepository(DataContext dataContext) + { + DataContext = dataContext; + } + + public DbSet Get() + { + return DataContext.NewsEntries; + } + + public NewsEntry Add(NewsEntry newsEntry) + { + var x = DataContext.NewsEntries.Add(newsEntry); + DataContext.SaveChanges(); + return x.Entity; + } + + public void Update(NewsEntry newsEntry) + { + DataContext.NewsEntries.Update(newsEntry); + DataContext.SaveChanges(); + } + + public void Delete(NewsEntry newsEntry) + { + DataContext.NewsEntries.Remove(newsEntry); + DataContext.SaveChanges(); + } + + public void Dispose() + { + DataContext.Dispose(); + } +} \ No newline at end of file diff --git a/Moonlight/App/Repositories/PleskServerRepository.cs b/Moonlight/App/Repositories/PleskServerRepository.cs new file mode 100644 index 0000000..5374263 --- /dev/null +++ b/Moonlight/App/Repositories/PleskServerRepository.cs @@ -0,0 +1,44 @@ +using Microsoft.EntityFrameworkCore; +using Moonlight.App.Database; +using Moonlight.App.Database.Entities; + +namespace Moonlight.App.Repositories; + +public class PleskServerRepository : IDisposable +{ + private readonly DataContext DataContext; + + public PleskServerRepository(DataContext dataContext) + { + DataContext = dataContext; + } + + public DbSet Get() + { + return DataContext.PleskServers; + } + + public PleskServer Add(PleskServer pleskServer) + { + var x = DataContext.PleskServers.Add(pleskServer); + DataContext.SaveChanges(); + return x.Entity; + } + + public void Update(PleskServer pleskServer) + { + DataContext.PleskServers.Update(pleskServer); + DataContext.SaveChanges(); + } + + public void Delete(PleskServer pleskServer) + { + DataContext.PleskServers.Remove(pleskServer); + DataContext.SaveChanges(); + } + + public void Dispose() + { + DataContext.Dispose(); + } +} \ No newline at end of file diff --git a/Moonlight/App/Repositories/StatisticsRepository.cs b/Moonlight/App/Repositories/StatisticsRepository.cs new file mode 100644 index 0000000..f33751b --- /dev/null +++ b/Moonlight/App/Repositories/StatisticsRepository.cs @@ -0,0 +1,37 @@ +using Microsoft.EntityFrameworkCore; +using Moonlight.App.Database; +using Moonlight.App.Database.Entities; + +namespace Moonlight.App.Repositories; + +public class StatisticsRepository : IDisposable +{ + private readonly DataContext DataContext; + + public StatisticsRepository(DataContext dataContext) + { + DataContext = dataContext; + } + + public DbSet Get() + { + return DataContext.Statistics; + } + + public StatisticsData Add(StatisticsData data) + { + var x = DataContext.Statistics.Add(data); + DataContext.SaveChanges(); + return x.Entity; + } + + public StatisticsData Add(string chart, double value) + { + return Add(new StatisticsData() {Chart = chart, Value = value, Date = DateTime.Now}); + } + + public void Dispose() + { + DataContext.Dispose(); + } +} \ No newline at end of file diff --git a/Moonlight/App/Repositories/WebsiteRepository.cs b/Moonlight/App/Repositories/WebsiteRepository.cs new file mode 100644 index 0000000..4a45b5d --- /dev/null +++ b/Moonlight/App/Repositories/WebsiteRepository.cs @@ -0,0 +1,44 @@ +using Microsoft.EntityFrameworkCore; +using Moonlight.App.Database; +using Moonlight.App.Database.Entities; + +namespace Moonlight.App.Repositories; + +public class WebsiteRepository : IDisposable +{ + private readonly DataContext DataContext; + + public WebsiteRepository(DataContext dataContext) + { + DataContext = dataContext; + } + + public DbSet Get() + { + return DataContext.Websites; + } + + public Website Add(Website website) + { + var x = DataContext.Websites.Add(website); + DataContext.SaveChanges(); + return x.Entity; + } + + public void Update(Website website) + { + DataContext.Websites.Update(website); + DataContext.SaveChanges(); + } + + public void Delete(Website website) + { + DataContext.Websites.Remove(website); + DataContext.SaveChanges(); + } + + public void Dispose() + { + DataContext.Dispose(); + } +} \ No newline at end of file diff --git a/Moonlight/App/Services/ConfigService.cs b/Moonlight/App/Services/ConfigService.cs index ed124f6..27bc5bc 100644 --- a/Moonlight/App/Services/ConfigService.cs +++ b/Moonlight/App/Services/ConfigService.cs @@ -7,26 +7,44 @@ namespace Moonlight.App.Services; public class ConfigService : IConfiguration { + private readonly StorageService StorageService; + private IConfiguration Configuration; public bool DebugMode { get; private set; } = false; - - public ConfigService() + + public ConfigService(StorageService storageService) { - Configuration = new ConfigurationBuilder().AddJsonStream( - new MemoryStream(Encoding.ASCII.GetBytes(File.ReadAllText("..\\..\\appsettings.json"))) - ).Build(); + StorageService = storageService; + StorageService.EnsureCreated(); + + Reload(); // Env vars var debugVar = Environment.GetEnvironmentVariable("ML_DEBUG"); if (debugVar != null) DebugMode = bool.Parse(debugVar); - - if(DebugMode) + + if (DebugMode) Logger.Debug("Debug mode enabled"); } - + + public void Reload() + { + Logger.Info($"Reading config from '{PathBuilder.File("storage", "configs", "config.json")}'"); + + Configuration = new ConfigurationBuilder().AddJsonStream( + new MemoryStream(Encoding.ASCII.GetBytes( + File.ReadAllText( + PathBuilder.File("storage", "configs", "config.json") + ) + ) + )).Build(); + + Logger.Info("Reloaded configuration file"); + } + public IEnumerable GetChildren() { return Configuration.GetChildren(); diff --git a/Moonlight/App/Services/DomainService.cs b/Moonlight/App/Services/DomainService.cs index 4bdbf9b..1b2cb98 100644 --- a/Moonlight/App/Services/DomainService.cs +++ b/Moonlight/App/Services/DomainService.cs @@ -48,6 +48,28 @@ public class DomainService ); } + public Task Create(string domain, SharedDomain sharedDomain, User user) + { + if (DomainRepository.Get().Where(x => x.SharedDomain.Id == sharedDomain.Id).Any(x => x.Name == domain)) + throw new DisplayException("A domain with this name does already exist for this shared domain"); + + var res = DomainRepository.Add(new() + { + Name = domain, + SharedDomain = sharedDomain, + Owner = user + }); + + return Task.FromResult(res); + } + + public Task Delete(Domain domain) + { + DomainRepository.Delete(domain); + + return Task.CompletedTask; + } + public async Task GetAvailableDomains() // This method returns all available domains which are not added as a shared domain { diff --git a/Moonlight/App/Services/LogServices/AuditLogService.cs b/Moonlight/App/Services/LogServices/AuditLogService.cs index 4b0fdf8..a4e3124 100644 --- a/Moonlight/App/Services/LogServices/AuditLogService.cs +++ b/Moonlight/App/Services/LogServices/AuditLogService.cs @@ -73,8 +73,11 @@ public class AuditLogService { private List Data = new List(); - public void Add(object data) + public void Add(object? data) { + if(data == null) + return; + Data.Add(new LogData() { Type = typeof(T), diff --git a/Moonlight/App/Services/LogServices/ErrorLogService.cs b/Moonlight/App/Services/LogServices/ErrorLogService.cs index 4328c95..c53fa11 100644 --- a/Moonlight/App/Services/LogServices/ErrorLogService.cs +++ b/Moonlight/App/Services/LogServices/ErrorLogService.cs @@ -98,8 +98,11 @@ public class ErrorLogService { private List Data = new List(); - public void Add(object data) + public void Add(object? data) { + if(data == null) + return; + Data.Add(new LogData() { Type = typeof(T), diff --git a/Moonlight/App/Services/LogServices/SecurityLogService.cs b/Moonlight/App/Services/LogServices/SecurityLogService.cs index f58fa19..ade94fa 100644 --- a/Moonlight/App/Services/LogServices/SecurityLogService.cs +++ b/Moonlight/App/Services/LogServices/SecurityLogService.cs @@ -72,8 +72,11 @@ public class SecurityLogService { private List Data = new List(); - public void Add(object data) + public void Add(object? data) { + if(data == null) + return; + Data.Add(new LogData() { Type = typeof(T), diff --git a/Moonlight/App/Services/MailService.cs b/Moonlight/App/Services/MailService.cs index 38482a2..2676460 100644 --- a/Moonlight/App/Services/MailService.cs +++ b/Moonlight/App/Services/MailService.cs @@ -3,6 +3,7 @@ using System.Net.Mail; using Logging.Net; using Moonlight.App.Database.Entities; using Moonlight.App.Exceptions; +using Moonlight.App.Helpers; namespace Moonlight.App.Services; @@ -31,13 +32,13 @@ public class MailService Action> values ) { - if (!File.Exists($"resources/mail/{name}.html")) + if (!File.Exists(PathBuilder.File("storage", "resources", "mail", $"{name}.html"))) { Logger.Warn($"Mail template '{name}' not found. Make sure to place one in the resources folder"); throw new DisplayException("Mail template not found"); } - var rawHtml = await File.ReadAllTextAsync($"resources/mail/{name}.html"); + var rawHtml = await File.ReadAllTextAsync(PathBuilder.File("storage", "resources", "mail", $"{name}.html")); var val = new Dictionary(); values.Invoke(val); diff --git a/Moonlight/App/Services/NodeService.cs b/Moonlight/App/Services/NodeService.cs index 577214e..87f0c30 100644 --- a/Moonlight/App/Services/NodeService.cs +++ b/Moonlight/App/Services/NodeService.cs @@ -41,4 +41,22 @@ public class NodeService { return await DaemonApiHelper.Get(node, "stats/container"); } + + public async Task IsHostUp(Node node) + { + try + { + //TODO: Implement status caching + var data = await GetStatus(node); + + if (data != null) + return true; + } + catch (Exception) + { + // ignored + } + + return false; + } } \ No newline at end of file diff --git a/Moonlight/App/Services/ServerService.cs b/Moonlight/App/Services/ServerService.cs index 8ec47c9..2e46eb4 100644 --- a/Moonlight/App/Services/ServerService.cs +++ b/Moonlight/App/Services/ServerService.cs @@ -29,6 +29,7 @@ public class ServerService private readonly SecurityLogService SecurityLogService; private readonly AuditLogService AuditLogService; private readonly ErrorLogService ErrorLogService; + private readonly NodeService NodeService; public ServerService( ServerRepository serverRepository, @@ -42,7 +43,8 @@ public class ServerService WingsJwtHelper wingsJwtHelper, SecurityLogService securityLogService, AuditLogService auditLogService, - ErrorLogService errorLogService) + ErrorLogService errorLogService, + NodeService nodeService) { ServerRepository = serverRepository; WingsApiHelper = wingsApiHelper; @@ -56,6 +58,7 @@ public class ServerService SecurityLogService = securityLogService; AuditLogService = auditLogService; ErrorLogService = errorLogService; + NodeService = nodeService; } private Server EnsureNodeData(Server s) @@ -192,13 +195,14 @@ public class ServerService ServerRepository.Update(serverData); await MessageService.Emit("wings.backups.delete", backup); - + await AuditLogService.Log(AuditLogType.DeleteBackup, x => { x.Add(server.Uuid); x.Add(backup.Uuid); - }); + } + ); } public async Task DownloadBackup(Server s, ServerBackup serverBackup) @@ -210,7 +214,7 @@ public class ServerService claims.Add("server_uuid", server.Uuid.ToString()); claims.Add("backup_uuid", serverBackup.Uuid.ToString()); }); - + await AuditLogService.Log(AuditLogType.DownloadBackup, x => { @@ -218,7 +222,10 @@ public class ServerService x.Add(serverBackup.Uuid); }); - return $"https://{server.Node.Fqdn}:{server.Node.HttpPort}/download/backup?token={token}"; + if (server.Node.Ssl) + return $"https://{server.Node.Fqdn}:{server.Node.HttpPort}/download/backup?token={token}"; + else + return $"http://{server.Node.Fqdn}:{server.Node.HttpPort}/download/backup?token={token}"; } public Task CreateFileAccess(Server s, User user) // We need the user to create the launch url @@ -237,7 +244,7 @@ public class ServerService } public async Task Create(string name, int cpu, long memory, long disk, User u, Image i, Node? n = null, - Action? modifyDetails = null) + Action? modifyDetails = null, int allocations = 1) { var user = UserRepository .Get() @@ -253,7 +260,10 @@ public class ServerService if (n == null) { - node = NodeRepository.Get().Include(x => x.Allocations).First(); //TODO: Smart deploy + node = NodeRepository + .Get() + .Include(x => x.Allocations) + .First(); //TODO: Add smart deploy maybe } else { @@ -263,22 +273,27 @@ public class ServerService .First(x => x.Id == n.Id); } - NodeAllocation freeAllo; + NodeAllocation[] freeAllocations; try { - freeAllo = node.Allocations.First(a => !ServerRepository.Get() - .SelectMany(s => s.Allocations) - .Any(b => b.Id == a.Id)); // Thank you ChatGPT <3 + freeAllocations = node.Allocations + .Where(a => !ServerRepository.Get() + .SelectMany(s => s.Allocations) + .Any(b => b.Id == a.Id)) + .Take(allocations).ToArray(); } catch (Exception) { throw new DisplayException("No allocation found"); } - if (freeAllo == null) + if (!freeAllocations.Any()) throw new DisplayException("No allocation found"); + if (freeAllocations.Length != allocations) + throw new DisplayException("Not enough allocations found"); + var server = new Server() { Cpu = cpu, @@ -289,11 +304,8 @@ public class ServerService Owner = user, Node = node, Uuid = Guid.NewGuid(), - MainAllocation = freeAllo, - Allocations = new() - { - freeAllo - }, + MainAllocation = freeAllocations.First(), + Allocations = freeAllocations.ToList(), Backups = new(), OverrideStartup = "", DockerImageIndex = image.DockerImages.FindIndex(x => x.Default) @@ -321,10 +333,7 @@ public class ServerService StartOnCompletion = false }); - await AuditLogService.Log(AuditLogType.CreateServer, x => - { - x.Add(newServerData.Uuid); - }); + await AuditLogService.Log(AuditLogType.CreateServer, x => { x.Add(newServerData.Uuid); }); return newServerData; } @@ -332,11 +341,11 @@ public class ServerService { await ErrorLogService.Log(e, x => { - x.Add(newServerData.Uuid); + x.Add(newServerData.Uuid); x.Add(node.Id); }); - ServerRepository.Delete(newServerData); + ServerRepository.Delete(newServerData); //TODO Remove unsinged table stuff throw new DisplayException("Error creating server on wings"); } @@ -348,10 +357,7 @@ public class ServerService await WingsApiHelper.Post(server.Node, $"api/servers/{server.Uuid}/reinstall", null); - await AuditLogService.Log(AuditLogType.ReinstallServer, x => - { - x.Add(server.Uuid); - }); + await AuditLogService.Log(AuditLogType.ReinstallServer, x => { x.Add(server.Uuid); }); } public async Task SftpServerLogin(int serverId, int id, string password) @@ -360,10 +366,7 @@ public class ServerService if (server == null) { - await SecurityLogService.LogSystem(SecurityLogType.SftpBruteForce, x => - { - x.Add(id); - }); + await SecurityLogService.LogSystem(SecurityLogType.SftpBruteForce, x => { x.Add(id); }); throw new Exception("Server not found"); } @@ -389,10 +392,42 @@ public class ServerService public async Task Delete(Server s) { + throw new DisplayException("Deleting a server is currently a bit buggy. So its disabled for your safety"); + var server = EnsureNodeData(s); + var backups = await GetBackups(server); + + foreach (var backup in backups) + { + try + { + await DeleteBackup(server, backup); + } + catch (Exception) + { + // ignored + } + } + await WingsApiHelper.Delete(server.Node, $"api/servers/{server.Uuid}", null); + + //TODO: Fix empty data models - ServerRepository.Delete(s); + server.Allocations = new(); + server.MainAllocation = null; + server.Variables = new(); + server.Backups = new(); + + ServerRepository.Update(server); + + ServerRepository.Delete(server); + } + + public async Task IsHostUp(Server s) + { + var server = EnsureNodeData(s); + + return await NodeService.IsHostUp(server.Node); } } \ No newline at end of file diff --git a/Moonlight/App/Services/SmartDeployService.cs b/Moonlight/App/Services/SmartDeployService.cs index 0585c1d..57aa4bc 100644 --- a/Moonlight/App/Services/SmartDeployService.cs +++ b/Moonlight/App/Services/SmartDeployService.cs @@ -6,12 +6,18 @@ namespace Moonlight.App.Services; public class SmartDeployService { private readonly NodeRepository NodeRepository; + private readonly PleskServerRepository PleskServerRepository; + private readonly WebsiteService WebsiteService; private readonly NodeService NodeService; - public SmartDeployService(NodeRepository nodeRepository, NodeService nodeService) + public SmartDeployService( + NodeRepository nodeRepository, + NodeService nodeService, PleskServerRepository pleskServerRepository, WebsiteService websiteService) { NodeRepository = nodeRepository; NodeService = nodeService; + PleskServerRepository = pleskServerRepository; + WebsiteService = websiteService; } public async Task GetNode() @@ -32,6 +38,21 @@ public class SmartDeployService return data.MaxBy(x => x.Value).Key; } + public async Task GetPleskServer() + { + var result = new List(); + + foreach (var pleskServer in PleskServerRepository.Get().ToArray()) + { + if (await WebsiteService.IsHostUp(pleskServer)) + { + result.Add(pleskServer); + } + } + + return result.FirstOrDefault(); + } + private async Task GetUsageScore(Node node) { var score = 0; diff --git a/Moonlight/App/Services/Statistics/StatisticsCaptureService.cs b/Moonlight/App/Services/Statistics/StatisticsCaptureService.cs new file mode 100644 index 0000000..3098e5d --- /dev/null +++ b/Moonlight/App/Services/Statistics/StatisticsCaptureService.cs @@ -0,0 +1,57 @@ +using Moonlight.App.Database; +using Moonlight.App.Repositories; + +namespace Moonlight.App.Services.Statistics; + +public class StatisticsCaptureService +{ + private readonly DataContext DataContext; + private readonly ConfigService ConfigService; + private readonly StatisticsRepository StatisticsRepository; + private readonly IServiceScopeFactory ServiceScopeFactory; + private readonly WebsiteService WebsiteService; + private readonly PleskServerRepository PleskServerRepository; + private PeriodicTimer Timer; + + public StatisticsCaptureService(IServiceScopeFactory serviceScopeFactory, ConfigService configService) + { + ServiceScopeFactory = serviceScopeFactory; + var provider = ServiceScopeFactory.CreateScope().ServiceProvider; + + DataContext = provider.GetRequiredService(); + ConfigService = configService; + StatisticsRepository = provider.GetRequiredService(); + WebsiteService = provider.GetRequiredService(); + PleskServerRepository = provider.GetRequiredService(); + + var config = ConfigService.GetSection("Moonlight").GetSection("Statistics"); + if(!config.GetValue("Enabled")) + return; + + var _period = config.GetValue("Wait"); + var period = TimeSpan.FromMinutes(_period); + Timer = new(period); + + Task.Run(Run); + } + + private async Task Run() + { + while (await Timer.WaitForNextTickAsync()) + { + StatisticsRepository.Add("statistics.usersCount", DataContext.Users.Count()); + StatisticsRepository.Add("statistics.serversCount", DataContext.Servers.Count()); + StatisticsRepository.Add("statistics.domainsCount", DataContext.Domains.Count()); + StatisticsRepository.Add("statistics.websitesCount", DataContext.Websites.Count()); + + int databases = 0; + + await foreach (var pleskServer in PleskServerRepository.Get()) + { + databases += (await WebsiteService.GetDefaultDatabaseServer(pleskServer)).DbCount; + } + + StatisticsRepository.Add("statistics.databasesCount", databases); + } + } +} \ No newline at end of file diff --git a/Moonlight/App/Services/Statistics/StatisticsViewService.cs b/Moonlight/App/Services/Statistics/StatisticsViewService.cs new file mode 100644 index 0000000..329445f --- /dev/null +++ b/Moonlight/App/Services/Statistics/StatisticsViewService.cs @@ -0,0 +1,24 @@ +using Moonlight.App.Database.Entities; +using Moonlight.App.Models.Misc; +using Moonlight.App.Repositories; + +namespace Moonlight.App.Services.Statistics; + +public class StatisticsViewService +{ + private readonly StatisticsRepository StatisticsRepository; + + public StatisticsViewService(StatisticsRepository statisticsRepository) + { + StatisticsRepository = statisticsRepository; + } + + public StatisticsData[] GetData(string chart, StatisticsTimeSpan timeSpan) + { + var startDate = DateTime.Now - TimeSpan.FromHours((int)timeSpan); + + var objs = StatisticsRepository.Get().Where(x => x.Date > startDate && x.Chart == chart); + + return objs.ToArray(); + } +} \ No newline at end of file diff --git a/Moonlight/App/Services/StorageService.cs b/Moonlight/App/Services/StorageService.cs new file mode 100644 index 0000000..f947458 --- /dev/null +++ b/Moonlight/App/Services/StorageService.cs @@ -0,0 +1,58 @@ +using Logging.Net; +using Moonlight.App.Helpers; + +namespace Moonlight.App.Services; + +public class StorageService +{ + public StorageService() + { + EnsureCreated(); + } + + public void EnsureCreated() + { + Directory.CreateDirectory(PathBuilder.Dir("storage", "uploads")); + Directory.CreateDirectory(PathBuilder.Dir("storage", "configs")); + Directory.CreateDirectory(PathBuilder.Dir("storage", "resources")); + + if(IsEmpty(PathBuilder.Dir("storage", "resources"))) + { + Logger.Info("Default resources not found. Copying default resources"); + + CopyFilesRecursively( + PathBuilder.Dir("defaultstorage", "resources"), + PathBuilder.Dir("storage", "resources") + ); + } + + if (IsEmpty(PathBuilder.Dir("storage", "configs"))) + { + Logger.Info("Default configs not found. Copying default configs"); + + CopyFilesRecursively( + PathBuilder.Dir("defaultstorage", "configs"), + PathBuilder.Dir("storage", "configs") + ); + } + } + + private bool IsEmpty(string path) + { + return !Directory.EnumerateFileSystemEntries(path).Any(); + } + private static void CopyFilesRecursively(string sourcePath, string targetPath) + { + //Now Create all of the directories + foreach (string dirPath in Directory.GetDirectories(sourcePath, "*", SearchOption.AllDirectories)) + { + Directory.CreateDirectory(dirPath.Replace(sourcePath, targetPath)); + } + + //Copy all the files & Replaces any files with the same name + foreach (string newPath in Directory.GetFiles(sourcePath, "*.*",SearchOption.AllDirectories)) + { + File.Copy(newPath, newPath.Replace(sourcePath, targetPath), true); + } + } +} \ No newline at end of file diff --git a/Moonlight/App/Services/UserService.cs b/Moonlight/App/Services/UserService.cs index 9cf6aa7..6a6fe02 100644 --- a/Moonlight/App/Services/UserService.cs +++ b/Moonlight/App/Services/UserService.cs @@ -18,6 +18,7 @@ public class UserService private readonly AuditLogService AuditLogService; private readonly MailService MailService; private readonly IdentityService IdentityService; + private readonly IpLocateService IpLocateService; private readonly string JwtSecret; @@ -28,7 +29,7 @@ public class UserService SecurityLogService securityLogService, AuditLogService auditLogService, MailService mailService, - IdentityService identityService) + IdentityService identityService, IpLocateService ipLocateService) { UserRepository = userRepository; TotpService = totpService; @@ -36,6 +37,7 @@ public class UserService AuditLogService = auditLogService; MailService = mailService; IdentityService = identityService; + IpLocateService = ipLocateService; JwtSecret = configService .GetSection("Moonlight") @@ -77,6 +79,7 @@ public class UserService }); await MailService.SendMail(user!, "register", values => {}); + await AuditLogService.Log(AuditLogType.Register, x => { x.Add(user.Email); @@ -177,11 +180,13 @@ public class UserService } else { + var location = await IpLocateService.GetLocation(); + await MailService.SendMail(user!, "passwordChange", values => { values.Add("Ip", IdentityService.GetIp()); values.Add("Device", IdentityService.GetDevice()); - values.Add("Location", "In your walls"); + values.Add("Location", location); }); await AuditLogService.Log(AuditLogType.ChangePassword, x => @@ -201,6 +206,7 @@ public class UserService { x.Add(id); }); + throw new Exception("Invalid username"); } @@ -223,12 +229,17 @@ public class UserService public async Task GenerateToken(User user, bool sendMail = false) { - await MailService.SendMail(user!, "login", values => + var location = await IpLocateService.GetLocation(); + + if (sendMail) { - values.Add("Ip", IdentityService.GetIp()); - values.Add("Device", IdentityService.GetDevice()); - values.Add("Location", "In your walls"); - }); + await MailService.SendMail(user!, "login", values => + { + values.Add("Ip", IdentityService.GetIp()); + values.Add("Device", IdentityService.GetDevice()); + values.Add("Location", location); + }); + } var token = JwtBuilder.Create() .WithAlgorithm(new HMACSHA256Algorithm()) @@ -257,11 +268,13 @@ public class UserService await AuditLogService.Log(AuditLogType.PasswordReset, x => {}); + var location = await IpLocateService.GetLocation(); + await MailService.SendMail(user, "passwordReset", values => { values.Add("Ip", IdentityService.GetIp()); values.Add("Device", IdentityService.GetDevice()); - values.Add("Location", "In your walls"); + values.Add("Location", location); values.Add("Password", newPassword); }); } diff --git a/Moonlight/App/Services/WebsiteService.cs b/Moonlight/App/Services/WebsiteService.cs new file mode 100644 index 0000000..1190603 --- /dev/null +++ b/Moonlight/App/Services/WebsiteService.cs @@ -0,0 +1,383 @@ +using Logging.Net; +using Microsoft.EntityFrameworkCore; +using Moonlight.App.Database.Entities; +using Moonlight.App.Exceptions; +using Moonlight.App.Helpers; +using Moonlight.App.Helpers.Files; +using Moonlight.App.Models.Plesk.Requests; +using Moonlight.App.Models.Plesk.Resources; +using Moonlight.App.Repositories; +using FileAccess = Moonlight.App.Helpers.Files.FileAccess; + +namespace Moonlight.App.Services; + +public class WebsiteService +{ + private readonly WebsiteRepository WebsiteRepository; + private readonly PleskServerRepository PleskServerRepository; + private readonly PleskApiHelper PleskApiHelper; + private readonly UserRepository UserRepository; + + public WebsiteService(WebsiteRepository websiteRepository, PleskApiHelper pleskApiHelper, PleskServerRepository pleskServerRepository, UserRepository userRepository) + { + WebsiteRepository = websiteRepository; + PleskApiHelper = pleskApiHelper; + PleskServerRepository = pleskServerRepository; + UserRepository = userRepository; + } + + public async Task Create(string baseDomain, User owner, PleskServer? ps = null) + { + if (WebsiteRepository.Get().Any(x => x.BaseDomain == baseDomain)) + throw new DisplayException("A website with this domain does already exist"); + + var pleskServer = ps ?? PleskServerRepository.Get().First(); + + var ftpLogin = baseDomain; + var ftpPassword = StringHelper.GenerateString(16); + + var w = new Website() + { + PleskServer = pleskServer, + Owner = owner, + BaseDomain = baseDomain, + PleskId = 0, + FtpPassword = ftpPassword, + FtpLogin = ftpLogin + }; + + var website = WebsiteRepository.Add(w); + + try + { + var id = await GetAdminAccount(pleskServer); + + var result = await PleskApiHelper.Post(pleskServer, "domains", new CreateDomain() + { + Description = $"moonlight website {website.Id}", + Name = baseDomain, + HostingType = "virtual", + Plan = new() + { + Name = "Unlimited" + }, + HostingSettings = new() + { + FtpLogin = ftpLogin, + FtpPassword = ftpPassword + }, + OwnerClient = new() + { + Id = id + } + }); + + website.PleskId = result.Id; + + WebsiteRepository.Update(website); + } + catch (Exception e) + { + WebsiteRepository.Delete(website); + throw; + } + + return website; + } + + public async Task Delete(Website w) + { + var website = EnsureData(w); + + await PleskApiHelper.Delete(website.PleskServer, $"domains/{w.PleskId}", null); + + WebsiteRepository.Delete(website); + } + + public async Task IsHostUp(PleskServer pleskServer) + { + try + { + var res = await PleskApiHelper.Get(pleskServer, "server"); + + if (res != null) + return true; + } + catch (Exception e) + { + // ignored + } + + return false; + } + + public async Task IsHostUp(Website w) + { + var website = EnsureData(w); + + try + { + var res = await PleskApiHelper.Get(website.PleskServer, "server"); + + if (res != null) + return true; + } + catch (Exception) + { + // ignored + } + + return false; + } + + #region Get host + + public async Task GetHost(PleskServer pleskServer) + { + return (await PleskApiHelper.Get(pleskServer, "server")).Hostname; + } + + public async Task GetHost(Website w) + { + var website = EnsureData(w); + + return await GetHost(website.PleskServer); + } + + #endregion + + private async Task GetAdminAccount(PleskServer pleskServer) + { + var users = await PleskApiHelper.Get(pleskServer, "clients"); + + var user = users.FirstOrDefault(x => x.Type == "admin"); + + if (user == null) + throw new DisplayException("No admin account in plesk found"); + + return user.Id; + } + + #region SSL + public async Task GetSslCertificates(Website w) + { + var website = EnsureData(w); + var certs = new List(); + + var data = await ExecuteCli(website.PleskServer, "certificate", p => + { + p.Add("-l"); + p.Add("-domain"); + p.Add(w.BaseDomain); + }); + + string[] lines = data.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.RemoveEmptyEntries); + + foreach (string line in lines) + { + if (line.Contains("Lets Encrypt")) + { + string[] parts = line.Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries); + + if(parts.Length > 6) + certs.Add($"{parts[4]} {parts[5]} {parts[6]}"); + } + else if (line.Contains("Listing of SSL/TLS certificates repository was successful")) + { + // This line indicates the end of the certificate listing, so we can stop parsing + break; + } + } + + return certs.ToArray(); + } + + public async Task CreateSslCertificate(Website w) + { + var website = EnsureData(w); + + await ExecuteCli(website.PleskServer, "extension", p => + { + p.Add("--exec"); + p.Add("letsencrypt"); + p.Add("cli.php"); + p.Add("-d"); + p.Add(website.BaseDomain); + p.Add("-m"); + p.Add(website.Owner.Email); + }); + } + + public async Task DeleteSslCertificate(Website w, string name) + { + var website = EnsureData(w); + + try + { + await ExecuteCli(website.PleskServer, "site", p => + { + p.Add("-u"); + p.Add(website.BaseDomain); + p.Add("-ssl"); + p.Add("false"); + }); + + try + { + await ExecuteCli(website.PleskServer, "certificate", p => + { + p.Add("--remove"); + p.Add(name); + p.Add("-domain"); + p.Add(website.BaseDomain); + }); + } + catch (Exception e) + { + Logger.Warn("Error removing ssl certificate"); + Logger.Warn(e); + + throw new DisplayException("An unknown error occured while removing ssl certificate"); + } + } + catch (DisplayException) + { + // Redirect all display exception to soft error handler + throw; + } + catch (Exception e) + { + Logger.Warn("Error disabling ssl certificate"); + Logger.Warn(e); + + throw new DisplayException("An unknown error occured while disabling ssl certificate"); + } + } + + #endregion + + #region Databases + + public async Task GetDatabases(Website w) + { + var website = EnsureData(w); + + var dbs = await PleskApiHelper.Get( + website.PleskServer, + $"databases?domain={w.BaseDomain}" + ); + + return dbs; + } + + public async Task CreateDatabase(Website w, string name, string password) + { + var website = EnsureData(w); + + var server = await GetDefaultDatabaseServer(website); + + if (server == null) + throw new DisplayException("No database server marked as default found"); + + var dbReq = new CreateDatabase() + { + Name = name, + Type = "mysql", + ParentDomain = new() + { + Name = website.BaseDomain + }, + ServerId = server.Id + }; + + var db = await PleskApiHelper.Post(website.PleskServer, "databases", dbReq); + + if (db == null) + throw new DisplayException("Unable to create database via api"); + + var dbUserReq = new CreateDatabaseUser() + { + DatabaseId = db.Id, + Login = name, + Password = password + }; + + await PleskApiHelper.Post(website.PleskServer, "dbusers", dbUserReq); + } + + public async Task DeleteDatabase(Website w, Models.Plesk.Resources.Database database) + { + var website = EnsureData(w); + + var dbUsers = await PleskApiHelper.Get( + website.PleskServer, + $"dbusers?dbId={database.Id}" + ); + + foreach (var dbUser in dbUsers) + { + await PleskApiHelper.Delete(website.PleskServer, $"dbusers/{dbUser.Id}", null); + } + + await PleskApiHelper.Delete(website.PleskServer, $"databases/{database.Id}", null); + } + + public async Task GetDefaultDatabaseServer(PleskServer pleskServer) + { + var dbServers = await PleskApiHelper.Get(pleskServer, "dbservers"); + + return dbServers.FirstOrDefault(x => x.IsDefault); + } + + public async Task GetDefaultDatabaseServer(Website w) + { + var website = EnsureData(w); + + return await GetDefaultDatabaseServer(website.PleskServer); + } + + #endregion + + public async Task CreateFileAccess(Website w) + { + var website = EnsureData(w); + var host = await GetHost(website.PleskServer); + + return new FtpFileAccess(host, 21, website.FtpLogin, website.FtpPassword); + } + + private async Task ExecuteCli( + PleskServer server, + string cli, Action>? parameters = null, + Action>? variables = null + ) + { + var p = new List(); + var v = new Dictionary(); + + parameters?.Invoke(p); + variables?.Invoke(v); + + var req = new CliCall() + { + Env = v, + Params = p + }; + + var res = await PleskApiHelper.Post(server, $"cli/{cli}/call", req); + + return res.Stdout; + } + + private Website EnsureData(Website website) + { + if (website.PleskServer == null || website.Owner == null) + return WebsiteRepository + .Get() + .Include(x => x.PleskServer) + .Include(x => x.Owner) + .First(x => x.Id == website.Id); + + return website; + } +} \ No newline at end of file diff --git a/Moonlight/Dockerfile b/Moonlight/Dockerfile index 8f612a2..f99a733 100644 --- a/Moonlight/Dockerfile +++ b/Moonlight/Dockerfile @@ -19,4 +19,7 @@ RUN dotnet publish "Moonlight.csproj" -c Release -o /app/publish FROM base AS final WORKDIR /app COPY --from=publish /app/publish . +RUN mkdir -p /app/storage +RUN rm -r /app/storage/* +COPY "Moonlight/defaultstorage" "/app/defaultstorage" ENTRYPOINT ["dotnet", "Moonlight.dll"] \ No newline at end of file diff --git a/Moonlight/Moonlight.csproj b/Moonlight/Moonlight.csproj index f4e5915..1c684c6 100644 --- a/Moonlight/Moonlight.csproj +++ b/Moonlight/Moonlight.csproj @@ -9,20 +9,22 @@ - + - + + + all runtime; build; native; contentfiles; analyzers; buildtransitive @@ -60,6 +62,8 @@ <_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" /> + <_ContentIncludedByDefault Remove="Shared\Components\News\NewsEditor.razor" /> + <_ContentIncludedByDefault Remove="Shared\News\Edit.razor" /> diff --git a/Moonlight/Pages/_Layout.cshtml b/Moonlight/Pages/_Layout.cshtml index b82364f..7a5518e 100644 --- a/Moonlight/Pages/_Layout.cshtml +++ b/Moonlight/Pages/_Layout.cshtml @@ -45,8 +45,7 @@ - - + @@ -87,13 +86,12 @@ - - - - + + + @@ -114,5 +112,7 @@ + + \ No newline at end of file diff --git a/Moonlight/Program.cs b/Moonlight/Program.cs index ccd680c..76e0189 100644 --- a/Moonlight/Program.cs +++ b/Moonlight/Program.cs @@ -16,6 +16,7 @@ using Moonlight.App.Services.LogServices; using Moonlight.App.Services.Notifications; using Moonlight.App.Services.OAuth2; using Moonlight.App.Services.Sessions; +using Moonlight.App.Services.Statistics; using Moonlight.App.Services.Support; namespace Moonlight @@ -60,7 +61,12 @@ namespace Moonlight builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); + builder.Services.AddScoped(); + builder.Services.AddScoped(); builder.Services.AddScoped(); + builder.Services.AddScoped(); + + builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); @@ -68,6 +74,7 @@ namespace Moonlight // Services builder.Services.AddSingleton(); + builder.Services.AddSingleton(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); @@ -90,6 +97,8 @@ namespace Moonlight builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); + builder.Services.AddScoped(); + builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); @@ -121,9 +130,11 @@ namespace Moonlight builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddScoped(); + builder.Services.AddScoped(); // Background services builder.Services.AddSingleton(); + builder.Services.AddSingleton(); // Third party services builder.Services.AddBlazorTable(); @@ -156,6 +167,7 @@ namespace Moonlight // AutoStart services _ = app.Services.GetRequiredService(); _ = app.Services.GetRequiredService(); + _ = app.Services.GetRequiredService(); // Discord bot service //var discordBotService = app.Services.GetRequiredService(); diff --git a/Moonlight/README.md b/Moonlight/README.md new file mode 100644 index 0000000..264af4c --- /dev/null +++ b/Moonlight/README.md @@ -0,0 +1,13 @@ +### Some explanations + +defaultstorage: + +This directory is for the default assets of a moonlight instance, e.g. lang files, images etc + +storage: + +This directory is empty in fresh moonlight instances and will be populated with example config upon first run. +Before using moonlight this config file has to be modified. +Also resources are going to be copied from the default storage to this storage. +To access files in this storage we recommend to use the PathBuilder functions to ensure cross platform compatibility. +The storage directory should be mounted to a specific path when using docker container so when the container is replaced/rebuild your storage will not be modified \ No newline at end of file diff --git a/Moonlight/Shared/Components/Auth/Register.razor b/Moonlight/Shared/Components/Auth/Register.razor index ab8010b..0b284a2 100644 --- a/Moonlight/Shared/Components/Auth/Register.razor +++ b/Moonlight/Shared/Components/Auth/Register.razor @@ -7,11 +7,17 @@ @using Moonlight.App.Services @using Moonlight.App.Services.OAuth2 +@using Moonlight.App.Models.Forms +@using Moonlight.App.Services.Interop +@using Moonlight.App.Services.Sessions @inject SmartTranslateService SmartTranslateService @inject GoogleOAuth2Service GoogleOAuth2Service @inject NavigationManager NavigationManager @inject DiscordOAuth2Service DiscordOAuth2Service +@inject AlertService AlertService +@inject UserService UserService +@inject CookieService CookieService
@@ -46,38 +52,44 @@
-
+
Or with email
+ + +
+ +
+ +
+
+ +
-
- -
+
+ +
+
-
- -
- -
- -
- -
- -
- -
- -
- -
- -
+
+
+ +
+
+ +
+
+ +
+ +
+
+
Already registered? @@ -93,6 +105,8 @@ @code { + private UserRegisterModel UserRegisterModel = new(); + private async Task DoGoogle() { var url = await GoogleOAuth2Service.GetUrl(); @@ -104,4 +118,21 @@ var url = await DiscordOAuth2Service.GetUrl(); NavigationManager.NavigateTo(url, true); } + + private async Task CreateUser() + { + if (UserRegisterModel.ConfirmPassword != UserRegisterModel.Password) + { + await AlertService.Error(SmartTranslateService.Translate("Passwords need to match")); + return; + } + + var token = await UserService.Register(UserRegisterModel.Email, UserRegisterModel.Password, UserRegisterModel.FirstName, UserRegisterModel.LastName); + await CookieService.SetValue("token", token, 10); + + if (NavigationManager.Uri.EndsWith("register")) + NavigationManager.NavigateTo("/", true); + else + NavigationManager.NavigateTo(NavigationManager.Uri, true); + } } diff --git a/Moonlight/Shared/Components/ErrorBoundaries/GlobalErrorBoundary.razor b/Moonlight/Shared/Components/ErrorBoundaries/GlobalErrorBoundary.razor index e3771cd..489a5a6 100644 --- a/Moonlight/Shared/Components/ErrorBoundaries/GlobalErrorBoundary.razor +++ b/Moonlight/Shared/Components/ErrorBoundaries/GlobalErrorBoundary.razor @@ -52,10 +52,7 @@ else { receivedExceptions.Add(exception); - var user = await IdentityService.Get(); - var id = user == null ? -1 : user.Id; - - Logger.Error($"[{id}] An unhanded exception occured:"); + Logger.Error($"An unhanded exception occured:"); Logger.Error(exception); await base.OnErrorAsync(exception); diff --git a/Moonlight/Shared/Components/ErrorBoundaries/SoftErrorBoundary.razor b/Moonlight/Shared/Components/ErrorBoundaries/SoftErrorBoundary.razor index ca47b59..3d59dd7 100644 --- a/Moonlight/Shared/Components/ErrorBoundaries/SoftErrorBoundary.razor +++ b/Moonlight/Shared/Components/ErrorBoundaries/SoftErrorBoundary.razor @@ -7,13 +7,35 @@ @inject AlertService AlertService @inject SmartTranslateService SmartTranslateService -@ChildContent +@if (Crashed) +{ +
+
+
+
+ + Ooops. This page is crashed + +
+
+ This page is crashed. The error has been reported to the moonlight team. Meanwhile you can try reloading the page +
+
+
+
+} +else +{ + @ChildContent +} @code { + private bool Crashed = false; + protected override async Task OnErrorAsync(Exception exception) { - Logger.Debug(exception); + Logger.Warn(exception); if (exception is DisplayException displayException) { @@ -36,9 +58,21 @@ wingsException.Message ); } + else if (exception is PleskException pleskException) + { + await AlertService.Error( + SmartTranslateService.Translate("Error from plesk"), + pleskException.Message + ); + } + else if (exception is NotImplementedException) + { + await AlertService.Error(SmartTranslateService.Translate("This function is not implemented")); + } else { - throw exception; + Crashed = true; + await InvokeAsync(StateHasChanged); } } } \ No newline at end of file diff --git a/Moonlight/Shared/Components/FileManagerPartials/FileManager.razor b/Moonlight/Shared/Components/FileManagerPartials/FileManager.razor index f1f9eea..5d0c8d0 100644 --- a/Moonlight/Shared/Components/FileManagerPartials/FileManager.razor +++ b/Moonlight/Shared/Components/FileManagerPartials/FileManager.razor @@ -24,7 +24,7 @@ else {
-
+
@@ -59,7 +59,7 @@ else else {
-
+
-@if (Editing == null) -{ -
-
-
- - - - - - - -
-
-
- @if (SelectedFiles.Count == 0) - { -
- - - - -
- } - else - { -
-
- - @(SelectedFiles.Count) - - Selected -
- - -
- } -
-
-
-
-
-
- @{ - var vx = "/"; - } - / - - - - - - @{ - var cp = "/"; - var lp = "/"; - var pathParts = CurrentPath.Replace("\\", "/").Split('/', StringSplitOptions.RemoveEmptyEntries); - foreach (var path in pathParts) - { - lp = cp; - @(path) - - - - - - - cp += path + "/"; - } - } -
-
-
- -
-} -else -{ - if (Loading) - { -
- - Loading - -
- } - else - { - - } -} -
- - - Rename - Move - Archive - Unarchive - Download - Delete - - -@code -{ - [Parameter] - public IFileAccess FileAccess { get; set; } - - // Data - - private List SelectedFiles { get; set; } = new(); - private List Objects { get; set; } = new(); - private string CurrentPath = ""; - - // Search - private string SearchValue = ""; - - private string Search - { - get { return SearchValue; } - set - { - SearchValue = value; - InvokeAsync(StateHasChanged); - } - } - - // States - private bool Loading = false; - - // States - Editor - private FileManagerObject? Editing = null; - private string InitialEditorData = ""; - private string Language = "plaintext"; - - // States - File Upload - private bool Uploading = false; - private int Percent = 0; - - protected override async Task OnAfterRenderAsync(bool firstRender) - { - if (firstRender) - { - await RefreshActive(); - } - } - - private async Task RefreshActive() - { - Loading = true; - await InvokeAsync(StateHasChanged); - - await Refresh(false); - - Loading = false; - await InvokeAsync(StateHasChanged); - } - - private async Task Refresh(bool rerender = true) - { - SelectedFiles.Clear(); - Objects.Clear(); - CurrentPath = await FileAccess.GetCurrentPath(); - - var data = await FileAccess.GetDirectoryContent(); - Objects = data.ToList(); - - if (rerender) - await InvokeAsync(StateHasChanged); - } - - private async Task CdPath(string path) - { - await FileAccess.ChangeDirectory(path); - await RefreshActive(); - } - - private async Task SetPath(string path) - { - await FileAccess.SetDirectory(path); - await RefreshActive(); - } - - private async Task OnContextMenuClick(ItemClickEventArgs e) - { - var data = e.Data as FileManagerObject; - - if (data == null) - return; - - switch (e.MenuItem.Id) - { - case "delete": - await FileAccess.Delete(data); - break; - case "download": - if (data.IsFile) - { - try - { - var stream = await FileAccess.GetDownloadStream(data); - await ToastService.Info(TranslationService.Translate("Starting download")); - await FileService.AddBuffer(stream); - await FileService.DownloadBinaryBuffers(data.Name); - } - catch (NotImplementedException) - { - try - { - var url = await FileAccess.GetDownloadUrl(data); - NavigationManager.NavigateTo(url, true); - await ToastService.Info(TranslationService.Translate("Starting download")); - } - catch (Exception exception) - { - await ToastService.Error(TranslationService.Translate("Error starting download")); - Logger.Error("Error downloading file"); - Logger.Error(exception.Message); - } - } - catch (Exception exception) - { - await ToastService.Error(TranslationService.Translate("Error starting download")); - Logger.Error("Error downloading file stream"); - Logger.Error(exception.Message); - } - } - break; - case "rename": - var newName = await AlertService.Text(TranslationService.Translate("Rename"), TranslationService.Translate("Enter a new name"), data.Name); - var path = await FileAccess.GetCurrentPath(); - await FileAccess.Move(data, path + "/" + newName); - break; - } - - await Refresh(false); - } - - private async Task OnFileToggle(ChangeEventArgs obj, FileManagerObject o) - { - if ((bool)obj.Value) - { - if (SelectedFiles.Contains(o)) - return; - - SelectedFiles.Add(o); - await InvokeAsync(StateHasChanged); - } - else - { - if (!SelectedFiles.Contains(o)) - return; - - SelectedFiles.Remove(o); - await InvokeAsync(StateHasChanged); - } - } - - private async Task OnAllFileToggle(ChangeEventArgs obj) - { - if ((bool)obj.Value) - { - foreach (var o in Objects) - { - if (SelectedFiles.Contains(o)) - continue; - - SelectedFiles.Add(o); - } - - await InvokeAsync(StateHasChanged); - } - else - { - foreach (var o in Objects) - { - if (!SelectedFiles.Contains(o)) - continue; - - SelectedFiles.Remove(o); - } - - - await InvokeAsync(StateHasChanged); - } - } - - private async Task CreateFolder() - { - var name = await AlertService.Text(TranslationService.Translate("Create a new folder"), TranslationService.Translate("Enter a name"), ""); - - if (string.IsNullOrEmpty(name)) - return; - - await FileAccess.CreateDirectory(name); - await Refresh(); - } - - private async void SaveFile(string data) - { - if (Editing == null) - return; - - await FileAccess.WriteFile(Editing, data); - Editing = null; - await Refresh(); - } - - private async Task OpenFile(FileManagerObject o) - { - Editing = o; - Loading = true; - await InvokeAsync(StateHasChanged); - - InitialEditorData = await FileAccess.ReadFile(Editing); - Language = MonacoTypeHelper.GetEditorType(Editing.Name); - - Loading = false; - await InvokeAsync(StateHasChanged); - } - - private async void CloseFile() - { - Editing = null; - await InvokeAsync(StateHasChanged); - } - - private async Task Launch() - { - NavigationManager.NavigateTo(await FileAccess.GetLaunchUrl()); - } - - private async Task OnFileChanged(InputFileChangeEventArgs arg) - { - Uploading = true; - await InvokeAsync(StateHasChanged); - - foreach (var browserFile in arg.GetMultipleFiles()) - { - if (browserFile.Size < 1024 * 1024 * 100) - { - Percent = 0; - - try - { - await FileAccess.UploadFile( - browserFile.Name, - browserFile.OpenReadStream(1024 * 1024 * 100), - async (i) => - { - Percent = i; - - Task.Run(() => { InvokeAsync(StateHasChanged); }); - }); - - await Refresh(); - } - catch (Exception e) - { - await ToastService.Error(TranslationService.Translate("An unknown error occured while uploading a file")); - Logger.Error("Error uploading file"); - Logger.Error(e); - } - } - else - { - await ToastService.Error(TranslationService.Translate("The uploaded file should not be bigger than 100MB")); - } - } - - Uploading = false; - await InvokeAsync(StateHasChanged); - - await ToastService.Success(TranslationService.Translate("File upload complete")); - } -} \ No newline at end of file diff --git a/Moonlight/Shared/Components/FileManagerPartials/FileView.razor b/Moonlight/Shared/Components/FileManagerPartials/FileView.razor index be3bafd..dcf0a34 100644 --- a/Moonlight/Shared/Components/FileManagerPartials/FileView.razor +++ b/Moonlight/Shared/Components/FileManagerPartials/FileView.razor @@ -97,7 +97,7 @@ @(Formatter.FormatSize(file.Size))
-
+
@if (ContextActions.Any()) { diff --git a/Moonlight/Shared/Components/Forms/DeleteButton.razor b/Moonlight/Shared/Components/Forms/DeleteButton.razor index 4c37ec9..e77ef22 100644 --- a/Moonlight/Shared/Components/Forms/DeleteButton.razor +++ b/Moonlight/Shared/Components/Forms/DeleteButton.razor @@ -6,14 +6,14 @@ @if (!Working) { - } else { - } @@ -27,6 +27,9 @@ else [Parameter] public bool Confirm { get; set; } = false; + [Parameter] + public string AdditionalCssClasses { get; set; } = ""; + private async Task Do() { Working = true; @@ -35,12 +38,7 @@ else { if (Confirm) { - var b = await AlertService.YesNo( - SmartTranslateService.Translate("Are you sure?"), - SmartTranslateService.Translate("Do you really want to delete it?"), - SmartTranslateService.Translate("Yes"), - SmartTranslateService.Translate("No") - ); + var b = await AlertService.ConfirmMath(); if (b) { diff --git a/Moonlight/Shared/Components/Forms/SmartDropdown.razor b/Moonlight/Shared/Components/Forms/SmartDropdown.razor new file mode 100644 index 0000000..868d035 --- /dev/null +++ b/Moonlight/Shared/Components/Forms/SmartDropdown.razor @@ -0,0 +1,93 @@ +@typeparam T +@using Logging.Net +@inherits InputBase + + + +@code { + + [Parameter] + public IEnumerable Items { get; set; } + + [Parameter] + public Func DisplayFunc { get; set; } + + [Parameter] + public Func SearchProp { get; set; } + + private string SearchTerm + { + get => searchTerm; + set + { + FilteredItems = Items.Where(i => SearchProp(i).Contains(SearchTerm, StringComparison.OrdinalIgnoreCase)).ToList(); + searchTerm = value; + } + } + + private string searchTerm = ""; + + private List FilteredItems = new(); + + private void SelectItem(T item) + { + CurrentValue = item; + SearchTerm = ""; + FilteredItems.Clear(); + } + + protected override bool TryParseValueFromString(string? value, out T result, out string? validationErrorMessage) + { + // Check if the value is null or empty + if (string.IsNullOrEmpty(value)) + { + result = default(T)!; + validationErrorMessage = "Value cannot be null or empty"; + return false; + } + + // Try to find an item that matches the search term + var item = FilteredItems.FirstOrDefault(i => SearchProp(i).Equals(value, StringComparison.OrdinalIgnoreCase)); + if (item != null) + { + result = item; + validationErrorMessage = null; + return true; + } + else + { + result = default(T)!; + validationErrorMessage = $"No item found for search term '{value}'"; + return false; + } + } + +} \ No newline at end of file diff --git a/Moonlight/Shared/Components/Forms/SmartSelect.razor b/Moonlight/Shared/Components/Forms/SmartSelect.razor index ea24682..970f180 100644 --- a/Moonlight/Shared/Components/Forms/SmartSelect.razor +++ b/Moonlight/Shared/Components/Forms/SmartSelect.razor @@ -11,10 +11,13 @@ @code { [Parameter] - public TField[] Items { get; set; } + public IEnumerable Items { get; set; } [Parameter] public Func DisplayField { get; set; } + + [Parameter] + public Func? OnChange { get; set; } protected override void OnInitialized() { @@ -53,6 +56,7 @@ { Value = i; ValueChanged.InvokeAsync(i); + OnChange?.Invoke(); } } } diff --git a/Moonlight/Shared/Components/Navigations/AdminSystemNavigation.razor b/Moonlight/Shared/Components/Navigations/AdminSystemNavigation.razor index 18385f6..bb235e5 100644 --- a/Moonlight/Shared/Components/Navigations/AdminSystemNavigation.razor +++ b/Moonlight/Shared/Components/Navigations/AdminSystemNavigation.razor @@ -39,6 +39,11 @@ Discord bot +
diff --git a/Moonlight/Shared/Components/Navigations/AdminWebsitesNavigation.razor b/Moonlight/Shared/Components/Navigations/AdminWebsitesNavigation.razor new file mode 100644 index 0000000..40b9802 --- /dev/null +++ b/Moonlight/Shared/Components/Navigations/AdminWebsitesNavigation.razor @@ -0,0 +1,22 @@ + + +@code +{ + [Parameter] + public int Index { get; set; } = 0; +} \ No newline at end of file diff --git a/Moonlight/Shared/Components/Partials/SidebarMenu.razor b/Moonlight/Shared/Components/Partials/SidebarMenu.razor index 37dd7d6..c7e6c69 100644 --- a/Moonlight/Shared/Components/Partials/SidebarMenu.razor +++ b/Moonlight/Shared/Components/Partials/SidebarMenu.razor @@ -52,14 +52,6 @@ else Websites
-
-