From 46f544b5f83866674f52b53b158cd7fa0a065a88 Mon Sep 17 00:00:00 2001 From: Marcel Baumgartner Date: Thu, 6 Apr 2023 00:14:15 +0200 Subject: [PATCH] Added website dash, files, ftp, databases, ssl. added plesk online check, did some renamming and stuff . Fixed some things --- Moonlight/App/Database/DataContext.cs | 2 + .../App/Database/Entities/PleskServer.cs | 9 + Moonlight/App/Database/Entities/Website.cs | 12 + ...522_AddedPleskAndWebsiteModels.Designer.cs | 946 +++++++++++++++++ ...230404181522_AddedPleskAndWebsiteModels.cs | 84 ++ ...0405162507_UpdatedWebsiteModel.Designer.cs | 954 ++++++++++++++++++ .../20230405162507_UpdatedWebsiteModel.cs | 40 + .../Migrations/DataContextModelSnapshot.cs | 78 ++ Moonlight/App/Exceptions/DaemonException.cs | 32 + Moonlight/App/Exceptions/PleskException.cs | 32 + Moonlight/App/Helpers/DaemonApiHelper.cs | 2 +- Moonlight/App/Helpers/PleskApiHelper.cs | 220 ++++ .../App/Models/Forms/PleskServerDataModel.cs | 16 + .../App/Models/Forms/WebsiteAdminDataModel.cs | 14 + .../App/Models/Forms/WebsiteDataModel.cs | 10 + .../App/Models/Plesk/Requests/CliCall.cs | 10 + .../App/Models/Plesk/Requests/CreateDomain.cs | 45 + .../App/Models/Plesk/Resources/CliResult.cs | 13 + .../App/Models/Plesk/Resources/Client.cs | 45 + .../Models/Plesk/Resources/CreateResult.cs | 12 + .../Models/Plesk/Resources/ServerStatus.cs | 18 + .../App/Repositories/PleskServerRepository.cs | 44 + .../App/Repositories/WebsiteRepository.cs | 44 + Moonlight/App/Services/NodeService.cs | 18 + Moonlight/App/Services/ServerService.cs | 18 +- Moonlight/App/Services/WebsiteService.cs | 236 +++++ Moonlight/Program.cs | 4 + .../ErrorBoundaries/SoftErrorBoundary.razor | 7 + .../Components/Forms/DeleteButton.razor | 7 +- .../Navigations/AdminWebsitesNavigation.razor | 22 + .../Components/Partials/SidebarMenu.razor | 29 +- .../WebsiteControl/WebsiteDashboard.razor | 101 ++ .../WebsiteControl/WebsiteFiles.razor | 24 + .../WebsiteControl/WebsiteFtp.razor | 64 ++ .../WebsiteControl/WebsiteNavigation.razor | 57 ++ .../Shared/Views/Admin/Servers/New.razor | 2 - .../Shared/Views/Admin/Websites/Index.razor | 93 ++ .../Shared/Views/Admin/Websites/New.razor | 79 ++ .../Views/Admin/Websites/Servers/Edit.razor | 98 ++ .../Views/Admin/Websites/Servers/Index.razor | 115 +++ .../Views/Admin/Websites/Servers/New.razor | 55 + Moonlight/Shared/Views/Server/Index.razor | 24 +- Moonlight/Shared/Views/Website/Index.razor | 129 +++ Moonlight/Shared/Views/Websites/Index.razor | 1 + Moonlight/Shared/Views/Websites/New.razor | 1 + Moonlight/resources/lang/de_de.lang | 13 + 46 files changed, 3825 insertions(+), 54 deletions(-) create mode 100644 Moonlight/App/Database/Entities/PleskServer.cs create mode 100644 Moonlight/App/Database/Entities/Website.cs create mode 100644 Moonlight/App/Database/Migrations/20230404181522_AddedPleskAndWebsiteModels.Designer.cs create mode 100644 Moonlight/App/Database/Migrations/20230404181522_AddedPleskAndWebsiteModels.cs create mode 100644 Moonlight/App/Database/Migrations/20230405162507_UpdatedWebsiteModel.Designer.cs create mode 100644 Moonlight/App/Database/Migrations/20230405162507_UpdatedWebsiteModel.cs create mode 100644 Moonlight/App/Exceptions/DaemonException.cs create mode 100644 Moonlight/App/Exceptions/PleskException.cs create mode 100644 Moonlight/App/Helpers/PleskApiHelper.cs create mode 100644 Moonlight/App/Models/Forms/PleskServerDataModel.cs create mode 100644 Moonlight/App/Models/Forms/WebsiteAdminDataModel.cs create mode 100644 Moonlight/App/Models/Forms/WebsiteDataModel.cs create mode 100644 Moonlight/App/Models/Plesk/Requests/CliCall.cs create mode 100644 Moonlight/App/Models/Plesk/Requests/CreateDomain.cs create mode 100644 Moonlight/App/Models/Plesk/Resources/CliResult.cs create mode 100644 Moonlight/App/Models/Plesk/Resources/Client.cs create mode 100644 Moonlight/App/Models/Plesk/Resources/CreateResult.cs create mode 100644 Moonlight/App/Models/Plesk/Resources/ServerStatus.cs create mode 100644 Moonlight/App/Repositories/PleskServerRepository.cs create mode 100644 Moonlight/App/Repositories/WebsiteRepository.cs create mode 100644 Moonlight/App/Services/WebsiteService.cs create mode 100644 Moonlight/Shared/Components/Navigations/AdminWebsitesNavigation.razor create mode 100644 Moonlight/Shared/Components/WebsiteControl/WebsiteDashboard.razor create mode 100644 Moonlight/Shared/Components/WebsiteControl/WebsiteFiles.razor create mode 100644 Moonlight/Shared/Components/WebsiteControl/WebsiteFtp.razor create mode 100644 Moonlight/Shared/Components/WebsiteControl/WebsiteNavigation.razor create mode 100644 Moonlight/Shared/Views/Admin/Websites/Index.razor create mode 100644 Moonlight/Shared/Views/Admin/Websites/New.razor create mode 100644 Moonlight/Shared/Views/Admin/Websites/Servers/Edit.razor create mode 100644 Moonlight/Shared/Views/Admin/Websites/Servers/Index.razor create mode 100644 Moonlight/Shared/Views/Admin/Websites/Servers/New.razor create mode 100644 Moonlight/Shared/Views/Website/Index.razor create mode 100644 Moonlight/Shared/Views/Websites/Index.razor create mode 100644 Moonlight/Shared/Views/Websites/New.razor diff --git a/Moonlight/App/Database/DataContext.cs b/Moonlight/App/Database/DataContext.cs index 906ce00..58bd3db 100644 --- a/Moonlight/App/Database/DataContext.cs +++ b/Moonlight/App/Database/DataContext.cs @@ -39,6 +39,8 @@ 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; } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { diff --git a/Moonlight/App/Database/Entities/PleskServer.cs b/Moonlight/App/Database/Entities/PleskServer.cs new file mode 100644 index 0000000..a4cd630 --- /dev/null +++ b/Moonlight/App/Database/Entities/PleskServer.cs @@ -0,0 +1,9 @@ +namespace Moonlight.App.Database.Entities; + +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/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/DataContextModelSnapshot.cs b/Moonlight/App/Database/Migrations/DataContextModelSnapshot.cs index 65062e1..1433119 100644 --- a/Moonlight/App/Database/Migrations/DataContextModelSnapshot.cs +++ b/Moonlight/App/Database/Migrations/DataContextModelSnapshot.cs @@ -395,6 +395,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") @@ -697,6 +720,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") @@ -847,6 +906,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..4c776a4 100644 --- a/Moonlight/App/Helpers/DaemonApiHelper.cs +++ b/Moonlight/App/Helpers/DaemonApiHelper.cs @@ -37,7 +37,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/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/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/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..d4a7f4b --- /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 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/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/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/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/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/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/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..bc22343 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) @@ -252,16 +255,14 @@ public class ServerService Node node; if (n == null) - { - node = NodeRepository.Get().Include(x => x.Allocations).First(); //TODO: Smart deploy - } - else { node = NodeRepository .Get() .Include(x => x.Allocations) .First(x => x.Id == n.Id); } + else + node = n; NodeAllocation freeAllo; @@ -395,4 +396,11 @@ public class ServerService ServerRepository.Delete(s); } + + 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/WebsiteService.cs b/Moonlight/App/Services/WebsiteService.cs new file mode 100644 index 0000000..44b2994 --- /dev/null +++ b/Moonlight/App/Services/WebsiteService.cs @@ -0,0 +1,236 @@ +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; + + public WebsiteService(WebsiteRepository websiteRepository, PleskApiHelper pleskApiHelper, PleskServerRepository pleskServerRepository) + { + WebsiteRepository = websiteRepository; + PleskApiHelper = pleskApiHelper; + PleskServerRepository = pleskServerRepository; + } + + 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; + } + + public async Task GetHost(PleskServer pleskServer) + { + return (await PleskApiHelper.Get(pleskServer, "server")).Hostname; + } + + 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; + } + + public async Task GetSslCertificates(Website w) + { + var website = EnsureData(w); + var certs = new List(); + + Logger.Debug("1"); + + var data = await ExecuteCli(website.PleskServer, "certificate", p => + { + p.Add("-l"); + p.Add("-domain"); + p.Add(w.BaseDomain); + }); + + Logger.Debug("2"); + + 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); + + foreach (var part in parts) + { + Logger.Debug(part); + } + + 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() + { + + } + + 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) + return WebsiteRepository + .Get() + .Include(x => x.PleskServer) + .First(x => x.Id == website.Id); + + return website; + } +} \ No newline at end of file diff --git a/Moonlight/Program.cs b/Moonlight/Program.cs index 5b4efeb..832b1f1 100644 --- a/Moonlight/Program.cs +++ b/Moonlight/Program.cs @@ -60,6 +60,8 @@ namespace Moonlight builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); + builder.Services.AddScoped(); + builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); @@ -89,6 +91,7 @@ namespace Moonlight builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); + builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); @@ -120,6 +123,7 @@ namespace Moonlight builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddScoped(); + builder.Services.AddScoped(); // Background services builder.Services.AddSingleton(); diff --git a/Moonlight/Shared/Components/ErrorBoundaries/SoftErrorBoundary.razor b/Moonlight/Shared/Components/ErrorBoundaries/SoftErrorBoundary.razor index ca47b59..cbad558 100644 --- a/Moonlight/Shared/Components/ErrorBoundaries/SoftErrorBoundary.razor +++ b/Moonlight/Shared/Components/ErrorBoundaries/SoftErrorBoundary.razor @@ -36,6 +36,13 @@ wingsException.Message ); } + else if (exception is PleskException pleskException) + { + await AlertService.Error( + SmartTranslateService.Translate("Error from plesk"), + pleskException.Message + ); + } else { throw exception; diff --git a/Moonlight/Shared/Components/Forms/DeleteButton.razor b/Moonlight/Shared/Components/Forms/DeleteButton.razor index 4c37ec9..7cd719a 100644 --- a/Moonlight/Shared/Components/Forms/DeleteButton.razor +++ b/Moonlight/Shared/Components/Forms/DeleteButton.razor @@ -35,12 +35,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/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..2d82030 100644 --- a/Moonlight/Shared/Components/Partials/SidebarMenu.razor +++ b/Moonlight/Shared/Components/Partials/SidebarMenu.razor @@ -155,32 +155,13 @@ else -