From 361eddb71bc6a97826e757ca878336ea436ebc9f Mon Sep 17 00:00:00 2001 From: Marcel Baumgartner Date: Thu, 23 Feb 2023 14:58:50 +0100 Subject: [PATCH] Added shared domains. Started implementing domains --- Moonlight/App/Database/DataContext.cs | 3 + Moonlight/App/Database/Entities/Domain.cs | 10 + .../App/Database/Entities/SharedDomain.cs | 8 + ...1_AddedDomainsAndSharedDomains.Designer.cs | 702 +++ ...0223121111_AddedDomainsAndSharedDomains.cs | 83 + .../Migrations/DataContextModelSnapshot.cs | 67 + .../App/Exceptions/CloudflareException.cs | 30 + .../Repositories/Domains/DomainRepository.cs | 44 + .../Domains/SharedDomainRepository.cs | 44 + Moonlight/App/Services/DomainService.cs | 65 + Moonlight/Moonlight.csproj | 2 + Moonlight/Program.cs | 5 + .../Components/Partials/SidebarMenu.razor | 27 + .../Shared/Views/Admin/Domains/Index.razor | 68 + .../Shared/Views/Admin/Domains/Shared.razor | 126 + Moonlight/Shared/Views/Domains.razor | 2 + ....GeneratedMSBuildEditorConfig.editorconfig | 12 + .../obj/Debug/net6.0/Moonlight.assets.cache | Bin 28772 -> 62588 bytes .../Moonlight.csproj.AssemblyReference.cache | Bin 153254 -> 154494 bytes .../obj/Moonlight.csproj.nuget.dgspec.json | 4 + Moonlight/obj/project.assets.json | 4585 +++++++++++++++++ Moonlight/obj/project.nuget.cache | 79 +- Moonlight/obj/project.packagespec.json | 2 +- Moonlight/resources/lang/de_de.lang | 5 + 24 files changed, 5971 insertions(+), 2 deletions(-) create mode 100644 Moonlight/App/Database/Entities/Domain.cs create mode 100644 Moonlight/App/Database/Entities/SharedDomain.cs create mode 100644 Moonlight/App/Database/Migrations/20230223121111_AddedDomainsAndSharedDomains.Designer.cs create mode 100644 Moonlight/App/Database/Migrations/20230223121111_AddedDomainsAndSharedDomains.cs create mode 100644 Moonlight/App/Exceptions/CloudflareException.cs create mode 100644 Moonlight/App/Repositories/Domains/DomainRepository.cs create mode 100644 Moonlight/App/Repositories/Domains/SharedDomainRepository.cs create mode 100644 Moonlight/App/Services/DomainService.cs create mode 100644 Moonlight/Shared/Views/Admin/Domains/Index.razor create mode 100644 Moonlight/Shared/Views/Admin/Domains/Shared.razor create mode 100644 Moonlight/Shared/Views/Domains.razor diff --git a/Moonlight/App/Database/DataContext.cs b/Moonlight/App/Database/DataContext.cs index 3ecdf31b..bd2d2720 100644 --- a/Moonlight/App/Database/DataContext.cs +++ b/Moonlight/App/Database/DataContext.cs @@ -27,6 +27,9 @@ public class DataContext : DbContext public DbSet AuditLog { get; set; } public DbSet Databases { get; set; } public DbSet SupportMessages { get; set; } + + public DbSet SharedDomains { get; set; } + public DbSet Domains { get; set; } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { diff --git a/Moonlight/App/Database/Entities/Domain.cs b/Moonlight/App/Database/Entities/Domain.cs new file mode 100644 index 00000000..99579135 --- /dev/null +++ b/Moonlight/App/Database/Entities/Domain.cs @@ -0,0 +1,10 @@ +namespace Moonlight.App.Database.Entities; + +public class Domain +{ + public int Id { get; set; } + public string Name { get; set; } + public SharedDomain SharedDomain { get; set; } + public User Owner { get; set; } + public string CloudflareId { get; set; } +} \ No newline at end of file diff --git a/Moonlight/App/Database/Entities/SharedDomain.cs b/Moonlight/App/Database/Entities/SharedDomain.cs new file mode 100644 index 00000000..cf9f27ae --- /dev/null +++ b/Moonlight/App/Database/Entities/SharedDomain.cs @@ -0,0 +1,8 @@ +namespace Moonlight.App.Database.Entities; + +public class SharedDomain +{ + public int Id { get; set; } + public string Name { get; set; } + public string CloudflareId { get; set; } +} \ No newline at end of file diff --git a/Moonlight/App/Database/Migrations/20230223121111_AddedDomainsAndSharedDomains.Designer.cs b/Moonlight/App/Database/Migrations/20230223121111_AddedDomainsAndSharedDomains.Designer.cs new file mode 100644 index 00000000..22e1f0c1 --- /dev/null +++ b/Moonlight/App/Database/Migrations/20230223121111_AddedDomainsAndSharedDomains.Designer.cs @@ -0,0 +1,702 @@ +// +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("20230223121111_AddedDomainsAndSharedDomains")] + partial class AddedDomainsAndSharedDomains + { + /// + 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.AuditLogEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + 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.Database", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("AaPanelId") + .HasColumnType("int"); + + b.Property("OwnerId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("OwnerId"); + + b.ToTable("Databases"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.DockerImage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Default") + .HasColumnType("tinyint(1)"); + + b.Property("ImageId") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("ImageId"); + + b.ToTable("DockerImages"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Domain", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("CloudflareId") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("OwnerId") + .HasColumnType("int"); + + b.Property("SharedDomainId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("OwnerId"); + + b.HasIndex("SharedDomainId"); + + b.ToTable("Domains"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Image", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("ConfigFiles") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Description") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("InstallDockerImage") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("InstallEntrypoint") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("InstallScript") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Startup") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("StartupDetection") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("StopCommand") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("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("ImageId") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("ImageId"); + + 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.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.Server", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Cpu") + .HasColumnType("int"); + + b.Property("Disk") + .HasColumnType("bigint"); + + b.Property("DockerImageIndex") + .HasColumnType("int"); + + b.Property("ImageId") + .HasColumnType("int"); + + b.Property("Installing") + .HasColumnType("tinyint(1)"); + + b.Property("MainAllocationId") + .HasColumnType("int"); + + b.Property("Memory") + .HasColumnType("bigint"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("NodeId") + .HasColumnType("int"); + + b.Property("OverrideStartup") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("OwnerId") + .HasColumnType("int"); + + b.Property("Suspended") + .HasColumnType("tinyint(1)"); + + b.Property("Uuid") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("ImageId"); + + b.HasIndex("MainAllocationId"); + + b.HasIndex("NodeId"); + + b.HasIndex("OwnerId"); + + b.ToTable("Servers"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.ServerBackup", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Bytes") + .HasColumnType("bigint"); + + b.Property("Created") + .HasColumnType("tinyint(1)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ServerId") + .HasColumnType("int"); + + b.Property("Uuid") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("ServerId"); + + b.ToTable("ServerBackups"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.ServerVariable", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Key") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ServerId") + .HasColumnType("int"); + + b.Property("Value") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("ServerId"); + + b.ToTable("ServerVariables"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.SharedDomain", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("CloudflareId") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("SharedDomains"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.SupportMessage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Answer") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsQuestion") + .HasColumnType("tinyint(1)"); + + b.Property("IsSupport") + .HasColumnType("tinyint(1)"); + + b.Property("IsSystem") + .HasColumnType("tinyint(1)"); + + b.Property("Message") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("RecipientId") + .HasColumnType("int"); + + b.Property("SenderId") + .HasColumnType("int"); + + b.Property("Type") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("RecipientId"); + + b.HasIndex("SenderId"); + + b.ToTable("SupportMessages"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Address") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Admin") + .HasColumnType("tinyint(1)"); + + b.Property("City") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Country") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("DiscordDiscriminator") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("DiscordId") + .HasColumnType("bigint"); + + b.Property("DiscordUsername") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Email") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("FirstName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("LastName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Password") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("State") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("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.ToTable("Users"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Database", b => + { + b.HasOne("Moonlight.App.Database.Entities.User", "Owner") + .WithMany() + .HasForeignKey("OwnerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Owner"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.DockerImage", b => + { + b.HasOne("Moonlight.App.Database.Entities.Image", null) + .WithMany("DockerImages") + .HasForeignKey("ImageId"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.Domain", b => + { + b.HasOne("Moonlight.App.Database.Entities.User", "Owner") + .WithMany() + .HasForeignKey("OwnerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Moonlight.App.Database.Entities.SharedDomain", "SharedDomain") + .WithMany() + .HasForeignKey("SharedDomainId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Owner"); + + b.Navigation("SharedDomain"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.ImageTag", b => + { + b.HasOne("Moonlight.App.Database.Entities.Image", null) + .WithMany("Tags") + .HasForeignKey("ImageId"); + }); + + modelBuilder.Entity("Moonlight.App.Database.Entities.ImageVariable", b => + { + b.HasOne("Moonlight.App.Database.Entities.Image", null) + .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.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.Image", b => + { + b.Navigation("DockerImages"); + + b.Navigation("Tags"); + + 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/20230223121111_AddedDomainsAndSharedDomains.cs b/Moonlight/App/Database/Migrations/20230223121111_AddedDomainsAndSharedDomains.cs new file mode 100644 index 00000000..1b1cc2c7 --- /dev/null +++ b/Moonlight/App/Database/Migrations/20230223121111_AddedDomainsAndSharedDomains.cs @@ -0,0 +1,83 @@ +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Moonlight.App.Database.Migrations +{ + /// + public partial class AddedDomainsAndSharedDomains : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "SharedDomains", + 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"), + CloudflareId = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK_SharedDomains", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "Domains", + 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"), + SharedDomainId = table.Column(type: "int", nullable: false), + OwnerId = table.Column(type: "int", nullable: false), + CloudflareId = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK_Domains", x => x.Id); + table.ForeignKey( + name: "FK_Domains_SharedDomains_SharedDomainId", + column: x => x.SharedDomainId, + principalTable: "SharedDomains", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_Domains_Users_OwnerId", + column: x => x.OwnerId, + principalTable: "Users", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateIndex( + name: "IX_Domains_OwnerId", + table: "Domains", + column: "OwnerId"); + + migrationBuilder.CreateIndex( + name: "IX_Domains_SharedDomainId", + table: "Domains", + column: "SharedDomainId"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "Domains"); + + migrationBuilder.DropTable( + name: "SharedDomains"); + } + } +} diff --git a/Moonlight/App/Database/Migrations/DataContextModelSnapshot.cs b/Moonlight/App/Database/Migrations/DataContextModelSnapshot.cs index d8c33505..d2fe1177 100644 --- a/Moonlight/App/Database/Migrations/DataContextModelSnapshot.cs +++ b/Moonlight/App/Database/Migrations/DataContextModelSnapshot.cs @@ -86,6 +86,35 @@ namespace Moonlight.App.Database.Migrations b.ToTable("DockerImages"); }); + modelBuilder.Entity("Moonlight.App.Database.Entities.Domain", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("CloudflareId") + .IsRequired() + .HasColumnType("longtext"); + + 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") @@ -374,6 +403,25 @@ namespace Moonlight.App.Database.Migrations 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.SupportMessage", b => { b.Property("Id") @@ -515,6 +563,25 @@ namespace Moonlight.App.Database.Migrations .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.ImageTag", b => { b.HasOne("Moonlight.App.Database.Entities.Image", null) diff --git a/Moonlight/App/Exceptions/CloudflareException.cs b/Moonlight/App/Exceptions/CloudflareException.cs new file mode 100644 index 00000000..d412edb2 --- /dev/null +++ b/Moonlight/App/Exceptions/CloudflareException.cs @@ -0,0 +1,30 @@ +using System.Runtime.Serialization; + +[Serializable] +public class CloudflareException : Exception +{ + // + // For guidelines regarding the creation of new exception types, see + // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpgenref/html/cpconerrorraisinghandlingguidelines.asp + // and + // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dncscol/html/csharp07192001.asp + // + + public CloudflareException() + { + } + + public CloudflareException(string message) : base(message) + { + } + + public CloudflareException(string message, Exception inner) : base(message, inner) + { + } + + protected CloudflareException( + SerializationInfo info, + StreamingContext context) : base(info, context) + { + } +} \ No newline at end of file diff --git a/Moonlight/App/Repositories/Domains/DomainRepository.cs b/Moonlight/App/Repositories/Domains/DomainRepository.cs new file mode 100644 index 00000000..6cacef2a --- /dev/null +++ b/Moonlight/App/Repositories/Domains/DomainRepository.cs @@ -0,0 +1,44 @@ +using Microsoft.EntityFrameworkCore; +using Moonlight.App.Database; +using Moonlight.App.Database.Entities; + +namespace Moonlight.App.Repositories.Domains; + +public class DomainRepository : IDisposable +{ + private readonly DataContext DataContext; + + public DomainRepository(DataContext dataContext) + { + DataContext = dataContext; + } + + public DbSet Get() + { + return DataContext.Domains; + } + + public Domain Add(Domain domain) + { + var x = DataContext.Domains.Add(domain); + DataContext.SaveChanges(); + return x.Entity; + } + + public void Update(Domain domain) + { + DataContext.Domains.Update(domain); + DataContext.SaveChanges(); + } + + public void Delete(Domain domain) + { + DataContext.Domains.Remove(domain); + DataContext.SaveChanges(); + } + + public void Dispose() + { + DataContext.Dispose(); + } +} \ No newline at end of file diff --git a/Moonlight/App/Repositories/Domains/SharedDomainRepository.cs b/Moonlight/App/Repositories/Domains/SharedDomainRepository.cs new file mode 100644 index 00000000..614869ab --- /dev/null +++ b/Moonlight/App/Repositories/Domains/SharedDomainRepository.cs @@ -0,0 +1,44 @@ +using Microsoft.EntityFrameworkCore; +using Moonlight.App.Database; +using Moonlight.App.Database.Entities; + +namespace Moonlight.App.Repositories.Domains; + +public class SharedDomainRepository : IDisposable +{ + private readonly DataContext DataContext; + + public SharedDomainRepository(DataContext dataContext) + { + DataContext = dataContext; + } + + public DbSet Get() + { + return DataContext.SharedDomains; + } + + public SharedDomain Add(SharedDomain sharedDomain) + { + var x = DataContext.SharedDomains.Add(sharedDomain); + DataContext.SaveChanges(); + return x.Entity; + } + + public void Update(SharedDomain sharedDomain) + { + DataContext.SharedDomains.Update(sharedDomain); + DataContext.SaveChanges(); + } + + public void Delete(SharedDomain domain) + { + DataContext.SharedDomains.Remove(domain); + DataContext.SaveChanges(); + } + + public void Dispose() + { + DataContext.Dispose(); + } +} \ No newline at end of file diff --git a/Moonlight/App/Services/DomainService.cs b/Moonlight/App/Services/DomainService.cs new file mode 100644 index 00000000..35a59f91 --- /dev/null +++ b/Moonlight/App/Services/DomainService.cs @@ -0,0 +1,65 @@ +using CloudFlare.Client; +using CloudFlare.Client.Api.Authentication; +using CloudFlare.Client.Api.Result; +using CloudFlare.Client.Api.Zones; +using Moonlight.App.Repositories.Domains; + +namespace Moonlight.App.Services; + +public class DomainService +{ + private readonly DomainRepository DomainRepository; + private readonly SharedDomainRepository SharedDomainRepository; + private readonly CloudFlareClient Client; + private readonly string AccountId; + + public DomainService(ConfigService configService, + DomainRepository domainRepository, + SharedDomainRepository sharedDomainRepository) + { + DomainRepository = domainRepository; + SharedDomainRepository = sharedDomainRepository; + + var config = configService + .GetSection("Moonlight") + .GetSection("Domains"); + + AccountId = config.GetValue("AccountId"); + + Client = new( + new ApiKeyAuthentication( + config.GetValue("Email"), + config.GetValue("Key") + ) + ); + } + + public async Task + GetAvailableDomains() // This method returns all available domains which are not added as a shared domain + { + var domains = await Client.Zones.GetAsync(new() + { + AccountId = AccountId + }); + + if (!domains.Success) + throw new CloudflareException(GetErrorMessage(domains)); + + var sharedDomains = SharedDomainRepository.Get().ToArray(); + + var freeDomains = domains.Result + .Where(x => sharedDomains.FirstOrDefault + ( + y => y.CloudflareId == x.Id + ) == null + ) + .ToArray(); + + return freeDomains; + } + + private string GetErrorMessage(CloudFlareResult result) + { + return result.Errors.First().ErrorChain.First().Message; + } +} \ No newline at end of file diff --git a/Moonlight/Moonlight.csproj b/Moonlight/Moonlight.csproj index 8c95a828..93896017 100644 --- a/Moonlight/Moonlight.csproj +++ b/Moonlight/Moonlight.csproj @@ -16,6 +16,7 @@ + @@ -61,6 +62,7 @@ + diff --git a/Moonlight/Program.cs b/Moonlight/Program.cs index 145e06ab..e9d78871 100644 --- a/Moonlight/Program.cs +++ b/Moonlight/Program.cs @@ -4,6 +4,7 @@ using Logging.Net; using Moonlight.App.Database; using Moonlight.App.Helpers; using Moonlight.App.Repositories; +using Moonlight.App.Repositories.Domains; using Moonlight.App.Repositories.Servers; using Moonlight.App.Services; using Moonlight.App.Services.Interop; @@ -39,6 +40,8 @@ namespace Moonlight builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); + builder.Services.AddScoped(); + builder.Services.AddScoped(); // Services builder.Services.AddSingleton(); @@ -57,6 +60,8 @@ namespace Moonlight builder.Services.AddSingleton(); builder.Services.AddScoped(); builder.Services.AddSingleton(); + builder.Services.AddScoped(); + builder.Services.AddScoped(); builder.Services.AddScoped(); diff --git a/Moonlight/Shared/Components/Partials/SidebarMenu.razor b/Moonlight/Shared/Components/Partials/SidebarMenu.razor index 2c38758e..b75a29d6 100644 --- a/Moonlight/Shared/Components/Partials/SidebarMenu.razor +++ b/Moonlight/Shared/Components/Partials/SidebarMenu.razor @@ -190,6 +190,33 @@ else Users +