diff --git a/Moonlight/Core/CoreFeature.cs b/Moonlight/Core/CoreFeature.cs index aef6a824..e100d481 100644 --- a/Moonlight/Core/CoreFeature.cs +++ b/Moonlight/Core/CoreFeature.cs @@ -251,11 +251,9 @@ public class CoreFeature : MoonlightFeature // Api if (config.Development.EnableApiReference) - { app.MapSwagger("/api/core/reference/openapi/{documentName}"); - - await pluginService.RegisterImplementation(new InternalApiDefinition()); - } + + await pluginService.RegisterImplementation(new InternalApiDefinition()); } public override Task OnUiInitialized(UiInitContext context) @@ -269,6 +267,7 @@ public class CoreFeature : MoonlightFeature context.AddSidebarItem("Dashboard", "bxs-dashboard", "/admin", needsExactMatch: true, isAdmin: true, index: int.MinValue); context.AddSidebarItem("Users", "bxs-group", "/admin/users", needsExactMatch: false, isAdmin: true); + context.AddSidebarItem("API", "bx-code-curly", "/admin/api", needsExactMatch: false, isAdmin: true); context.AddSidebarItem("System", "bxs-component", "/admin/sys", needsExactMatch: false, isAdmin: true); return Task.CompletedTask; diff --git a/Moonlight/Core/Database/DataContext.cs b/Moonlight/Core/Database/DataContext.cs index c2daaa76..d987ada6 100644 --- a/Moonlight/Core/Database/DataContext.cs +++ b/Moonlight/Core/Database/DataContext.cs @@ -12,6 +12,7 @@ public class DataContext : DbContext // Core public DbSet Users { get; set; } + public DbSet ApiKeys { get; set; } // Servers public DbSet Servers { get; set; } diff --git a/Moonlight/Core/Database/Entities/ApiKey.cs b/Moonlight/Core/Database/Entities/ApiKey.cs new file mode 100644 index 00000000..ae02fcd2 --- /dev/null +++ b/Moonlight/Core/Database/Entities/ApiKey.cs @@ -0,0 +1,11 @@ +namespace Moonlight.Core.Database.Entities; + +public class ApiKey +{ + public int Id { get; set; } + public string Key { get; set; } = ""; + public string Description { get; set; } = ""; + public DateTime CreatedAt { get; set; } = DateTime.UtcNow; + public DateTime ExpiresAt { get; set; } = DateTime.UtcNow; + public string PermissionJson { get; set; } = "[]"; +} \ No newline at end of file diff --git a/Moonlight/Core/Database/Migrations/20240605120928_AddedApiKeys.Designer.cs b/Moonlight/Core/Database/Migrations/20240605120928_AddedApiKeys.Designer.cs new file mode 100644 index 00000000..a40acb82 --- /dev/null +++ b/Moonlight/Core/Database/Migrations/20240605120928_AddedApiKeys.Designer.cs @@ -0,0 +1,657 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Moonlight.Core.Database; + +#nullable disable + +namespace Moonlight.Core.Database.Migrations +{ + [DbContext(typeof(DataContext))] + [Migration("20240605120928_AddedApiKeys")] + partial class AddedApiKeys + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.6") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("Moonlight.Core.Database.Entities.ApiKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Description") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ExpiresAt") + .HasColumnType("datetime(6)"); + + b.Property("Key") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("PermissionJson") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("ApiKeys"); + }); + + modelBuilder.Entity("Moonlight.Core.Database.Entities.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Email") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Flags") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Password") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Permissions") + .HasColumnType("int"); + + b.Property("TokenValidTimestamp") + .HasColumnType("datetime(6)"); + + b.Property("Totp") + .HasColumnType("tinyint(1)"); + + b.Property("TotpSecret") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Username") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("Moonlight.Features.Servers.Entities.Server", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("Cpu") + .HasColumnType("int"); + + b.Property("DisablePublicNetwork") + .HasColumnType("tinyint(1)"); + + b.Property("Disk") + .HasColumnType("int"); + + b.Property("DockerImageIndex") + .HasColumnType("int"); + + b.Property("ImageId") + .HasColumnType("int"); + + b.Property("MainAllocationId") + .HasColumnType("int"); + + b.Property("Memory") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("NetworkId") + .HasColumnType("int"); + + b.Property("NodeId") + .HasColumnType("int"); + + b.Property("OverrideStartupCommand") + .HasColumnType("longtext"); + + b.Property("OwnerId") + .HasColumnType("int"); + + b.Property("UseVirtualDisk") + .HasColumnType("tinyint(1)"); + + b.HasKey("Id"); + + b.HasIndex("ImageId"); + + b.HasIndex("MainAllocationId"); + + b.HasIndex("NetworkId"); + + b.HasIndex("NodeId"); + + b.HasIndex("OwnerId"); + + b.ToTable("Servers"); + }); + + modelBuilder.Entity("Moonlight.Features.Servers.Entities.ServerAllocation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("IpAddress") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Note") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Port") + .HasColumnType("int"); + + b.Property("ServerId") + .HasColumnType("int"); + + b.Property("ServerNodeId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("ServerId"); + + b.HasIndex("ServerNodeId"); + + b.ToTable("ServerAllocations"); + }); + + modelBuilder.Entity("Moonlight.Features.Servers.Entities.ServerBackup", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("Completed") + .HasColumnType("tinyint(1)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("ServerId") + .HasColumnType("int"); + + b.Property("Size") + .HasColumnType("bigint"); + + b.Property("Successful") + .HasColumnType("tinyint(1)"); + + b.HasKey("Id"); + + b.HasIndex("ServerId"); + + b.ToTable("ServerBackup"); + }); + + modelBuilder.Entity("Moonlight.Features.Servers.Entities.ServerDockerImage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("AutoPull") + .HasColumnType("tinyint(1)"); + + b.Property("DisplayName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ServerImageId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("ServerImageId"); + + b.ToTable("ServerDockerImages"); + }); + + modelBuilder.Entity("Moonlight.Features.Servers.Entities.ServerImage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("AllocationsNeeded") + .HasColumnType("int"); + + b.Property("AllowDockerImageChange") + .HasColumnType("tinyint(1)"); + + b.Property("Author") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("DefaultDockerImage") + .HasColumnType("int"); + + b.Property("DonateUrl") + .HasColumnType("longtext"); + + b.Property("InstallDockerImage") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("InstallScript") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("InstallShell") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("OnlineDetection") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ParseConfiguration") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("StartupCommand") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("StopCommand") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("UpdateUrl") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("ServerImages"); + }); + + modelBuilder.Entity("Moonlight.Features.Servers.Entities.ServerImageVariable", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("AllowEdit") + .HasColumnType("tinyint(1)"); + + b.Property("AllowView") + .HasColumnType("tinyint(1)"); + + b.Property("DefaultValue") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Description") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("DisplayName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Filter") + .HasColumnType("longtext"); + + b.Property("Key") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ServerImageId") + .HasColumnType("int"); + + b.Property("Type") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("ServerImageId"); + + b.ToTable("ServerImageVariables"); + }); + + modelBuilder.Entity("Moonlight.Features.Servers.Entities.ServerNetwork", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("NodeId") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("NodeId"); + + b.HasIndex("UserId"); + + b.ToTable("ServerNetworks"); + }); + + modelBuilder.Entity("Moonlight.Features.Servers.Entities.ServerNode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("Fqdn") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("FtpPort") + .HasColumnType("int"); + + b.Property("HttpPort") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Ssl") + .HasColumnType("tinyint(1)"); + + b.Property("Token") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("ServerNodes"); + }); + + modelBuilder.Entity("Moonlight.Features.Servers.Entities.ServerSchedule", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ExecutionSeconds") + .HasColumnType("int"); + + b.Property("LastRun") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ServerId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("ServerId"); + + b.ToTable("ServerSchedules"); + }); + + modelBuilder.Entity("Moonlight.Features.Servers.Entities.ServerScheduleItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("Action") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("DataJson") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Priority") + .HasColumnType("int"); + + b.Property("ServerScheduleId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("ServerScheduleId"); + + b.ToTable("ServerScheduleItems"); + }); + + modelBuilder.Entity("Moonlight.Features.Servers.Entities.ServerVariable", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + 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.Features.Servers.Entities.Server", b => + { + b.HasOne("Moonlight.Features.Servers.Entities.ServerImage", "Image") + .WithMany() + .HasForeignKey("ImageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Moonlight.Features.Servers.Entities.ServerAllocation", "MainAllocation") + .WithMany() + .HasForeignKey("MainAllocationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Moonlight.Features.Servers.Entities.ServerNetwork", "Network") + .WithMany() + .HasForeignKey("NetworkId"); + + b.HasOne("Moonlight.Features.Servers.Entities.ServerNode", "Node") + .WithMany() + .HasForeignKey("NodeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Moonlight.Core.Database.Entities.User", "Owner") + .WithMany() + .HasForeignKey("OwnerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Image"); + + b.Navigation("MainAllocation"); + + b.Navigation("Network"); + + b.Navigation("Node"); + + b.Navigation("Owner"); + }); + + modelBuilder.Entity("Moonlight.Features.Servers.Entities.ServerAllocation", b => + { + b.HasOne("Moonlight.Features.Servers.Entities.Server", null) + .WithMany("Allocations") + .HasForeignKey("ServerId"); + + b.HasOne("Moonlight.Features.Servers.Entities.ServerNode", null) + .WithMany("Allocations") + .HasForeignKey("ServerNodeId"); + }); + + modelBuilder.Entity("Moonlight.Features.Servers.Entities.ServerBackup", b => + { + b.HasOne("Moonlight.Features.Servers.Entities.Server", null) + .WithMany("Backups") + .HasForeignKey("ServerId"); + }); + + modelBuilder.Entity("Moonlight.Features.Servers.Entities.ServerDockerImage", b => + { + b.HasOne("Moonlight.Features.Servers.Entities.ServerImage", null) + .WithMany("DockerImages") + .HasForeignKey("ServerImageId"); + }); + + modelBuilder.Entity("Moonlight.Features.Servers.Entities.ServerImageVariable", b => + { + b.HasOne("Moonlight.Features.Servers.Entities.ServerImage", null) + .WithMany("Variables") + .HasForeignKey("ServerImageId"); + }); + + modelBuilder.Entity("Moonlight.Features.Servers.Entities.ServerNetwork", b => + { + b.HasOne("Moonlight.Features.Servers.Entities.ServerNode", "Node") + .WithMany() + .HasForeignKey("NodeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Moonlight.Core.Database.Entities.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Node"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Moonlight.Features.Servers.Entities.ServerSchedule", b => + { + b.HasOne("Moonlight.Features.Servers.Entities.Server", null) + .WithMany("Schedules") + .HasForeignKey("ServerId"); + }); + + modelBuilder.Entity("Moonlight.Features.Servers.Entities.ServerScheduleItem", b => + { + b.HasOne("Moonlight.Features.Servers.Entities.ServerSchedule", null) + .WithMany("Items") + .HasForeignKey("ServerScheduleId"); + }); + + modelBuilder.Entity("Moonlight.Features.Servers.Entities.ServerVariable", b => + { + b.HasOne("Moonlight.Features.Servers.Entities.Server", null) + .WithMany("Variables") + .HasForeignKey("ServerId"); + }); + + modelBuilder.Entity("Moonlight.Features.Servers.Entities.Server", b => + { + b.Navigation("Allocations"); + + b.Navigation("Backups"); + + b.Navigation("Schedules"); + + b.Navigation("Variables"); + }); + + modelBuilder.Entity("Moonlight.Features.Servers.Entities.ServerImage", b => + { + b.Navigation("DockerImages"); + + b.Navigation("Variables"); + }); + + modelBuilder.Entity("Moonlight.Features.Servers.Entities.ServerNode", b => + { + b.Navigation("Allocations"); + }); + + modelBuilder.Entity("Moonlight.Features.Servers.Entities.ServerSchedule", b => + { + b.Navigation("Items"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Moonlight/Core/Database/Migrations/20240605120928_AddedApiKeys.cs b/Moonlight/Core/Database/Migrations/20240605120928_AddedApiKeys.cs new file mode 100644 index 00000000..99eb351d --- /dev/null +++ b/Moonlight/Core/Database/Migrations/20240605120928_AddedApiKeys.cs @@ -0,0 +1,44 @@ +using System; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Moonlight.Core.Database.Migrations +{ + /// + public partial class AddedApiKeys : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "ApiKeys", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + Key = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Description = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + CreatedAt = table.Column(type: "datetime(6)", nullable: false), + ExpiresAt = table.Column(type: "datetime(6)", nullable: false), + PermissionJson = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK_ApiKeys", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "ApiKeys"); + } + } +} diff --git a/Moonlight/Core/Database/Migrations/DataContextModelSnapshot.cs b/Moonlight/Core/Database/Migrations/DataContextModelSnapshot.cs index c6a063bd..7b9b056e 100644 --- a/Moonlight/Core/Database/Migrations/DataContextModelSnapshot.cs +++ b/Moonlight/Core/Database/Migrations/DataContextModelSnapshot.cs @@ -2,6 +2,7 @@ using System; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; using Moonlight.Core.Database; @@ -16,15 +17,50 @@ namespace Moonlight.Core.Database.Migrations { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "7.0.2") + .HasAnnotation("ProductVersion", "8.0.6") .HasAnnotation("Relational:MaxIdentifierLength", 64); + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("Moonlight.Core.Database.Entities.ApiKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Description") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ExpiresAt") + .HasColumnType("datetime(6)"); + + b.Property("Key") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("PermissionJson") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("ApiKeys"); + }); + modelBuilder.Entity("Moonlight.Core.Database.Entities.User", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int"); + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + b.Property("CreatedAt") .HasColumnType("datetime(6)"); @@ -68,6 +104,8 @@ namespace Moonlight.Core.Database.Migrations .ValueGeneratedOnAdd() .HasColumnType("int"); + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + b.Property("Cpu") .HasColumnType("int"); @@ -129,6 +167,8 @@ namespace Moonlight.Core.Database.Migrations .ValueGeneratedOnAdd() .HasColumnType("int"); + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + b.Property("IpAddress") .IsRequired() .HasColumnType("longtext"); @@ -161,6 +201,8 @@ namespace Moonlight.Core.Database.Migrations .ValueGeneratedOnAdd() .HasColumnType("int"); + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + b.Property("Completed") .HasColumnType("tinyint(1)"); @@ -189,6 +231,8 @@ namespace Moonlight.Core.Database.Migrations .ValueGeneratedOnAdd() .HasColumnType("int"); + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + b.Property("AutoPull") .HasColumnType("tinyint(1)"); @@ -216,6 +260,8 @@ namespace Moonlight.Core.Database.Migrations .ValueGeneratedOnAdd() .HasColumnType("int"); + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + b.Property("AllocationsNeeded") .HasColumnType("int"); @@ -278,6 +324,8 @@ namespace Moonlight.Core.Database.Migrations .ValueGeneratedOnAdd() .HasColumnType("int"); + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + b.Property("AllowEdit") .HasColumnType("tinyint(1)"); @@ -322,6 +370,8 @@ namespace Moonlight.Core.Database.Migrations .ValueGeneratedOnAdd() .HasColumnType("int"); + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + b.Property("Name") .IsRequired() .HasColumnType("longtext"); @@ -347,6 +397,8 @@ namespace Moonlight.Core.Database.Migrations .ValueGeneratedOnAdd() .HasColumnType("int"); + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + b.Property("Fqdn") .IsRequired() .HasColumnType("longtext"); @@ -379,6 +431,8 @@ namespace Moonlight.Core.Database.Migrations .ValueGeneratedOnAdd() .HasColumnType("int"); + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + b.Property("ExecutionSeconds") .HasColumnType("int"); @@ -405,6 +459,8 @@ namespace Moonlight.Core.Database.Migrations .ValueGeneratedOnAdd() .HasColumnType("int"); + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + b.Property("Action") .IsRequired() .HasColumnType("longtext"); @@ -432,6 +488,8 @@ namespace Moonlight.Core.Database.Migrations .ValueGeneratedOnAdd() .HasColumnType("int"); + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + b.Property("Key") .IsRequired() .HasColumnType("longtext"); diff --git a/Moonlight/Core/Http/Controllers/ApiReferenceController.cs b/Moonlight/Core/Http/Controllers/ApiReferenceController.cs index 8bae2e68..cd047354 100644 --- a/Moonlight/Core/Http/Controllers/ApiReferenceController.cs +++ b/Moonlight/Core/Http/Controllers/ApiReferenceController.cs @@ -1,4 +1,5 @@ -using Microsoft.AspNetCore.Mvc; +using System.ComponentModel.DataAnnotations; +using Microsoft.AspNetCore.Mvc; using MoonCore.Services; using Moonlight.Core.Attributes; using Moonlight.Core.Configuration; @@ -20,7 +21,7 @@ public class ApiReferenceController : Controller } [HttpGet] - public async Task Get([FromQuery] string document) + public async Task Get([FromQuery][RegularExpression("^[a-z0-9_\\-]+$")] string document) { if (!ConfigService.Get().Development.EnableApiReference) return BadRequest("Api reference is disabled"); @@ -34,6 +35,7 @@ public class ApiReferenceController : Controller "Moonlight Api Reference\n" + "\n" + "\n" + + "\n"+ "\n" + "\n" + $"\n" + diff --git a/Moonlight/Core/Models/Forms/ApiKeys/CreateApiKeyForm.cs b/Moonlight/Core/Models/Forms/ApiKeys/CreateApiKeyForm.cs new file mode 100644 index 00000000..1ba1a765 --- /dev/null +++ b/Moonlight/Core/Models/Forms/ApiKeys/CreateApiKeyForm.cs @@ -0,0 +1,18 @@ +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; + +namespace Moonlight.Core.Models.Forms.ApiKeys; + +public class CreateApiKeyForm +{ + [Required(ErrorMessage = "You need to provide a description")] + [Description("Write a note here for which application the api key is used for")] + public string Description { get; set; } = ""; + + [Required(ErrorMessage = "You need to specify the expiration date of the api key")] + [Description("Specify when the api key should expire")] + public DateTime ExpiresAt { get; set; } = DateTime.UtcNow; + + [Required(ErrorMessage = "You need to specify what permissions the api key should have")] + public string Permissions { get; set; } = "[]"; +} \ No newline at end of file diff --git a/Moonlight/Core/Models/Forms/ApiKeys/UpdateApiKeyForm.cs b/Moonlight/Core/Models/Forms/ApiKeys/UpdateApiKeyForm.cs new file mode 100644 index 00000000..1ce3d059 --- /dev/null +++ b/Moonlight/Core/Models/Forms/ApiKeys/UpdateApiKeyForm.cs @@ -0,0 +1,18 @@ +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; + +namespace Moonlight.Core.Models.Forms.ApiKeys; + +public class UpdateApiKeyForm +{ + [Required(ErrorMessage = "You need to provide a description")] + [Description("Write a note here for which application the api key is used for")] + public string Description { get; set; } = ""; + + [Required(ErrorMessage = "You need to specify the expiration date of the api key")] + [Description("Specify when the api key should expire")] + public DateTime ExpiresAt { get; set; } + + [Required(ErrorMessage = "You need to specify what permissions the api key should have")] + public string Permissions { get; set; } = "[]"; +} \ No newline at end of file diff --git a/Moonlight/Core/UI/Components/Navigations/AdminApiNavigation.razor b/Moonlight/Core/UI/Components/Navigations/AdminApiNavigation.razor new file mode 100644 index 00000000..d589ab6d --- /dev/null +++ b/Moonlight/Core/UI/Components/Navigations/AdminApiNavigation.razor @@ -0,0 +1,15 @@ +
+ +
+ +@code +{ + [Parameter] public int Index { get; set; } = 0; +} \ No newline at end of file diff --git a/Moonlight/Core/UI/Views/Admin/Api/Index.razor b/Moonlight/Core/UI/Views/Admin/Api/Index.razor new file mode 100644 index 00000000..06d66566 --- /dev/null +++ b/Moonlight/Core/UI/Views/Admin/Api/Index.razor @@ -0,0 +1,80 @@ +@page "/admin/api" + +@using MoonCore.Services +@using Moonlight.Core.Configuration +@using Moonlight.Core.Interfaces +@using Moonlight.Core.Services +@using Moonlight.Core.UI.Components.Navigations + +@inject PluginService PluginService +@inject ConfigService ConfigService + +@attribute [RequirePermission(9998)] + + + + + These apis allow other applications to communicate with moonlight and for example create a new user. + These apis are still work in progress and might change a lot so dont be mad at me if i change how they work. + + +
+
+ + + + + + + + + + +
+
+ +@code +{ + private ApiModel[] Apis; + + private async Task LoadApis(LazyLoader _) + { + List models = new(); + + foreach (var definition in await PluginService.GetImplementations()) + { + models.Add(new() + { + Id = definition.GetId(), + Name = definition.GetName(), + Version = definition.GetVersion() + }); + } + + Apis = models.ToArray(); + } + + class ApiModel + { + public string Id { get; set; } + public string Name { get; set; } + public string Version { get; set; } + } +} diff --git a/Moonlight/Core/UI/Views/Admin/Api/Keys.razor b/Moonlight/Core/UI/Views/Admin/Api/Keys.razor new file mode 100644 index 00000000..c524c8e7 --- /dev/null +++ b/Moonlight/Core/UI/Views/Admin/Api/Keys.razor @@ -0,0 +1,68 @@ +@page "/admin/api/keys" + +@using MoonCore.Abstractions +@using MoonCore.Helpers +@using MoonCoreUI.Services +@using Moonlight.Core.Database.Entities +@using Moonlight.Core.Models.Forms.ApiKeys +@using Moonlight.Core.UI.Components.Navigations + +@inject ClipboardService ClipboardService +@inject ToastService ToastService + +@attribute [RequirePermission(9998)] + + + +
+ + + + + + + + + + + + + + + +
+ +@code +{ + private IEnumerable ApiKeysLoader(Repository repository) + { + return repository.Get(); + } + + private async Task ValidateAdd(ApiKey apiKey) + { + var key = Formatter.GenerateString(32); + apiKey.Key = key; + + await ClipboardService.Copy(key); + await ToastService.Info("Copied api key into your clipboard"); + } +} diff --git a/Moonlight/Moonlight.csproj b/Moonlight/Moonlight.csproj index 847bd682..3007059b 100644 --- a/Moonlight/Moonlight.csproj +++ b/Moonlight/Moonlight.csproj @@ -87,6 +87,10 @@ + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + all runtime; build; native; contentfiles; analyzers; buildtransitive