diff --git a/Moonlight.ApiServer.Runtime/Program.cs b/Moonlight.ApiServer.Runtime/Program.cs index 237a59af..94027838 100644 --- a/Moonlight.ApiServer.Runtime/Program.cs +++ b/Moonlight.ApiServer.Runtime/Program.cs @@ -10,15 +10,15 @@ await startup.Run(args, pluginLoader.Instances); var cs = new Startup(); -await cs.Initialize(args, pluginLoader.Instances); +await cs.InitializeAsync(args, pluginLoader.Instances); var builder = WebApplication.CreateBuilder(args); -await cs.AddMoonlight(builder); +await cs.AddMoonlightAsync(builder); var app = builder.Build(); -await cs.AddMoonlight(app); +await cs.AddMoonlightAsync(app); // Handle setup of wasm app hosting in the runtime // so the Moonlight.ApiServer doesn't need the wasm package diff --git a/Moonlight.ApiServer/Configuration/AppConfiguration.cs b/Moonlight.ApiServer/Configuration/AppConfiguration.cs index 34bebf5f..bdcc9b42 100644 --- a/Moonlight.ApiServer/Configuration/AppConfiguration.cs +++ b/Moonlight.ApiServer/Configuration/AppConfiguration.cs @@ -62,7 +62,7 @@ public record AppConfiguration public record FilesData { [YamlMember(Description = "The maximum file size limit a combine operation is allowed to process")] - public long CombineLimit { get; set; } = ByteConverter.FromGigaBytes(5).MegaBytes; + public double CombineLimit { get; set; } = ByteConverter.FromGigaBytes(5).MegaBytes; } public record FrontendData diff --git a/Moonlight.ApiServer/Database/CoreDataContext.cs b/Moonlight.ApiServer/Database/CoreDataContext.cs index 447f716a..5f6df282 100644 --- a/Moonlight.ApiServer/Database/CoreDataContext.cs +++ b/Moonlight.ApiServer/Database/CoreDataContext.cs @@ -1,34 +1,47 @@ using Hangfire.EntityFrameworkCore; using Microsoft.EntityFrameworkCore; -using MoonCore.Extended.SingleDb; using Moonlight.ApiServer.Configuration; using Moonlight.ApiServer.Database.Entities; using Moonlight.ApiServer.Models; namespace Moonlight.ApiServer.Database; -public class CoreDataContext : DatabaseContext +public class CoreDataContext : DbContext { - public override string Prefix { get; } = "Core"; - + private readonly AppConfiguration Configuration; + public DbSet Users { get; set; } public DbSet ApiKeys { get; set; } public DbSet Themes { get; set; } public CoreDataContext(AppConfiguration configuration) { - Options = new() + Configuration = configuration; + } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + if(optionsBuilder.IsConfigured) + return; + + var database = Configuration.Database; + + var connectionString = $"Host={database.Host};" + + $"Port={database.Port};" + + $"Database={database.Database};" + + $"Username={database.Username};" + + $"Password={database.Password}"; + + optionsBuilder.UseNpgsql(connectionString, builder => { - Host = configuration.Database.Host, - Port = configuration.Database.Port, - Username = configuration.Database.Username, - Password = configuration.Database.Password, - Database = configuration.Database.Database - }; + builder.MigrationsHistoryTable("MigrationsHistory", "core"); + }); } protected override void OnModelCreating(ModelBuilder modelBuilder) { + modelBuilder.Model.SetDefaultSchema("core"); + base.OnModelCreating(modelBuilder); modelBuilder.OnHangfireModelCreating(); diff --git a/Moonlight.ApiServer/Database/Migrations/20250226080942_AddedUsersAndApiKey.Designer.cs b/Moonlight.ApiServer/Database/Migrations/20250226080942_AddedUsersAndApiKey.Designer.cs deleted file mode 100644 index 8b884463..00000000 --- a/Moonlight.ApiServer/Database/Migrations/20250226080942_AddedUsersAndApiKey.Designer.cs +++ /dev/null @@ -1,90 +0,0 @@ -// -using System; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Moonlight.ApiServer.Database; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -#nullable disable - -namespace Moonlight.ApiServer.Database.Migrations -{ - [DbContext(typeof(CoreDataContext))] - [Migration("20250226080942_AddedUsersAndApiKey")] - partial class AddedUsersAndApiKey - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "8.0.11") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("Moonlight.ApiServer.Database.Entities.ApiKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Description") - .IsRequired() - .HasColumnType("text"); - - b.Property("ExpiresAt") - .HasColumnType("timestamp with time zone"); - - b.Property("PermissionsJson") - .IsRequired() - .HasColumnType("jsonb"); - - b.Property("Secret") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.ToTable("Core_ApiKeys", (string)null); - }); - - modelBuilder.Entity("Moonlight.ApiServer.Database.Entities.User", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Email") - .IsRequired() - .HasColumnType("text"); - - b.Property("Password") - .IsRequired() - .HasColumnType("text"); - - b.Property("PermissionsJson") - .IsRequired() - .HasColumnType("jsonb"); - - b.Property("TokenValidTimestamp") - .HasColumnType("timestamp with time zone"); - - b.Property("Username") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.ToTable("Core_Users", (string)null); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Moonlight.ApiServer/Database/Migrations/20250226080942_AddedUsersAndApiKey.cs b/Moonlight.ApiServer/Database/Migrations/20250226080942_AddedUsersAndApiKey.cs deleted file mode 100644 index d6024fb0..00000000 --- a/Moonlight.ApiServer/Database/Migrations/20250226080942_AddedUsersAndApiKey.cs +++ /dev/null @@ -1,59 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -#nullable disable - -namespace Moonlight.ApiServer.Database.Migrations -{ - /// - public partial class AddedUsersAndApiKey : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateTable( - name: "Core_ApiKeys", - columns: table => new - { - Id = table.Column(type: "integer", nullable: false) - .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), - Secret = table.Column(type: "text", nullable: false), - Description = table.Column(type: "text", nullable: false), - PermissionsJson = table.Column(type: "jsonb", nullable: false), - ExpiresAt = table.Column(type: "timestamp with time zone", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_Core_ApiKeys", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "Core_Users", - columns: table => new - { - Id = table.Column(type: "integer", nullable: false) - .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), - Username = table.Column(type: "text", nullable: false), - Email = table.Column(type: "text", nullable: false), - Password = table.Column(type: "text", nullable: false), - TokenValidTimestamp = table.Column(type: "timestamp with time zone", nullable: false), - PermissionsJson = table.Column(type: "jsonb", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_Core_Users", x => x.Id); - }); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "Core_ApiKeys"); - - migrationBuilder.DropTable( - name: "Core_Users"); - } - } -} diff --git a/Moonlight.ApiServer/Database/Migrations/20250314095412_ModifiedApiKeyEntity.Designer.cs b/Moonlight.ApiServer/Database/Migrations/20250314095412_ModifiedApiKeyEntity.Designer.cs deleted file mode 100644 index 6e3d6105..00000000 --- a/Moonlight.ApiServer/Database/Migrations/20250314095412_ModifiedApiKeyEntity.Designer.cs +++ /dev/null @@ -1,89 +0,0 @@ -// -using System; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Moonlight.ApiServer.Database; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -#nullable disable - -namespace Moonlight.ApiServer.Database.Migrations -{ - [DbContext(typeof(CoreDataContext))] - [Migration("20250314095412_ModifiedApiKeyEntity")] - partial class ModifiedApiKeyEntity - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "8.0.11") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("Moonlight.ApiServer.Database.Entities.ApiKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("CreatedAt") - .HasColumnType("timestamp with time zone"); - - b.Property("Description") - .IsRequired() - .HasColumnType("text"); - - b.Property("ExpiresAt") - .HasColumnType("timestamp with time zone"); - - b.Property("PermissionsJson") - .IsRequired() - .HasColumnType("jsonb"); - - b.HasKey("Id"); - - b.ToTable("Core_ApiKeys", (string)null); - }); - - modelBuilder.Entity("Moonlight.ApiServer.Database.Entities.User", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Email") - .IsRequired() - .HasColumnType("text"); - - b.Property("Password") - .IsRequired() - .HasColumnType("text"); - - b.Property("PermissionsJson") - .IsRequired() - .HasColumnType("jsonb"); - - b.Property("TokenValidTimestamp") - .HasColumnType("timestamp with time zone"); - - b.Property("Username") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.ToTable("Core_Users", (string)null); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Moonlight.ApiServer/Database/Migrations/20250314095412_ModifiedApiKeyEntity.cs b/Moonlight.ApiServer/Database/Migrations/20250314095412_ModifiedApiKeyEntity.cs deleted file mode 100644 index b7d47632..00000000 --- a/Moonlight.ApiServer/Database/Migrations/20250314095412_ModifiedApiKeyEntity.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Moonlight.ApiServer.Database.Migrations -{ - /// - public partial class ModifiedApiKeyEntity : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropColumn( - name: "Secret", - table: "Core_ApiKeys"); - - migrationBuilder.AddColumn( - name: "CreatedAt", - table: "Core_ApiKeys", - type: "timestamp with time zone", - nullable: false, - defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropColumn( - name: "CreatedAt", - table: "Core_ApiKeys"); - - migrationBuilder.AddColumn( - name: "Secret", - table: "Core_ApiKeys", - type: "text", - nullable: false, - defaultValue: ""); - } - } -} diff --git a/Moonlight.ApiServer/Database/Migrations/20250405172522_AddedHangfireTables.Designer.cs b/Moonlight.ApiServer/Database/Migrations/20250405172522_AddedHangfireTables.Designer.cs deleted file mode 100644 index 96b7445b..00000000 --- a/Moonlight.ApiServer/Database/Migrations/20250405172522_AddedHangfireTables.Designer.cs +++ /dev/null @@ -1,393 +0,0 @@ -// -using System; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Moonlight.ApiServer.Database; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -#nullable disable - -namespace Moonlight.ApiServer.Database.Migrations -{ - [DbContext(typeof(CoreDataContext))] - [Migration("20250405172522_AddedHangfireTables")] - partial class AddedHangfireTables - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "8.0.11") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireCounter", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("ExpireAt") - .HasColumnType("timestamp with time zone"); - - b.Property("Key") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("Value") - .HasColumnType("bigint"); - - b.HasKey("Id"); - - b.HasIndex("ExpireAt"); - - b.HasIndex("Key", "Value"); - - b.ToTable("HangfireCounter"); - }); - - modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireHash", b => - { - b.Property("Key") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("Field") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("ExpireAt") - .HasColumnType("timestamp with time zone"); - - b.Property("Value") - .HasColumnType("text"); - - b.HasKey("Key", "Field"); - - b.HasIndex("ExpireAt"); - - b.ToTable("HangfireHash"); - }); - - modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireJob", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("CreatedAt") - .HasColumnType("timestamp with time zone"); - - b.Property("ExpireAt") - .HasColumnType("timestamp with time zone"); - - b.Property("InvocationData") - .IsRequired() - .HasColumnType("text"); - - b.Property("StateId") - .HasColumnType("bigint"); - - b.Property("StateName") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.HasKey("Id"); - - b.HasIndex("ExpireAt"); - - b.HasIndex("StateId"); - - b.HasIndex("StateName"); - - b.ToTable("HangfireJob"); - }); - - modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireJobParameter", b => - { - b.Property("JobId") - .HasColumnType("bigint"); - - b.Property("Name") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("Value") - .HasColumnType("text"); - - b.HasKey("JobId", "Name"); - - b.ToTable("HangfireJobParameter"); - }); - - modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireList", b => - { - b.Property("Key") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("Position") - .HasColumnType("integer"); - - b.Property("ExpireAt") - .HasColumnType("timestamp with time zone"); - - b.Property("Value") - .HasColumnType("text"); - - b.HasKey("Key", "Position"); - - b.HasIndex("ExpireAt"); - - b.ToTable("HangfireList"); - }); - - modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireLock", b => - { - b.Property("Id") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("AcquiredAt") - .HasColumnType("timestamp with time zone"); - - b.HasKey("Id"); - - b.ToTable("HangfireLock"); - }); - - modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireQueuedJob", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("FetchedAt") - .IsConcurrencyToken() - .HasColumnType("timestamp with time zone"); - - b.Property("JobId") - .HasColumnType("bigint"); - - b.Property("Queue") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.HasKey("Id"); - - b.HasIndex("JobId"); - - b.HasIndex("Queue", "FetchedAt"); - - b.ToTable("HangfireQueuedJob"); - }); - - modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireServer", b => - { - b.Property("Id") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("Heartbeat") - .HasColumnType("timestamp with time zone"); - - b.Property("Queues") - .IsRequired() - .HasColumnType("text"); - - b.Property("StartedAt") - .HasColumnType("timestamp with time zone"); - - b.Property("WorkerCount") - .HasColumnType("integer"); - - b.HasKey("Id"); - - b.HasIndex("Heartbeat"); - - b.ToTable("HangfireServer"); - }); - - modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireSet", b => - { - b.Property("Key") - .HasMaxLength(100) - .HasColumnType("character varying(100)"); - - b.Property("Value") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("ExpireAt") - .HasColumnType("timestamp with time zone"); - - b.Property("Score") - .HasColumnType("double precision"); - - b.HasKey("Key", "Value"); - - b.HasIndex("ExpireAt"); - - b.HasIndex("Key", "Score"); - - b.ToTable("HangfireSet"); - }); - - modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireState", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("CreatedAt") - .HasColumnType("timestamp with time zone"); - - b.Property("Data") - .IsRequired() - .HasColumnType("text"); - - b.Property("JobId") - .HasColumnType("bigint"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("Reason") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("JobId"); - - b.ToTable("HangfireState"); - }); - - modelBuilder.Entity("Moonlight.ApiServer.Database.Entities.ApiKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("CreatedAt") - .HasColumnType("timestamp with time zone"); - - b.Property("Description") - .IsRequired() - .HasColumnType("text"); - - b.Property("ExpiresAt") - .HasColumnType("timestamp with time zone"); - - b.Property("PermissionsJson") - .IsRequired() - .HasColumnType("jsonb"); - - b.HasKey("Id"); - - b.ToTable("Core_ApiKeys", (string)null); - }); - - modelBuilder.Entity("Moonlight.ApiServer.Database.Entities.User", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Email") - .IsRequired() - .HasColumnType("text"); - - b.Property("Password") - .IsRequired() - .HasColumnType("text"); - - b.Property("PermissionsJson") - .IsRequired() - .HasColumnType("jsonb"); - - b.Property("TokenValidTimestamp") - .HasColumnType("timestamp with time zone"); - - b.Property("Username") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.ToTable("Core_Users", (string)null); - }); - - modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireJob", b => - { - b.HasOne("Hangfire.EntityFrameworkCore.HangfireState", "State") - .WithMany() - .HasForeignKey("StateId"); - - b.Navigation("State"); - }); - - modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireJobParameter", b => - { - b.HasOne("Hangfire.EntityFrameworkCore.HangfireJob", "Job") - .WithMany("Parameters") - .HasForeignKey("JobId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Job"); - }); - - modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireQueuedJob", b => - { - b.HasOne("Hangfire.EntityFrameworkCore.HangfireJob", "Job") - .WithMany("QueuedJobs") - .HasForeignKey("JobId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Job"); - }); - - modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireState", b => - { - b.HasOne("Hangfire.EntityFrameworkCore.HangfireJob", "Job") - .WithMany("States") - .HasForeignKey("JobId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Job"); - }); - - modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireJob", b => - { - b.Navigation("Parameters"); - - b.Navigation("QueuedJobs"); - - b.Navigation("States"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Moonlight.ApiServer/Database/Migrations/20250712202608_SwitchedToPgArraysForPermissions.Designer.cs b/Moonlight.ApiServer/Database/Migrations/20250712202608_SwitchedToPgArraysForPermissions.Designer.cs deleted file mode 100644 index fcf228a0..00000000 --- a/Moonlight.ApiServer/Database/Migrations/20250712202608_SwitchedToPgArraysForPermissions.Designer.cs +++ /dev/null @@ -1,393 +0,0 @@ -// -using System; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Moonlight.ApiServer.Database; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -#nullable disable - -namespace Moonlight.ApiServer.Database.Migrations -{ - [DbContext(typeof(CoreDataContext))] - [Migration("20250712202608_SwitchedToPgArraysForPermissions")] - partial class SwitchedToPgArraysForPermissions - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "9.0.7") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireCounter", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("ExpireAt") - .HasColumnType("timestamp with time zone"); - - b.Property("Key") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("Value") - .HasColumnType("bigint"); - - b.HasKey("Id"); - - b.HasIndex("ExpireAt"); - - b.HasIndex("Key", "Value"); - - b.ToTable("HangfireCounter"); - }); - - modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireHash", b => - { - b.Property("Key") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("Field") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("ExpireAt") - .HasColumnType("timestamp with time zone"); - - b.Property("Value") - .HasColumnType("text"); - - b.HasKey("Key", "Field"); - - b.HasIndex("ExpireAt"); - - b.ToTable("HangfireHash"); - }); - - modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireJob", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("CreatedAt") - .HasColumnType("timestamp with time zone"); - - b.Property("ExpireAt") - .HasColumnType("timestamp with time zone"); - - b.Property("InvocationData") - .IsRequired() - .HasColumnType("text"); - - b.Property("StateId") - .HasColumnType("bigint"); - - b.Property("StateName") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.HasKey("Id"); - - b.HasIndex("ExpireAt"); - - b.HasIndex("StateId"); - - b.HasIndex("StateName"); - - b.ToTable("HangfireJob"); - }); - - modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireJobParameter", b => - { - b.Property("JobId") - .HasColumnType("bigint"); - - b.Property("Name") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("Value") - .HasColumnType("text"); - - b.HasKey("JobId", "Name"); - - b.ToTable("HangfireJobParameter"); - }); - - modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireList", b => - { - b.Property("Key") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("Position") - .HasColumnType("integer"); - - b.Property("ExpireAt") - .HasColumnType("timestamp with time zone"); - - b.Property("Value") - .HasColumnType("text"); - - b.HasKey("Key", "Position"); - - b.HasIndex("ExpireAt"); - - b.ToTable("HangfireList"); - }); - - modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireLock", b => - { - b.Property("Id") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("AcquiredAt") - .HasColumnType("timestamp with time zone"); - - b.HasKey("Id"); - - b.ToTable("HangfireLock"); - }); - - modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireQueuedJob", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("FetchedAt") - .IsConcurrencyToken() - .HasColumnType("timestamp with time zone"); - - b.Property("JobId") - .HasColumnType("bigint"); - - b.Property("Queue") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.HasKey("Id"); - - b.HasIndex("JobId"); - - b.HasIndex("Queue", "FetchedAt"); - - b.ToTable("HangfireQueuedJob"); - }); - - modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireServer", b => - { - b.Property("Id") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("Heartbeat") - .HasColumnType("timestamp with time zone"); - - b.Property("Queues") - .IsRequired() - .HasColumnType("text"); - - b.Property("StartedAt") - .HasColumnType("timestamp with time zone"); - - b.Property("WorkerCount") - .HasColumnType("integer"); - - b.HasKey("Id"); - - b.HasIndex("Heartbeat"); - - b.ToTable("HangfireServer"); - }); - - modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireSet", b => - { - b.Property("Key") - .HasMaxLength(100) - .HasColumnType("character varying(100)"); - - b.Property("Value") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("ExpireAt") - .HasColumnType("timestamp with time zone"); - - b.Property("Score") - .HasColumnType("double precision"); - - b.HasKey("Key", "Value"); - - b.HasIndex("ExpireAt"); - - b.HasIndex("Key", "Score"); - - b.ToTable("HangfireSet"); - }); - - modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireState", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("CreatedAt") - .HasColumnType("timestamp with time zone"); - - b.Property("Data") - .IsRequired() - .HasColumnType("text"); - - b.Property("JobId") - .HasColumnType("bigint"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("Reason") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("JobId"); - - b.ToTable("HangfireState"); - }); - - modelBuilder.Entity("Moonlight.ApiServer.Database.Entities.ApiKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("CreatedAt") - .HasColumnType("timestamp with time zone"); - - b.Property("Description") - .IsRequired() - .HasColumnType("text"); - - b.Property("ExpiresAt") - .HasColumnType("timestamp with time zone"); - - b.PrimitiveCollection("Permissions") - .IsRequired() - .HasColumnType("text[]"); - - b.HasKey("Id"); - - b.ToTable("Core_ApiKeys", (string)null); - }); - - modelBuilder.Entity("Moonlight.ApiServer.Database.Entities.User", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Email") - .IsRequired() - .HasColumnType("text"); - - b.Property("Password") - .IsRequired() - .HasColumnType("text"); - - b.PrimitiveCollection("Permissions") - .IsRequired() - .HasColumnType("text[]"); - - b.Property("TokenValidTimestamp") - .HasColumnType("timestamp with time zone"); - - b.Property("Username") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.ToTable("Core_Users", (string)null); - }); - - modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireJob", b => - { - b.HasOne("Hangfire.EntityFrameworkCore.HangfireState", "State") - .WithMany() - .HasForeignKey("StateId"); - - b.Navigation("State"); - }); - - modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireJobParameter", b => - { - b.HasOne("Hangfire.EntityFrameworkCore.HangfireJob", "Job") - .WithMany("Parameters") - .HasForeignKey("JobId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Job"); - }); - - modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireQueuedJob", b => - { - b.HasOne("Hangfire.EntityFrameworkCore.HangfireJob", "Job") - .WithMany("QueuedJobs") - .HasForeignKey("JobId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Job"); - }); - - modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireState", b => - { - b.HasOne("Hangfire.EntityFrameworkCore.HangfireJob", "Job") - .WithMany("States") - .HasForeignKey("JobId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Job"); - }); - - modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireJob", b => - { - b.Navigation("Parameters"); - - b.Navigation("QueuedJobs"); - - b.Navigation("States"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Moonlight.ApiServer/Database/Migrations/20250712202608_SwitchedToPgArraysForPermissions.cs b/Moonlight.ApiServer/Database/Migrations/20250712202608_SwitchedToPgArraysForPermissions.cs deleted file mode 100644 index 887066c4..00000000 --- a/Moonlight.ApiServer/Database/Migrations/20250712202608_SwitchedToPgArraysForPermissions.cs +++ /dev/null @@ -1,62 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Moonlight.ApiServer.Database.Migrations -{ - /// - public partial class SwitchedToPgArraysForPermissions : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropColumn( - name: "PermissionsJson", - table: "Core_Users"); - - migrationBuilder.DropColumn( - name: "PermissionsJson", - table: "Core_ApiKeys"); - - migrationBuilder.AddColumn( - name: "Permissions", - table: "Core_Users", - type: "text[]", - nullable: false, - defaultValue: new string[0]); - - migrationBuilder.AddColumn( - name: "Permissions", - table: "Core_ApiKeys", - type: "text[]", - nullable: false, - defaultValue: new string[0]); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropColumn( - name: "Permissions", - table: "Core_Users"); - - migrationBuilder.DropColumn( - name: "Permissions", - table: "Core_ApiKeys"); - - migrationBuilder.AddColumn( - name: "PermissionsJson", - table: "Core_Users", - type: "jsonb", - nullable: false, - defaultValue: ""); - - migrationBuilder.AddColumn( - name: "PermissionsJson", - table: "Core_ApiKeys", - type: "jsonb", - nullable: false, - defaultValue: ""); - } - } -} diff --git a/Moonlight.ApiServer/Database/Migrations/20250720203346_AddedThemes.cs b/Moonlight.ApiServer/Database/Migrations/20250720203346_AddedThemes.cs deleted file mode 100644 index 0b4b643e..00000000 --- a/Moonlight.ApiServer/Database/Migrations/20250720203346_AddedThemes.cs +++ /dev/null @@ -1,41 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -#nullable disable - -namespace Moonlight.ApiServer.Database.Migrations -{ - /// - public partial class AddedThemes : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateTable( - name: "Core_Themes", - columns: table => new - { - Id = table.Column(type: "integer", nullable: false) - .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), - IsEnabled = table.Column(type: "boolean", nullable: false), - Name = table.Column(type: "text", nullable: false), - Author = table.Column(type: "text", nullable: false), - Version = table.Column(type: "text", nullable: false), - UpdateUrl = table.Column(type: "text", nullable: true), - DonateUrl = table.Column(type: "text", nullable: true), - Content = table.Column(type: "jsonb", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_Core_Themes", x => x.Id); - }); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "Core_Themes"); - } - } -} diff --git a/Moonlight.ApiServer/Database/Migrations/20250720203346_AddedThemes.Designer.cs b/Moonlight.ApiServer/Database/Migrations/20250919201409_RecreatedMigrationsForChangeOfSchema.Designer.cs similarity index 94% rename from Moonlight.ApiServer/Database/Migrations/20250720203346_AddedThemes.Designer.cs rename to Moonlight.ApiServer/Database/Migrations/20250919201409_RecreatedMigrationsForChangeOfSchema.Designer.cs index 0e2cf75f..9c9d9577 100644 --- a/Moonlight.ApiServer/Database/Migrations/20250720203346_AddedThemes.Designer.cs +++ b/Moonlight.ApiServer/Database/Migrations/20250919201409_RecreatedMigrationsForChangeOfSchema.Designer.cs @@ -12,15 +12,16 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; namespace Moonlight.ApiServer.Database.Migrations { [DbContext(typeof(CoreDataContext))] - [Migration("20250720203346_AddedThemes")] - partial class AddedThemes + [Migration("20250919201409_RecreatedMigrationsForChangeOfSchema")] + partial class RecreatedMigrationsForChangeOfSchema { /// protected override void BuildTargetModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "9.0.7") + .HasDefaultSchema("core") + .HasAnnotation("ProductVersion", "9.0.8") .HasAnnotation("Relational:MaxIdentifierLength", 63); NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); @@ -50,7 +51,7 @@ namespace Moonlight.ApiServer.Database.Migrations b.HasIndex("Key", "Value"); - b.ToTable("HangfireCounter"); + b.ToTable("HangfireCounter", "core"); }); modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireHash", b => @@ -73,7 +74,7 @@ namespace Moonlight.ApiServer.Database.Migrations b.HasIndex("ExpireAt"); - b.ToTable("HangfireHash"); + b.ToTable("HangfireHash", "core"); }); modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireJob", b => @@ -109,7 +110,7 @@ namespace Moonlight.ApiServer.Database.Migrations b.HasIndex("StateName"); - b.ToTable("HangfireJob"); + b.ToTable("HangfireJob", "core"); }); modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireJobParameter", b => @@ -126,7 +127,7 @@ namespace Moonlight.ApiServer.Database.Migrations b.HasKey("JobId", "Name"); - b.ToTable("HangfireJobParameter"); + b.ToTable("HangfireJobParameter", "core"); }); modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireList", b => @@ -148,7 +149,7 @@ namespace Moonlight.ApiServer.Database.Migrations b.HasIndex("ExpireAt"); - b.ToTable("HangfireList"); + b.ToTable("HangfireList", "core"); }); modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireLock", b => @@ -162,7 +163,7 @@ namespace Moonlight.ApiServer.Database.Migrations b.HasKey("Id"); - b.ToTable("HangfireLock"); + b.ToTable("HangfireLock", "core"); }); modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireQueuedJob", b => @@ -191,7 +192,7 @@ namespace Moonlight.ApiServer.Database.Migrations b.HasIndex("Queue", "FetchedAt"); - b.ToTable("HangfireQueuedJob"); + b.ToTable("HangfireQueuedJob", "core"); }); modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireServer", b => @@ -217,7 +218,7 @@ namespace Moonlight.ApiServer.Database.Migrations b.HasIndex("Heartbeat"); - b.ToTable("HangfireServer"); + b.ToTable("HangfireServer", "core"); }); modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireSet", b => @@ -242,7 +243,7 @@ namespace Moonlight.ApiServer.Database.Migrations b.HasIndex("Key", "Score"); - b.ToTable("HangfireSet"); + b.ToTable("HangfireSet", "core"); }); modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireState", b => @@ -275,7 +276,7 @@ namespace Moonlight.ApiServer.Database.Migrations b.HasIndex("JobId"); - b.ToTable("HangfireState"); + b.ToTable("HangfireState", "core"); }); modelBuilder.Entity("Moonlight.ApiServer.Database.Entities.ApiKey", b => @@ -302,7 +303,7 @@ namespace Moonlight.ApiServer.Database.Migrations b.HasKey("Id"); - b.ToTable("Core_ApiKeys", (string)null); + b.ToTable("ApiKeys", "core"); }); modelBuilder.Entity("Moonlight.ApiServer.Database.Entities.Theme", b => @@ -336,7 +337,7 @@ namespace Moonlight.ApiServer.Database.Migrations b.HasKey("Id"); - b.ToTable("Core_Themes", (string)null); + b.ToTable("Themes", "core"); }); modelBuilder.Entity("Moonlight.ApiServer.Database.Entities.User", b => @@ -368,7 +369,7 @@ namespace Moonlight.ApiServer.Database.Migrations b.HasKey("Id"); - b.ToTable("Core_Users", (string)null); + b.ToTable("Users", "core"); }); modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireJob", b => @@ -515,11 +516,11 @@ namespace Moonlight.ApiServer.Database.Migrations .IsRequired() .HasColumnType("text"); - b1.Property("Depth") - .HasColumnType("real"); + b1.Property("Depth") + .HasColumnType("integer"); - b1.Property("Noise") - .HasColumnType("real"); + b1.Property("Noise") + .HasColumnType("integer"); b1.Property("RadiusBox") .HasColumnType("real"); @@ -538,7 +539,7 @@ namespace Moonlight.ApiServer.Database.Migrations b1.HasKey("ThemeId"); - b1.ToTable("Core_Themes"); + b1.ToTable("Themes", "core"); b1.ToJson("Content"); diff --git a/Moonlight.ApiServer/Database/Migrations/20250405172522_AddedHangfireTables.cs b/Moonlight.ApiServer/Database/Migrations/20250919201409_RecreatedMigrationsForChangeOfSchema.cs similarity index 70% rename from Moonlight.ApiServer/Database/Migrations/20250405172522_AddedHangfireTables.cs rename to Moonlight.ApiServer/Database/Migrations/20250919201409_RecreatedMigrationsForChangeOfSchema.cs index 9fd060d1..5330cca4 100644 --- a/Moonlight.ApiServer/Database/Migrations/20250405172522_AddedHangfireTables.cs +++ b/Moonlight.ApiServer/Database/Migrations/20250919201409_RecreatedMigrationsForChangeOfSchema.cs @@ -7,13 +7,34 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; namespace Moonlight.ApiServer.Database.Migrations { /// - public partial class AddedHangfireTables : Migration + public partial class RecreatedMigrationsForChangeOfSchema : Migration { /// protected override void Up(MigrationBuilder migrationBuilder) { + migrationBuilder.EnsureSchema( + name: "core"); + + migrationBuilder.CreateTable( + name: "ApiKeys", + schema: "core", + columns: table => new + { + Id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + Description = table.Column(type: "text", nullable: false), + Permissions = table.Column(type: "text[]", nullable: false), + ExpiresAt = table.Column(type: "timestamp with time zone", nullable: false), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_ApiKeys", x => x.Id); + }); + migrationBuilder.CreateTable( name: "HangfireCounter", + schema: "core", columns: table => new { Id = table.Column(type: "bigint", nullable: false) @@ -29,6 +50,7 @@ namespace Moonlight.ApiServer.Database.Migrations migrationBuilder.CreateTable( name: "HangfireHash", + schema: "core", columns: table => new { Key = table.Column(type: "character varying(256)", maxLength: 256, nullable: false), @@ -43,6 +65,7 @@ namespace Moonlight.ApiServer.Database.Migrations migrationBuilder.CreateTable( name: "HangfireList", + schema: "core", columns: table => new { Key = table.Column(type: "character varying(256)", maxLength: 256, nullable: false), @@ -57,6 +80,7 @@ namespace Moonlight.ApiServer.Database.Migrations migrationBuilder.CreateTable( name: "HangfireLock", + schema: "core", columns: table => new { Id = table.Column(type: "character varying(256)", maxLength: 256, nullable: false), @@ -69,6 +93,7 @@ namespace Moonlight.ApiServer.Database.Migrations migrationBuilder.CreateTable( name: "HangfireServer", + schema: "core", columns: table => new { Id = table.Column(type: "character varying(256)", maxLength: 256, nullable: false), @@ -84,6 +109,7 @@ namespace Moonlight.ApiServer.Database.Migrations migrationBuilder.CreateTable( name: "HangfireSet", + schema: "core", columns: table => new { Key = table.Column(type: "character varying(100)", maxLength: 100, nullable: false), @@ -96,8 +122,47 @@ namespace Moonlight.ApiServer.Database.Migrations table.PrimaryKey("PK_HangfireSet", x => new { x.Key, x.Value }); }); + migrationBuilder.CreateTable( + name: "Themes", + schema: "core", + columns: table => new + { + Id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + IsEnabled = table.Column(type: "boolean", nullable: false), + Name = table.Column(type: "text", nullable: false), + Author = table.Column(type: "text", nullable: false), + Version = table.Column(type: "text", nullable: false), + UpdateUrl = table.Column(type: "text", nullable: true), + DonateUrl = table.Column(type: "text", nullable: true), + Content = table.Column(type: "jsonb", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Themes", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Users", + schema: "core", + columns: table => new + { + Id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + Username = table.Column(type: "text", nullable: false), + Email = table.Column(type: "text", nullable: false), + Password = table.Column(type: "text", nullable: false), + TokenValidTimestamp = table.Column(type: "timestamp with time zone", nullable: false), + Permissions = table.Column(type: "text[]", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Users", x => x.Id); + }); + migrationBuilder.CreateTable( name: "HangfireJob", + schema: "core", columns: table => new { Id = table.Column(type: "bigint", nullable: false) @@ -115,6 +180,7 @@ namespace Moonlight.ApiServer.Database.Migrations migrationBuilder.CreateTable( name: "HangfireJobParameter", + schema: "core", columns: table => new { JobId = table.Column(type: "bigint", nullable: false), @@ -127,6 +193,7 @@ namespace Moonlight.ApiServer.Database.Migrations table.ForeignKey( name: "FK_HangfireJobParameter_HangfireJob_JobId", column: x => x.JobId, + principalSchema: "core", principalTable: "HangfireJob", principalColumn: "Id", onDelete: ReferentialAction.Cascade); @@ -134,6 +201,7 @@ namespace Moonlight.ApiServer.Database.Migrations migrationBuilder.CreateTable( name: "HangfireQueuedJob", + schema: "core", columns: table => new { Id = table.Column(type: "bigint", nullable: false) @@ -148,6 +216,7 @@ namespace Moonlight.ApiServer.Database.Migrations table.ForeignKey( name: "FK_HangfireQueuedJob_HangfireJob_JobId", column: x => x.JobId, + principalSchema: "core", principalTable: "HangfireJob", principalColumn: "Id", onDelete: ReferentialAction.Cascade); @@ -155,6 +224,7 @@ namespace Moonlight.ApiServer.Database.Migrations migrationBuilder.CreateTable( name: "HangfireState", + schema: "core", columns: table => new { Id = table.Column(type: "bigint", nullable: false) @@ -171,6 +241,7 @@ namespace Moonlight.ApiServer.Database.Migrations table.ForeignKey( name: "FK_HangfireState_HangfireJob_JobId", column: x => x.JobId, + principalSchema: "core", principalTable: "HangfireJob", principalColumn: "Id", onDelete: ReferentialAction.Cascade); @@ -178,73 +249,88 @@ namespace Moonlight.ApiServer.Database.Migrations migrationBuilder.CreateIndex( name: "IX_HangfireCounter_ExpireAt", + schema: "core", table: "HangfireCounter", column: "ExpireAt"); migrationBuilder.CreateIndex( name: "IX_HangfireCounter_Key_Value", + schema: "core", table: "HangfireCounter", columns: new[] { "Key", "Value" }); migrationBuilder.CreateIndex( name: "IX_HangfireHash_ExpireAt", + schema: "core", table: "HangfireHash", column: "ExpireAt"); migrationBuilder.CreateIndex( name: "IX_HangfireJob_ExpireAt", + schema: "core", table: "HangfireJob", column: "ExpireAt"); migrationBuilder.CreateIndex( name: "IX_HangfireJob_StateId", + schema: "core", table: "HangfireJob", column: "StateId"); migrationBuilder.CreateIndex( name: "IX_HangfireJob_StateName", + schema: "core", table: "HangfireJob", column: "StateName"); migrationBuilder.CreateIndex( name: "IX_HangfireList_ExpireAt", + schema: "core", table: "HangfireList", column: "ExpireAt"); migrationBuilder.CreateIndex( name: "IX_HangfireQueuedJob_JobId", + schema: "core", table: "HangfireQueuedJob", column: "JobId"); migrationBuilder.CreateIndex( name: "IX_HangfireQueuedJob_Queue_FetchedAt", + schema: "core", table: "HangfireQueuedJob", columns: new[] { "Queue", "FetchedAt" }); migrationBuilder.CreateIndex( name: "IX_HangfireServer_Heartbeat", + schema: "core", table: "HangfireServer", column: "Heartbeat"); migrationBuilder.CreateIndex( name: "IX_HangfireSet_ExpireAt", + schema: "core", table: "HangfireSet", column: "ExpireAt"); migrationBuilder.CreateIndex( name: "IX_HangfireSet_Key_Score", + schema: "core", table: "HangfireSet", columns: new[] { "Key", "Score" }); migrationBuilder.CreateIndex( name: "IX_HangfireState_JobId", + schema: "core", table: "HangfireState", column: "JobId"); migrationBuilder.AddForeignKey( name: "FK_HangfireJob_HangfireState_StateId", + schema: "core", table: "HangfireJob", column: "StateId", + principalSchema: "core", principalTable: "HangfireState", principalColumn: "Id"); } @@ -254,37 +340,60 @@ namespace Moonlight.ApiServer.Database.Migrations { migrationBuilder.DropForeignKey( name: "FK_HangfireJob_HangfireState_StateId", + schema: "core", table: "HangfireJob"); migrationBuilder.DropTable( - name: "HangfireCounter"); + name: "ApiKeys", + schema: "core"); migrationBuilder.DropTable( - name: "HangfireHash"); + name: "HangfireCounter", + schema: "core"); migrationBuilder.DropTable( - name: "HangfireJobParameter"); + name: "HangfireHash", + schema: "core"); migrationBuilder.DropTable( - name: "HangfireList"); + name: "HangfireJobParameter", + schema: "core"); migrationBuilder.DropTable( - name: "HangfireLock"); + name: "HangfireList", + schema: "core"); migrationBuilder.DropTable( - name: "HangfireQueuedJob"); + name: "HangfireLock", + schema: "core"); migrationBuilder.DropTable( - name: "HangfireServer"); + name: "HangfireQueuedJob", + schema: "core"); migrationBuilder.DropTable( - name: "HangfireSet"); + name: "HangfireServer", + schema: "core"); migrationBuilder.DropTable( - name: "HangfireState"); + name: "HangfireSet", + schema: "core"); migrationBuilder.DropTable( - name: "HangfireJob"); + name: "Themes", + schema: "core"); + + migrationBuilder.DropTable( + name: "Users", + schema: "core"); + + migrationBuilder.DropTable( + name: "HangfireState", + schema: "core"); + + migrationBuilder.DropTable( + name: "HangfireJob", + schema: "core"); } } } diff --git a/Moonlight.ApiServer/Database/Migrations/CoreDataContextModelSnapshot.cs b/Moonlight.ApiServer/Database/Migrations/CoreDataContextModelSnapshot.cs index e743555e..540e66c1 100644 --- a/Moonlight.ApiServer/Database/Migrations/CoreDataContextModelSnapshot.cs +++ b/Moonlight.ApiServer/Database/Migrations/CoreDataContextModelSnapshot.cs @@ -17,7 +17,8 @@ namespace Moonlight.ApiServer.Database.Migrations { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "9.0.7") + .HasDefaultSchema("core") + .HasAnnotation("ProductVersion", "9.0.8") .HasAnnotation("Relational:MaxIdentifierLength", 63); NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); @@ -47,7 +48,7 @@ namespace Moonlight.ApiServer.Database.Migrations b.HasIndex("Key", "Value"); - b.ToTable("HangfireCounter"); + b.ToTable("HangfireCounter", "core"); }); modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireHash", b => @@ -70,7 +71,7 @@ namespace Moonlight.ApiServer.Database.Migrations b.HasIndex("ExpireAt"); - b.ToTable("HangfireHash"); + b.ToTable("HangfireHash", "core"); }); modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireJob", b => @@ -106,7 +107,7 @@ namespace Moonlight.ApiServer.Database.Migrations b.HasIndex("StateName"); - b.ToTable("HangfireJob"); + b.ToTable("HangfireJob", "core"); }); modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireJobParameter", b => @@ -123,7 +124,7 @@ namespace Moonlight.ApiServer.Database.Migrations b.HasKey("JobId", "Name"); - b.ToTable("HangfireJobParameter"); + b.ToTable("HangfireJobParameter", "core"); }); modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireList", b => @@ -145,7 +146,7 @@ namespace Moonlight.ApiServer.Database.Migrations b.HasIndex("ExpireAt"); - b.ToTable("HangfireList"); + b.ToTable("HangfireList", "core"); }); modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireLock", b => @@ -159,7 +160,7 @@ namespace Moonlight.ApiServer.Database.Migrations b.HasKey("Id"); - b.ToTable("HangfireLock"); + b.ToTable("HangfireLock", "core"); }); modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireQueuedJob", b => @@ -188,7 +189,7 @@ namespace Moonlight.ApiServer.Database.Migrations b.HasIndex("Queue", "FetchedAt"); - b.ToTable("HangfireQueuedJob"); + b.ToTable("HangfireQueuedJob", "core"); }); modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireServer", b => @@ -214,7 +215,7 @@ namespace Moonlight.ApiServer.Database.Migrations b.HasIndex("Heartbeat"); - b.ToTable("HangfireServer"); + b.ToTable("HangfireServer", "core"); }); modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireSet", b => @@ -239,7 +240,7 @@ namespace Moonlight.ApiServer.Database.Migrations b.HasIndex("Key", "Score"); - b.ToTable("HangfireSet"); + b.ToTable("HangfireSet", "core"); }); modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireState", b => @@ -272,7 +273,7 @@ namespace Moonlight.ApiServer.Database.Migrations b.HasIndex("JobId"); - b.ToTable("HangfireState"); + b.ToTable("HangfireState", "core"); }); modelBuilder.Entity("Moonlight.ApiServer.Database.Entities.ApiKey", b => @@ -299,7 +300,7 @@ namespace Moonlight.ApiServer.Database.Migrations b.HasKey("Id"); - b.ToTable("Core_ApiKeys", (string)null); + b.ToTable("ApiKeys", "core"); }); modelBuilder.Entity("Moonlight.ApiServer.Database.Entities.Theme", b => @@ -333,7 +334,7 @@ namespace Moonlight.ApiServer.Database.Migrations b.HasKey("Id"); - b.ToTable("Core_Themes", (string)null); + b.ToTable("Themes", "core"); }); modelBuilder.Entity("Moonlight.ApiServer.Database.Entities.User", b => @@ -365,7 +366,7 @@ namespace Moonlight.ApiServer.Database.Migrations b.HasKey("Id"); - b.ToTable("Core_Users", (string)null); + b.ToTable("Users", "core"); }); modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireJob", b => @@ -512,11 +513,11 @@ namespace Moonlight.ApiServer.Database.Migrations .IsRequired() .HasColumnType("text"); - b1.Property("Depth") - .HasColumnType("real"); + b1.Property("Depth") + .HasColumnType("integer"); - b1.Property("Noise") - .HasColumnType("real"); + b1.Property("Noise") + .HasColumnType("integer"); b1.Property("RadiusBox") .HasColumnType("real"); @@ -535,7 +536,7 @@ namespace Moonlight.ApiServer.Database.Migrations b1.HasKey("ThemeId"); - b1.ToTable("Core_Themes"); + b1.ToTable("Themes", "core"); b1.ToJson("Content"); diff --git a/Moonlight.ApiServer/Http/Controllers/Admin/ApiKeys/ApiKeysController.cs b/Moonlight.ApiServer/Http/Controllers/Admin/ApiKeys/ApiKeysController.cs index 8cedfb93..4cf928e7 100644 --- a/Moonlight.ApiServer/Http/Controllers/Admin/ApiKeys/ApiKeysController.cs +++ b/Moonlight.ApiServer/Http/Controllers/Admin/ApiKeys/ApiKeysController.cs @@ -1,12 +1,10 @@ -using System.ComponentModel.DataAnnotations; -using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; -using MoonCore.Exceptions; using MoonCore.Extended.Abstractions; -using MoonCore.Extended.Models; using MoonCore.Models; using Moonlight.ApiServer.Database.Entities; +using Moonlight.ApiServer.Mappers; using Moonlight.ApiServer.Services; using Moonlight.Shared.Http.Requests.Admin.ApiKeys; using Moonlight.Shared.Http.Responses.Admin.ApiKeys; @@ -28,69 +26,82 @@ public class ApiKeysController : Controller [HttpGet] [Authorize(Policy = "permissions:admin.apikeys.get")] - public async Task> Get([FromQuery] PagedOptions options) + public async Task>> GetAsync( + [FromQuery] int startIndex, + [FromQuery] int count, + [FromQuery] string? orderBy, + [FromQuery] string? filter, + [FromQuery] string orderByDir = "asc" + ) { - var count = await ApiKeyRepository.Get().CountAsync(); + if (count > 100) + return Problem("You cannot fetch more items than 100 at a time", statusCode: 400); + + IQueryable query = ApiKeyRepository.Get(); - var apiKeys = await ApiKeyRepository - .Get() - .OrderBy(x => x.Id) - .Skip(options.Page * options.PageSize) - .Take(options.PageSize) + query = orderBy switch + { + nameof(ApiKey.Id) => orderByDir == "desc" + ? query.OrderByDescending(x => x.Id) + : query.OrderBy(x => x.Id), + + nameof(ApiKey.ExpiresAt) => orderByDir == "desc" + ? query.OrderByDescending(x => x.ExpiresAt) + : query.OrderBy(x => x.ExpiresAt), + + nameof(ApiKey.CreatedAt) => orderByDir == "desc" + ? query.OrderByDescending(x => x.CreatedAt) + : query.OrderBy(x => x.CreatedAt), + + _ => query.OrderBy(x => x.Id) + }; + + if (!string.IsNullOrEmpty(filter)) + { + query = query.Where(x => + EF.Functions.ILike(x.Description, $"%{filter}%") + ); + } + + var totalCount = await query.CountAsync(); + + var items = await query + .Skip(startIndex) + .Take(count) + .AsNoTracking() + .ProjectToResponse() .ToArrayAsync(); - var mappedApiKey = apiKeys - .Select(x => new ApiKeyResponse() - { - Id = x.Id, - Permissions = x.Permissions, - Description = x.Description, - ExpiresAt = x.ExpiresAt - }) - .ToArray(); - - return new PagedData() + return new CountedData() { - CurrentPage = options.Page, - Items = mappedApiKey, - PageSize = options.PageSize, - TotalItems = count, - TotalPages = count == 0 ? 0 : (count - 1) / options.PageSize + Items = items, + TotalCount = totalCount }; } - [HttpGet("{id}")] + [HttpGet("{id:int}")] [Authorize(Policy = "permissions:admin.apikeys.get")] - public async Task GetSingle(int id) + public async Task> GetSingleAsync(int id) { var apiKey = await ApiKeyRepository .Get() + .AsNoTracking() + .ProjectToResponse() .FirstOrDefaultAsync(x => x.Id == id); if (apiKey == null) - throw new HttpApiException("No api key with that id found", 404); + return Problem("No api key with that id found", statusCode: 404); - return new ApiKeyResponse() - { - Id = apiKey.Id, - Permissions = apiKey.Permissions, - Description = apiKey.Description, - ExpiresAt = apiKey.ExpiresAt - }; + return apiKey; } [HttpPost] [Authorize(Policy = "permissions:admin.apikeys.create")] - public async Task Create([FromBody] CreateApiKeyRequest request) + public async Task CreateAsync([FromBody] CreateApiKeyRequest request) { - var apiKey = new ApiKey() - { - Description = request.Description, - Permissions = request.Permissions, - ExpiresAt = request.ExpiresAt - }; - - var finalApiKey = await ApiKeyRepository.Add(apiKey); + var apiKey = ApiKeyMapper.ToApiKey(request); + + var finalApiKey = await ApiKeyRepository.AddAsync(apiKey); var response = new CreateApiKeyResponse { @@ -104,41 +115,36 @@ public class ApiKeysController : Controller return response; } - [HttpPatch("{id}")] + [HttpPatch("{id:int}")] [Authorize(Policy = "permissions:admin.apikeys.update")] - public async Task Update([FromRoute] int id, [FromBody] UpdateApiKeyRequest request) + public async Task> UpdateAsync([FromRoute] int id, [FromBody] UpdateApiKeyRequest request) { var apiKey = await ApiKeyRepository .Get() .FirstOrDefaultAsync(x => x.Id == id); if (apiKey == null) - throw new HttpApiException("No api key with that id found", 404); + return Problem("No api key with that id found", statusCode: 404); - apiKey.Description = request.Description; + ApiKeyMapper.Merge(apiKey, request); - await ApiKeyRepository.Update(apiKey); + await ApiKeyRepository.UpdateAsync(apiKey); - return new ApiKeyResponse() - { - Id = apiKey.Id, - Description = apiKey.Description, - Permissions = apiKey.Permissions, - ExpiresAt = apiKey.ExpiresAt - }; + return ApiKeyMapper.ToResponse(apiKey); } - [HttpDelete("{id}")] + [HttpDelete("{id:int}")] [Authorize(Policy = "permissions:admin.apikeys.delete")] - public async Task Delete([FromRoute] int id) + public async Task DeleteAsync([FromRoute] int id) { var apiKey = await ApiKeyRepository .Get() .FirstOrDefaultAsync(x => x.Id == id); if (apiKey == null) - throw new HttpApiException("No api key with that id found", 404); + return Problem("No api key with that id found", statusCode: 404); - await ApiKeyRepository.Remove(apiKey); + await ApiKeyRepository.RemoveAsync(apiKey); + return NoContent(); } } \ No newline at end of file diff --git a/Moonlight.ApiServer/Http/Controllers/Admin/Sys/AdvancedController.cs b/Moonlight.ApiServer/Http/Controllers/Admin/Sys/AdvancedController.cs index 3873564f..68204362 100644 --- a/Moonlight.ApiServer/Http/Controllers/Admin/Sys/AdvancedController.cs +++ b/Moonlight.ApiServer/Http/Controllers/Admin/Sys/AdvancedController.cs @@ -19,9 +19,9 @@ public class AdvancedController : Controller [HttpGet("frontend")] [Authorize(Policy = "permissions:admin.system.advanced.frontend")] - public async Task Frontend() + public async Task FrontendAsync() { - var stream = await FrontendService.GenerateZip(); + var stream = await FrontendService.GenerateZipAsync(); await Results.File(stream, fileDownloadName: "frontend.zip").ExecuteAsync(HttpContext); } } \ No newline at end of file diff --git a/Moonlight.ApiServer/Http/Controllers/Admin/Sys/Customisation/ThemesController.cs b/Moonlight.ApiServer/Http/Controllers/Admin/Sys/Customisation/ThemesController.cs index 73867116..0a16e7c3 100644 --- a/Moonlight.ApiServer/Http/Controllers/Admin/Sys/Customisation/ThemesController.cs +++ b/Moonlight.ApiServer/Http/Controllers/Admin/Sys/Customisation/ThemesController.cs @@ -26,65 +26,96 @@ public class ThemesController : Controller [HttpGet] [Authorize(Policy = "permissions:admin.system.customisation.themes.read")] - public async Task> Get([FromQuery] PagedOptions options) + public async Task>> GetAsync( + [FromQuery] int startIndex, + [FromQuery] int count, + [FromQuery] string? orderBy, + [FromQuery] string? filter, + [FromQuery] string orderByDir = "asc" + ) { - var count = await ThemeRepository.Get().CountAsync(); + if (count > 100) + return Problem("You cannot fetch more items than 100 at a time", statusCode: 400); - var items = await ThemeRepository - .Get() - .Skip(options.Page * options.PageSize) - .Take(options.PageSize) - .ToArrayAsync(); - - var mappedItems = items - .Select(ThemeMapper.ToResponse) - .ToArray(); - - return new PagedData() + IQueryable query = ThemeRepository.Get(); + + query = orderBy switch { - CurrentPage = options.Page, - Items = mappedItems, - PageSize = options.PageSize, - TotalItems = count, - TotalPages = count == 0 ? 0 : (count - 1) / options.PageSize + nameof(Theme.Id) => orderByDir == "desc" + ? query.OrderByDescending(x => x.Id) + : query.OrderBy(x => x.Id), + + nameof(Theme.Name) => orderByDir == "desc" + ? query.OrderByDescending(x => x.Name) + : query.OrderBy(x => x.Name), + + nameof(Theme.Version) => orderByDir == "desc" + ? query.OrderByDescending(x => x.Version) + : query.OrderBy(x => x.Version), + + _ => query.OrderBy(x => x.Id) + }; + + if (!string.IsNullOrEmpty(filter)) + { + query = query.Where(x => + EF.Functions.ILike(x.Name, $"%{filter}%") + ); + } + + var totalCount = await query.CountAsync(); + + var items = await query + .Skip(startIndex) + .Take(count) + .AsNoTracking() + .ProjectToResponse() + .ToArrayAsync(); + + return new CountedData() + { + Items = items, + TotalCount = totalCount }; } [HttpGet("{id:int}")] [Authorize(Policy = "permissions:admin.system.customisation.themes.read")] - public async Task GetSingle([FromRoute] int id) + public async Task> GetSingleAsync([FromRoute] int id) { var theme = await ThemeRepository .Get() + .AsNoTracking() + .ProjectToResponse() .FirstOrDefaultAsync(t => t.Id == id); if (theme == null) - throw new HttpApiException("Theme with this id not found", 404); - - return ThemeMapper.ToResponse(theme); + return Problem("Theme with this id not found", statusCode: 404); + + return theme; } [HttpPost] [Authorize(Policy = "permissions:admin.system.customisation.themes.write")] - public async Task Create([FromBody] CreateThemeRequest request) + public async Task> CreateAsync([FromBody] CreateThemeRequest request) { var theme = ThemeMapper.ToTheme(request); - - var finalTheme = await ThemeRepository.Add(theme); - + + var finalTheme = await ThemeRepository.AddAsync(theme); + return ThemeMapper.ToResponse(finalTheme); } [HttpPatch("{id:int}")] [Authorize(Policy = "permissions:admin.system.customisation.themes.write")] - public async Task Update([FromRoute] int id, [FromBody] UpdateThemeRequest request) + public async Task> UpdateAsync([FromRoute] int id, [FromBody] UpdateThemeRequest request) { var theme = await ThemeRepository .Get() .FirstOrDefaultAsync(t => t.Id == id); - + if (theme == null) - throw new HttpApiException("Theme with this id not found", 404); + return Problem("Theme with this id not found", statusCode: 404); // Disable all other enabled themes if we are enabling the current theme. // This ensures only one theme is enabled at the time @@ -98,29 +129,28 @@ public class ThemesController : Controller foreach (var otherTheme in otherThemes) otherTheme.IsEnabled = false; - await ThemeRepository.RunTransaction(set => - { - set.UpdateRange(otherThemes); - }); + await ThemeRepository.RunTransactionAsync(set => { set.UpdateRange(otherThemes); }); } - + ThemeMapper.Merge(theme, request); - await ThemeRepository.Update(theme); + await ThemeRepository.UpdateAsync(theme); + return ThemeMapper.ToResponse(theme); } [HttpDelete("{id:int}")] [Authorize(Policy = "permissions:admin.system.customisation.themes.write")] - public async Task Delete([FromRoute] int id) + public async Task DeleteAsync([FromRoute] int id) { var theme = await ThemeRepository .Get() .FirstOrDefaultAsync(x => x.Id == id); - + if (theme == null) - throw new HttpApiException("Theme with this id not found", 404); - - await ThemeRepository.Remove(theme); + return Problem("Theme with this id not found", statusCode: 404); + + await ThemeRepository.RemoveAsync(theme); + return NoContent(); } } \ No newline at end of file diff --git a/Moonlight.ApiServer/Http/Controllers/Admin/Sys/DiagnoseController.cs b/Moonlight.ApiServer/Http/Controllers/Admin/Sys/DiagnoseController.cs index 8f79f4b4..cc32ded1 100644 --- a/Moonlight.ApiServer/Http/Controllers/Admin/Sys/DiagnoseController.cs +++ b/Moonlight.ApiServer/Http/Controllers/Admin/Sys/DiagnoseController.cs @@ -21,7 +21,7 @@ public class DiagnoseController : Controller } [HttpPost] - public async Task Diagnose([FromBody] GenerateDiagnoseRequest request) + public async Task DiagnoseAsync([FromBody] GenerateDiagnoseRequest request) { var stream = await DiagnoseService.GenerateDiagnoseAsync(request.Providers); @@ -29,7 +29,7 @@ public class DiagnoseController : Controller } [HttpGet("providers")] - public async Task> GetProviders() + public async Task> GetProvidersAsync() { return await DiagnoseService.GetProvidersAsync(); } diff --git a/Moonlight.ApiServer/Http/Controllers/Admin/Sys/Files/CombineController.cs b/Moonlight.ApiServer/Http/Controllers/Admin/Sys/Files/CombineController.cs index 5d4e006e..1b5e3410 100644 --- a/Moonlight.ApiServer/Http/Controllers/Admin/Sys/Files/CombineController.cs +++ b/Moonlight.ApiServer/Http/Controllers/Admin/Sys/Files/CombineController.cs @@ -23,7 +23,7 @@ public class CombineController : Controller } [HttpPost("combine")] - public async Task Combine([FromBody] CombineRequest request) + public async Task CombineAsync([FromBody] CombineRequest request) { // Validate file lenght if (request.Files.Length < 2) diff --git a/Moonlight.ApiServer/Http/Controllers/Admin/Sys/Files/CompressController.cs b/Moonlight.ApiServer/Http/Controllers/Admin/Sys/Files/CompressController.cs index 26584ee1..dedea4cf 100644 --- a/Moonlight.ApiServer/Http/Controllers/Admin/Sys/Files/CompressController.cs +++ b/Moonlight.ApiServer/Http/Controllers/Admin/Sys/Files/CompressController.cs @@ -19,7 +19,7 @@ public class CompressController : Controller private const string BaseDirectory = "storage"; [HttpPost("compress")] - public async Task Compress([FromBody] CompressRequest request) + public async Task CompressAsync([FromBody] CompressRequest request) { // Validate item length if (request.Items.Length == 0) @@ -48,11 +48,11 @@ public class CompressController : Controller switch (request.Format) { case "tar.gz": - await CompressTarGz(destinationPath, itemsPaths, rootPath); + await CompressTarGzAsync(destinationPath, itemsPaths, rootPath); break; case "zip": - await CompressZip(destinationPath, itemsPaths, rootPath); + await CompressZipAsync(destinationPath, itemsPaths, rootPath); break; default: @@ -66,14 +66,14 @@ public class CompressController : Controller #region Tar Gz - private async Task CompressTarGz(string destination, IEnumerable items, string root) + private async Task CompressTarGzAsync(string destination, IEnumerable items, string root) { await using var outStream = System.IO.File.Create(destination); await using var gzoStream = new GZipOutputStream(outStream); await using var tarStream = new TarOutputStream(gzoStream, Encoding.UTF8); foreach (var item in items) - await CompressItemToTarGz(tarStream, item, root); + await CompressItemToTarGzAsync(tarStream, item, root); await tarStream.FlushAsync(); await gzoStream.FlushAsync(); @@ -84,7 +84,7 @@ public class CompressController : Controller outStream.Close(); } - private async Task CompressItemToTarGz(TarOutputStream tarOutputStream, string item, string root) + private async Task CompressItemToTarGzAsync(TarOutputStream tarOutputStream, string item, string root) { if (System.IO.File.Exists(item)) { @@ -117,7 +117,7 @@ public class CompressController : Controller if (Directory.Exists(item)) { foreach (var fsEntry in Directory.EnumerateFileSystemEntries(item)) - await CompressItemToTarGz(tarOutputStream, fsEntry, root); + await CompressItemToTarGzAsync(tarOutputStream, fsEntry, root); } } @@ -125,13 +125,13 @@ public class CompressController : Controller #region ZIP - private async Task CompressZip(string destination, IEnumerable items, string root) + private async Task CompressZipAsync(string destination, IEnumerable items, string root) { await using var outStream = System.IO.File.Create(destination); await using var zipOutputStream = new ZipOutputStream(outStream); foreach (var item in items) - await AddItemToZip(zipOutputStream, item, root); + await AddItemToZipAsync(zipOutputStream, item, root); await zipOutputStream.FlushAsync(); await outStream.FlushAsync(); @@ -140,7 +140,7 @@ public class CompressController : Controller outStream.Close(); } - private async Task AddItemToZip(ZipOutputStream outputStream, string item, string root) + private async Task AddItemToZipAsync(ZipOutputStream outputStream, string item, string root) { if (System.IO.File.Exists(item)) { @@ -175,7 +175,7 @@ public class CompressController : Controller if (Directory.Exists(item)) { foreach (var subItem in Directory.EnumerateFileSystemEntries(item)) - await AddItemToZip(outputStream, subItem, root); + await AddItemToZipAsync(outputStream, subItem, root); } } diff --git a/Moonlight.ApiServer/Http/Controllers/Admin/Sys/Files/DecompressController.cs b/Moonlight.ApiServer/Http/Controllers/Admin/Sys/Files/DecompressController.cs index 72db443e..8a44d59c 100644 --- a/Moonlight.ApiServer/Http/Controllers/Admin/Sys/Files/DecompressController.cs +++ b/Moonlight.ApiServer/Http/Controllers/Admin/Sys/Files/DecompressController.cs @@ -17,7 +17,7 @@ public class DecompressController : Controller private const string BaseDirectory = "storage"; [HttpPost("decompress")] - public async Task Decompress([FromBody] DecompressRequest request) + public async Task DecompressAsync([FromBody] DecompressRequest request) { var path = Path.Combine(BaseDirectory, FilePathHelper.SanitizePath(request.Path)); var destination = Path.Combine(BaseDirectory, FilePathHelper.SanitizePath(request.Destination)); @@ -25,18 +25,18 @@ public class DecompressController : Controller switch (request.Format) { case "tar.gz": - await DecompressTarGz(path, destination); + await DecompressTarGzAsync(path, destination); break; case "zip": - await DecompressZip(path, destination); + await DecompressZipAsync(path, destination); break; } } #region Tar Gz - private async Task DecompressTarGz(string path, string destination) + private async Task DecompressTarGzAsync(string path, string destination) { await using var fs = System.IO.File.Open(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); await using var gzipInputStream = new GZipInputStream(fs); @@ -74,7 +74,7 @@ public class DecompressController : Controller #region Zip - private async Task DecompressZip(string path, string destination) + private async Task DecompressZipAsync(string path, string destination) { await using var fs = System.IO.File.Open(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); await using var zipInputStream = new ZipInputStream(fs); diff --git a/Moonlight.ApiServer/Http/Controllers/Admin/Sys/Files/DownloadUrlController.cs b/Moonlight.ApiServer/Http/Controllers/Admin/Sys/Files/DownloadUrlController.cs index baa1498c..d813c1f0 100644 --- a/Moonlight.ApiServer/Http/Controllers/Admin/Sys/Files/DownloadUrlController.cs +++ b/Moonlight.ApiServer/Http/Controllers/Admin/Sys/Files/DownloadUrlController.cs @@ -25,7 +25,7 @@ public class DownloadUrlController : Controller } [HttpGet] - public async Task Get([FromQuery] string path) + public async Task GetAsync([FromQuery] string path) { var physicalPath = Path.Combine(BaseDirectory, FilePathHelper.SanitizePath(path)); var name = Path.GetFileName(physicalPath); @@ -55,7 +55,7 @@ public class DownloadUrlController : Controller await using var zipStream = new ZipOutputStream(Response.Body); zipStream.IsStreamOwner = false; - await StreamFolderAsZip(zipStream, physicalPath, baseDirectory, HttpContext.RequestAborted); + await StreamFolderAsZipAsync(zipStream, physicalPath, baseDirectory, HttpContext.RequestAborted); } catch (ZipException) { @@ -68,7 +68,7 @@ public class DownloadUrlController : Controller } } - private async Task StreamFolderAsZip( + private async Task StreamFolderAsZipAsync( ZipOutputStream zipStream, string path, string rootPath, CancellationToken cancellationToken @@ -102,7 +102,7 @@ public class DownloadUrlController : Controller if (HttpContext.RequestAborted.IsCancellationRequested) return; - await StreamFolderAsZip(zipStream, directory, rootPath, cancellationToken); + await StreamFolderAsZipAsync(zipStream, directory, rootPath, cancellationToken); } } @@ -110,7 +110,7 @@ public class DownloadUrlController : Controller // Yes I know we can just create that url on the client as the exist validation is done on both endpoints, // but we leave it here for future modifications. E.g. using a distributed file provider or smth like that [HttpPost] - public Task Post([FromQuery] string path) + public Task PostAsync([FromQuery] string path) { var safePath = FilePathHelper.SanitizePath(path); var physicalPath = Path.Combine(BaseDirectory, safePath); diff --git a/Moonlight.ApiServer/Http/Controllers/Admin/Sys/Files/FilesController.cs b/Moonlight.ApiServer/Http/Controllers/Admin/Sys/Files/FilesController.cs index c399ff7f..ab14bb3d 100644 --- a/Moonlight.ApiServer/Http/Controllers/Admin/Sys/Files/FilesController.cs +++ b/Moonlight.ApiServer/Http/Controllers/Admin/Sys/Files/FilesController.cs @@ -15,7 +15,7 @@ public class FilesController : Controller private const string BaseDirectory = "storage"; [HttpPost("touch")] - public async Task CreateFile([FromQuery] string path) + public async Task CreateFileAsync([FromQuery] string path) { var safePath = FilePathHelper.SanitizePath(path); var physicalPath = Path.Combine(BaseDirectory, safePath); @@ -31,7 +31,7 @@ public class FilesController : Controller } [HttpPost("mkdir")] - public Task CreateFolder([FromQuery] string path) + public Task CreateFolderAsync([FromQuery] string path) { var safePath = FilePathHelper.SanitizePath(path); var physicalPath = Path.Combine(BaseDirectory, safePath); @@ -47,7 +47,7 @@ public class FilesController : Controller } [HttpGet("list")] - public Task List([FromQuery] string path) + public Task ListAsync([FromQuery] string path) { var safePath = FilePathHelper.SanitizePath(path); var physicalPath = Path.Combine(BaseDirectory, safePath); @@ -92,7 +92,7 @@ public class FilesController : Controller } [HttpPost("move")] - public Task Move([FromQuery] string oldPath, [FromQuery] string newPath) + public Task MoveAsync([FromQuery] string oldPath, [FromQuery] string newPath) { var oldSafePath = FilePathHelper.SanitizePath(oldPath); var newSafePath = FilePathHelper.SanitizePath(newPath); @@ -123,7 +123,7 @@ public class FilesController : Controller } [HttpDelete("delete")] - public Task Delete([FromQuery] string path) + public Task DeleteAsync([FromQuery] string path) { var safePath = FilePathHelper.SanitizePath(path); var physicalDirPath = Path.Combine(BaseDirectory, safePath); @@ -141,7 +141,7 @@ public class FilesController : Controller } [HttpPost("upload")] - public async Task Upload([FromQuery] string path) + public async Task UploadAsync([FromQuery] string path) { if (Request.Form.Files.Count != 1) return Results.Problem("Only one file is allowed in the request", statusCode: 400); @@ -179,7 +179,7 @@ public class FilesController : Controller } [HttpGet("download")] - public async Task Download([FromQuery] string path) + public async Task DownloadAsync([FromQuery] string path) { var safePath = FilePathHelper.SanitizePath(path); var physicalPath = Path.Combine(BaseDirectory, safePath); diff --git a/Moonlight.ApiServer/Http/Controllers/Admin/Sys/HangfireController.cs b/Moonlight.ApiServer/Http/Controllers/Admin/Sys/HangfireController.cs index 319042a9..1c426b69 100644 --- a/Moonlight.ApiServer/Http/Controllers/Admin/Sys/HangfireController.cs +++ b/Moonlight.ApiServer/Http/Controllers/Admin/Sys/HangfireController.cs @@ -18,7 +18,7 @@ public class HangfireController : Controller } [HttpGet("stats")] - public Task GetStats() + public Task GetStatsAsync() { var statistics = JobStorage.GetMonitoringApi().GetStatistics(); diff --git a/Moonlight.ApiServer/Http/Controllers/Admin/Sys/SystemController.cs b/Moonlight.ApiServer/Http/Controllers/Admin/Sys/SystemController.cs index a8883a4b..9778a4ef 100644 --- a/Moonlight.ApiServer/Http/Controllers/Admin/Sys/SystemController.cs +++ b/Moonlight.ApiServer/Http/Controllers/Admin/Sys/SystemController.cs @@ -18,21 +18,21 @@ public class SystemController : Controller [HttpGet] [Authorize(Policy = "permissions:admin.system.overview")] - public async Task GetOverview() + public async Task GetOverviewAsync() { return new() { - Uptime = await ApplicationService.GetUptime(), - CpuUsage = await ApplicationService.GetCpuUsage(), - MemoryUsage = await ApplicationService.GetMemoryUsage(), - OperatingSystem = await ApplicationService.GetOsName() + Uptime = await ApplicationService.GetUptimeAsync(), + CpuUsage = await ApplicationService.GetCpuUsageAsync(), + MemoryUsage = await ApplicationService.GetMemoryUsageAsync(), + OperatingSystem = await ApplicationService.GetOsNameAsync() }; } [HttpPost("shutdown")] [Authorize(Policy = "permissions:admin.system.shutdown")] - public async Task Shutdown() + public async Task ShutdownAsync() { - await ApplicationService.Shutdown(); + await ApplicationService.ShutdownAsync(); } } \ No newline at end of file diff --git a/Moonlight.ApiServer/Http/Controllers/Admin/Users/UsersController.cs b/Moonlight.ApiServer/Http/Controllers/Admin/Users/UsersController.cs index 247617bc..411c281d 100644 --- a/Moonlight.ApiServer/Http/Controllers/Admin/Users/UsersController.cs +++ b/Moonlight.ApiServer/Http/Controllers/Admin/Users/UsersController.cs @@ -1,4 +1,3 @@ -using System.ComponentModel.DataAnnotations; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; @@ -6,10 +5,10 @@ using Microsoft.Extensions.DependencyInjection; using MoonCore.Exceptions; using MoonCore.Extended.Abstractions; using MoonCore.Extended.Helpers; -using MoonCore.Extended.Models; using MoonCore.Models; using Moonlight.ApiServer.Database.Entities; using Moonlight.ApiServer.Services; +using Moonlight.ApiServer.Mappers; using Moonlight.Shared.Http.Requests.Admin.Users; using Moonlight.Shared.Http.Responses.Admin.Users; @@ -28,60 +27,78 @@ public class UsersController : Controller [HttpGet] [Authorize(Policy = "permissions:admin.users.get")] - public async Task> Get([FromQuery] PagedOptions options) + public async Task>> GetAsync( + [FromQuery] int startIndex, + [FromQuery] int count, + [FromQuery] string? orderBy, + [FromQuery] string? filter, + [FromQuery] string orderByDir = "asc" + ) { - var count = await UserRepository.Get().CountAsync(); + if (count > 100) + return Problem("You cannot fetch more items than 100 at a time", statusCode: 400); + + IQueryable query = UserRepository.Get(); - var users = await UserRepository - .Get() - .OrderBy(x => x.Id) - .Skip(options.Page * options.PageSize) - .Take(options.PageSize) + query = orderBy switch + { + nameof(Database.Entities.User.Id) => orderByDir == "desc" + ? query.OrderByDescending(x => x.Id) + : query.OrderBy(x => x.Id), + + nameof(Database.Entities.User.Username) => orderByDir == "desc" + ? query.OrderByDescending(x => x.Username) + : query.OrderBy(x => x.Username), + + nameof(Database.Entities.User.Email) => orderByDir == "desc" + ? query.OrderByDescending(x => x.Email) + : query.OrderBy(x => x.Email), + + _ => query.OrderBy(x => x.Id) + }; + + if (!string.IsNullOrEmpty(filter)) + { + query = query.Where(x => + EF.Functions.ILike(x.Username, $"%{filter}%") || + EF.Functions.ILike(x.Email, $"%{filter}%") + ); + } + + var totalCount = await query.CountAsync(); + + var items = await query + .Skip(startIndex) + .Take(count) + .AsNoTracking() + .ProjectToResponse() .ToArrayAsync(); - var mappedUsers = users - .Select(x => new UserResponse() - { - Id = x.Id, - Email = x.Email, - Username = x.Username, - Permissions = x.Permissions - }) - .ToArray(); - - return new PagedData() + return new CountedData() { - CurrentPage = options.Page, - Items = mappedUsers, - PageSize = options.PageSize, - TotalItems = count, - TotalPages = count == 0 ? 0 : (count - 1) / options.PageSize + Items = items, + TotalCount = totalCount }; } [HttpGet("{id}")] [Authorize(Policy = "permissions:admin.users.get")] - public async Task GetSingle(int id) + public async Task> GetSingleAsync(int id) { var user = await UserRepository .Get() + .ProjectToResponse() .FirstOrDefaultAsync(x => x.Id == id); if (user == null) - throw new HttpApiException("No user with that id found", 404); + return Problem("No user with that id found", statusCode: 404); - return new UserResponse() - { - Id = user.Id, - Email = user.Email, - Username = user.Username, - Permissions = user.Permissions - }; + return user; } [HttpPost] [Authorize(Policy = "permissions:admin.users.create")] - public async Task Create([FromBody] CreateUserRequest request) + public async Task> CreateAsync([FromBody] CreateUserRequest request) { // Reformat values request.Username = request.Username.ToLower().Trim(); @@ -89,10 +106,10 @@ public class UsersController : Controller // Check for users with the same values if (UserRepository.Get().Any(x => x.Username == request.Username)) - throw new HttpApiException("A user with that username already exists", 400); + return Problem("A user with that username already exists", statusCode: 400); if (UserRepository.Get().Any(x => x.Email == request.Email)) - throw new HttpApiException("A user with that email address already exists", 400); + return Problem("A user with that email address already exists", statusCode: 400); var hashedPassword = HashHelper.Hash(request.Password); @@ -104,27 +121,21 @@ public class UsersController : Controller Permissions = request.Permissions }; - var finalUser = await UserRepository.Add(user); + var finalUser = await UserRepository.AddAsync(user); - return new UserResponse() - { - Id = finalUser.Id, - Email = finalUser.Email, - Username = finalUser.Username, - Permissions = finalUser.Permissions - }; + return UserMapper.ToResponse(finalUser); } [HttpPatch("{id}")] [Authorize(Policy = "permissions:admin.users.update")] - public async Task Update([FromRoute] int id, [FromBody] UpdateUserRequest request) + public async Task> UpdateAsync([FromRoute] int id, [FromBody] UpdateUserRequest request) { var user = await UserRepository .Get() .FirstOrDefaultAsync(x => x.Id == id); if (user == null) - throw new HttpApiException("No user with that id found", 404); + return Problem("No user with that id found", statusCode: 404); // Reformat values request.Username = request.Username.ToLower().Trim(); @@ -132,10 +143,10 @@ public class UsersController : Controller // Check for users with the same values if (UserRepository.Get().Any(x => x.Username == request.Username && x.Id != user.Id)) - throw new HttpApiException("A user with that username already exists", 400); + return Problem("Another user with that username already exists", statusCode: 400); if (UserRepository.Get().Any(x => x.Email == request.Email && x.Id != user.Id)) - throw new HttpApiException("A user with that email address already exists", 400); + return Problem("Another user with that email address already exists", statusCode: 400); // Perform hashing the password if required if (!string.IsNullOrEmpty(request.Password)) @@ -153,38 +164,33 @@ public class UsersController : Controller user.Email = request.Email; user.Username = request.Username; - await UserRepository.Update(user); + await UserRepository.UpdateAsync(user); - return new UserResponse() - { - Id = user.Id, - Email = user.Email, - Username = user.Username, - Permissions = user.Permissions - }; + return UserMapper.ToResponse(user); } [HttpDelete("{id}")] [Authorize(Policy = "permissions:admin.users.delete")] - public async Task Delete([FromRoute] int id, [FromQuery] bool force = false) + public async Task DeleteAsync([FromRoute] int id, [FromQuery] bool force = false) { var user = await UserRepository .Get() .FirstOrDefaultAsync(x => x.Id == id); if (user == null) - throw new HttpApiException("No user with that id found", 404); + return Problem("No user with that id found", statusCode: 404); var deletionService = HttpContext.RequestServices.GetRequiredService(); if (!force) { - var validationResult = await deletionService.Validate(user); + var validationResult = await deletionService.ValidateAsync(user); if (!validationResult.IsAllowed) - throw new HttpApiException($"Unable to delete user", 400, validationResult.Reason); + return Problem("Unable to delete user", statusCode: 400, title: validationResult.Reason); } - await deletionService.Delete(user, force); + await deletionService.DeleteAsync(user, force); + return NoContent(); } } \ No newline at end of file diff --git a/Moonlight.ApiServer/Http/Controllers/Auth/AuthController.cs b/Moonlight.ApiServer/Http/Controllers/Auth/AuthController.cs index 2af686d2..6be998cd 100644 --- a/Moonlight.ApiServer/Http/Controllers/Auth/AuthController.cs +++ b/Moonlight.ApiServer/Http/Controllers/Auth/AuthController.cs @@ -30,7 +30,7 @@ public class AuthController : Controller } [HttpGet] - public async Task GetSchemes() + public async Task GetSchemesAsync() { var schemes = await SchemeProvider.GetAllSchemesAsync(); @@ -47,7 +47,7 @@ public class AuthController : Controller } [HttpGet("{identifier:alpha}")] - public async Task StartScheme([FromRoute] string identifier) + public async Task StartSchemeAsync([FromRoute] string identifier) { // Validate identifier against our enable list var allowedSchemes = Configuration.Authentication.EnabledSchemes; @@ -91,7 +91,7 @@ public class AuthController : Controller [Authorize] [HttpGet("check")] - public async Task Check() + public async Task CheckAsync() { var username = User.FindFirstValue(ClaimTypes.Name)!; var id = User.FindFirstValue(ClaimTypes.NameIdentifier)!; @@ -113,7 +113,7 @@ public class AuthController : Controller foreach (var extension in Extensions) { claims.AddRange( - await extension.GetFrontendClaims(User) + await extension.GetFrontendClaimsAsync(User) ); } @@ -121,7 +121,7 @@ public class AuthController : Controller } [HttpGet("logout")] - public async Task Logout() + public async Task LogoutAsync() { await HttpContext.SignOutAsync(); await Results.Redirect("/").ExecuteAsync(HttpContext); diff --git a/Moonlight.ApiServer/Http/Controllers/Frontend/FrontendController.cs b/Moonlight.ApiServer/Http/Controllers/Frontend/FrontendController.cs index 9271af67..7d9592a9 100644 --- a/Moonlight.ApiServer/Http/Controllers/Frontend/FrontendController.cs +++ b/Moonlight.ApiServer/Http/Controllers/Frontend/FrontendController.cs @@ -18,13 +18,13 @@ public class FrontendController : Controller } [HttpGet("frontend.json")] - public async Task GetConfiguration() - => await FrontendService.GetConfiguration(); + public async Task GetConfigurationAsync() + => await FrontendService.GetConfigurationAsync(); [HttpGet] - public async Task Index() + public async Task IndexAsync() { - var content = await FrontendService.GenerateIndexHtml(); + var content = await FrontendService.GenerateIndexHtmlAsync(); return Results.Text(content, "text/html", Encoding.UTF8); } diff --git a/Moonlight.ApiServer/Http/Controllers/LocalAuth/LocalAuthController.cs b/Moonlight.ApiServer/Http/Controllers/LocalAuth/LocalAuthController.cs index 68b90a75..d8715e35 100644 --- a/Moonlight.ApiServer/Http/Controllers/LocalAuth/LocalAuthController.cs +++ b/Moonlight.ApiServer/Http/Controllers/LocalAuth/LocalAuthController.cs @@ -45,29 +45,29 @@ public class LocalAuthController : Controller [HttpGet] [HttpGet("login")] - public async Task Login() + public async Task LoginAsync() { - var html = await ComponentHelper.RenderComponent(ServiceProvider); + var html = await ComponentHelper.RenderToHtmlAsync(ServiceProvider); - return Results.Content(html, "text/html"); + return Content(html, "text/html"); } [HttpGet("register")] - public async Task Register() + public async Task RegisterAsync() { - var html = await ComponentHelper.RenderComponent(ServiceProvider); + var html = await ComponentHelper.RenderToHtmlAsync(ServiceProvider); - return Results.Content(html, "text/html"); + return Content(html, "text/html"); } [HttpPost] [HttpPost("login")] - public async Task Login([FromForm] string email, [FromForm] string password) + public async Task LoginAsync([FromForm] string email, [FromForm] string password) { try { // Perform login - var user = await InternalLogin(email, password); + var user = await InternalLoginAsync(email, password); // Login user var options = Options.Get(LocalAuthConstants.AuthenticationScheme); @@ -84,34 +84,34 @@ public class LocalAuthController : Controller ), new AuthenticationProperties()); // Redirect back to wasm app - return Results.Redirect("/"); + return Redirect("/"); } catch (Exception e) { string errorMessage; - if (e is HttpApiException apiException) - errorMessage = apiException.Title; + if (e is AggregateException aggregateException) + errorMessage = aggregateException.Message; else { errorMessage = "An internal error occured"; Logger.LogError(e, "An unhandled error occured while logging in user"); } - var html = await ComponentHelper.RenderComponent(ServiceProvider, + var html = await ComponentHelper.RenderToHtmlAsync(ServiceProvider, parameters => { parameters["ErrorMessage"] = errorMessage; }); - return Results.Content(html, "text/html"); + return Content(html, "text/html"); } } [HttpPost("register")] - public async Task Register([FromForm] string email, [FromForm] string password, [FromForm] string username) + public async Task RegisterAsync([FromForm] string email, [FromForm] string password, [FromForm] string username) { try { // Perform register - var user = await InternalRegister(username, email, password); + var user = await InternalRegisterAsync(username, email, password); // Login user var options = Options.Get(LocalAuthConstants.AuthenticationScheme); @@ -128,37 +128,37 @@ public class LocalAuthController : Controller ), new AuthenticationProperties()); // Redirect back to wasm app - return Results.Redirect("/"); + return Redirect("/"); } catch (Exception e) { string errorMessage; - if (e is HttpApiException apiException) - errorMessage = apiException.Title; + if (e is AggregateException aggregateException) + errorMessage = aggregateException.Message; else { errorMessage = "An internal error occured"; Logger.LogError(e, "An unhandled error occured while logging in user"); } - var html = await ComponentHelper.RenderComponent(ServiceProvider, + var html = await ComponentHelper.RenderToHtmlAsync(ServiceProvider, parameters => { parameters["ErrorMessage"] = errorMessage; }); - return Results.Content(html, "text/html"); + return Content(html, "text/html"); } } - private async Task InternalRegister(string username, string email, string password) + private async Task InternalRegisterAsync(string username, string email, string password) { email = email.ToLower(); username = username.ToLower(); if (await UserRepository.Get().AnyAsync(x => x.Username == username)) - throw new HttpApiException("A account with that username already exists", 400); + throw new AggregateException("A account with that username already exists"); if (await UserRepository.Get().AnyAsync(x => x.Email == email)) - throw new HttpApiException("A account with that email already exists", 400); + throw new AggregateException("A account with that email already exists"); string[] permissions = []; @@ -180,12 +180,12 @@ public class LocalAuthController : Controller Permissions = permissions }; - var finalUser = await UserRepository.Add(user); + var finalUser = await UserRepository.AddAsync(user); return finalUser; } - private async Task InternalLogin(string email, string password) + private async Task InternalLoginAsync(string email, string password) { email = email.ToLower(); @@ -194,10 +194,10 @@ public class LocalAuthController : Controller .FirstOrDefaultAsync(x => x.Email == email); if (user == null) - throw new HttpApiException("Invalid combination of email and password", 400); + throw new AggregateException("Invalid combination of email and password"); if (!HashHelper.Verify(password, user.Password)) - throw new HttpApiException("Invalid combination of email and password", 400); + throw new AggregateException("Invalid combination of email and password"); return user; } diff --git a/Moonlight.ApiServer/Http/Controllers/Swagger/SwaggerController.cs b/Moonlight.ApiServer/Http/Controllers/Swagger/SwaggerController.cs index 5c8941fc..8e293774 100644 --- a/Moonlight.ApiServer/Http/Controllers/Swagger/SwaggerController.cs +++ b/Moonlight.ApiServer/Http/Controllers/Swagger/SwaggerController.cs @@ -24,7 +24,7 @@ public class SwaggerController : Controller [HttpGet] [Authorize] - public async Task Get() + public async Task GetAsync() { if (!Configuration.Development.EnableApiDocs) return BadRequest("Api docs are disabled"); @@ -32,7 +32,7 @@ public class SwaggerController : Controller var options = new ApiDocsOptions(); var optionsJson = JsonSerializer.Serialize(options); - var html = await ComponentHelper.RenderComponent( + var html = await ComponentHelper.RenderToHtmlAsync( ServiceProvider, parameters => { diff --git a/Moonlight.ApiServer/Http/Hubs/DiagnoseHub.cs b/Moonlight.ApiServer/Http/Hubs/DiagnoseHub.cs index a9712499..3207e3b2 100644 --- a/Moonlight.ApiServer/Http/Hubs/DiagnoseHub.cs +++ b/Moonlight.ApiServer/Http/Hubs/DiagnoseHub.cs @@ -7,7 +7,7 @@ namespace Moonlight.ApiServer.Http.Hubs; public class DiagnoseHub : Hub { [HubMethodName("Ping")] - public async Task Ping() + public async Task PingAsync() { await Clients.All.SendAsync("Pong"); } diff --git a/Moonlight.ApiServer/Implementations/Metrics/ApplicationMetric.cs b/Moonlight.ApiServer/Implementations/Metrics/ApplicationMetric.cs index acb2dddb..1ca9b24a 100644 --- a/Moonlight.ApiServer/Implementations/Metrics/ApplicationMetric.cs +++ b/Moonlight.ApiServer/Implementations/Metrics/ApplicationMetric.cs @@ -11,7 +11,7 @@ public class ApplicationMetric : IMetric private Gauge CpuUsage; private Gauge Uptime; - public Task Initialize(Meter meter) + public Task InitializeAsync(Meter meter) { MemoryUsage = meter.CreateGauge("moonlight_memory_usage"); CpuUsage = meter.CreateGauge("moonlight_cpu_usage"); @@ -20,17 +20,17 @@ public class ApplicationMetric : IMetric return Task.CompletedTask; } - public async Task Run(IServiceProvider provider, CancellationToken cancellationToken) + public async Task RunAsync(IServiceProvider provider, CancellationToken cancellationToken) { var applicationService = provider.GetRequiredService(); - var memory = await applicationService.GetMemoryUsage(); + var memory = await applicationService.GetMemoryUsageAsync(); MemoryUsage.Record(memory); - var uptime = await applicationService.GetUptime(); + var uptime = await applicationService.GetUptimeAsync(); Uptime.Record(uptime.TotalSeconds); - var cpu = await applicationService.GetCpuUsage(); + var cpu = await applicationService.GetCpuUsageAsync(); CpuUsage.Record(cpu); } } \ No newline at end of file diff --git a/Moonlight.ApiServer/Implementations/Metrics/UsersMetric.cs b/Moonlight.ApiServer/Implementations/Metrics/UsersMetric.cs index 65bad6f1..006f55b1 100644 --- a/Moonlight.ApiServer/Implementations/Metrics/UsersMetric.cs +++ b/Moonlight.ApiServer/Implementations/Metrics/UsersMetric.cs @@ -11,14 +11,14 @@ public class UsersMetric : IMetric { private Gauge Users; - public Task Initialize(Meter meter) + public Task InitializeAsync(Meter meter) { Users = meter.CreateGauge("moonlight_users"); return Task.CompletedTask; } - public async Task Run(IServiceProvider provider, CancellationToken cancellationToken) + public async Task RunAsync(IServiceProvider provider, CancellationToken cancellationToken) { var usersRepo = provider.GetRequiredService>(); var count = await usersRepo.Get().CountAsync(cancellationToken: cancellationToken); diff --git a/Moonlight.ApiServer/Implementations/Startup/CoreStartup.cs b/Moonlight.ApiServer/Implementations/Startup/CoreStartup.cs index 28918b5b..042b3619 100644 --- a/Moonlight.ApiServer/Implementations/Startup/CoreStartup.cs +++ b/Moonlight.ApiServer/Implementations/Startup/CoreStartup.cs @@ -21,7 +21,7 @@ namespace Moonlight.ApiServer.Implementations.Startup; public class CoreStartup : IPluginStartup { - public Task BuildApplication(IServiceProvider serviceProvider, IHostApplicationBuilder builder) + public Task BuildApplicationAsync(IServiceProvider serviceProvider, IHostApplicationBuilder builder) { var configuration = serviceProvider.GetRequiredService(); @@ -142,7 +142,7 @@ public class CoreStartup : IPluginStartup return Task.CompletedTask; } - public Task ConfigureApplication(IServiceProvider serviceProvider, IApplicationBuilder app) + public Task ConfigureApplicationAsync(IServiceProvider serviceProvider, IApplicationBuilder app) { var configuration = serviceProvider.GetRequiredService(); @@ -156,7 +156,7 @@ public class CoreStartup : IPluginStartup return Task.CompletedTask; } - public Task ConfigureEndpoints(IServiceProvider serviceProvider, IEndpointRouteBuilder routeBuilder) + public Task ConfigureEndpointsAsync(IServiceProvider serviceProvider, IEndpointRouteBuilder routeBuilder) { var configuration = serviceProvider.GetRequiredService(); diff --git a/Moonlight.ApiServer/Interfaces/IAuthCheckExtension.cs b/Moonlight.ApiServer/Interfaces/IAuthCheckExtension.cs index 9183682b..41a2bce2 100644 --- a/Moonlight.ApiServer/Interfaces/IAuthCheckExtension.cs +++ b/Moonlight.ApiServer/Interfaces/IAuthCheckExtension.cs @@ -12,5 +12,5 @@ public interface IAuthCheckExtension /// /// The principal of the current signed-in user /// An array of claim responses which gets added to the list of claims to send to the frontend - public Task GetFrontendClaims(ClaimsPrincipal principal); + public Task GetFrontendClaimsAsync(ClaimsPrincipal principal); } \ No newline at end of file diff --git a/Moonlight.ApiServer/Interfaces/IMetric.cs b/Moonlight.ApiServer/Interfaces/IMetric.cs index 3cd4cace..77507005 100644 --- a/Moonlight.ApiServer/Interfaces/IMetric.cs +++ b/Moonlight.ApiServer/Interfaces/IMetric.cs @@ -4,6 +4,6 @@ namespace Moonlight.ApiServer.Interfaces; public interface IMetric { - public Task Initialize(Meter meter); - public Task Run(IServiceProvider provider, CancellationToken cancellationToken); + public Task InitializeAsync(Meter meter); + public Task RunAsync(IServiceProvider provider, CancellationToken cancellationToken); } \ No newline at end of file diff --git a/Moonlight.ApiServer/Interfaces/IUserAuthExtension.cs b/Moonlight.ApiServer/Interfaces/IUserAuthExtension.cs index 965efbb0..eca9963d 100644 --- a/Moonlight.ApiServer/Interfaces/IUserAuthExtension.cs +++ b/Moonlight.ApiServer/Interfaces/IUserAuthExtension.cs @@ -12,7 +12,7 @@ public interface IUserAuthExtension /// The current user this method is called for /// The principal after being processed by moonlight itself /// The result of the synchronisation. Returning false will immediately invalidate the sign-in and no other extensions will be called - public Task Sync(User user, ClaimsPrincipal principal); + public Task SyncAsync(User user, ClaimsPrincipal principal); /// /// IMPORTANT: Please note that heavy operations should not occur in this method as it will be called for every request @@ -21,5 +21,5 @@ public interface IUserAuthExtension /// The current user this method is called for /// The principal after being processed by moonlight itself /// The result of the validation. Returning false will immediately invalidate the users session and no other extensions will be called - public Task Validate(User user, ClaimsPrincipal principal); + public Task ValidateAsync(User user, ClaimsPrincipal principal); } \ No newline at end of file diff --git a/Moonlight.ApiServer/Interfaces/IUserDeleteHandler.cs b/Moonlight.ApiServer/Interfaces/IUserDeleteHandler.cs index 5365e5f1..614c5031 100644 --- a/Moonlight.ApiServer/Interfaces/IUserDeleteHandler.cs +++ b/Moonlight.ApiServer/Interfaces/IUserDeleteHandler.cs @@ -5,6 +5,6 @@ namespace Moonlight.ApiServer.Interfaces; public interface IUserDeleteHandler { - public Task Validate(User user); - public Task Delete(User user, bool force); + public Task ValidateAsync(User user); + public Task DeleteAsync(User user, bool force); } \ No newline at end of file diff --git a/Moonlight.ApiServer/Mappers/ApiKeyMapper.cs b/Moonlight.ApiServer/Mappers/ApiKeyMapper.cs new file mode 100644 index 00000000..2425cda2 --- /dev/null +++ b/Moonlight.ApiServer/Mappers/ApiKeyMapper.cs @@ -0,0 +1,19 @@ +using Moonlight.ApiServer.Database.Entities; +using Moonlight.Shared.Http.Requests.Admin.ApiKeys; +using Moonlight.Shared.Http.Responses.Admin.ApiKeys; +using Riok.Mapperly.Abstractions; + +namespace Moonlight.ApiServer.Mappers; + +[Mapper] +public static partial class ApiKeyMapper +{ + // Mappers + public static partial ApiKeyResponse ToResponse(ApiKey apiKey); + public static partial ApiKey ToApiKey(CreateApiKeyRequest request); + public static partial void Merge([MappingTarget] ApiKey apiKey, UpdateApiKeyRequest request); + + // EF Relations + + public static partial IQueryable ProjectToResponse(this IQueryable apiKeys); +} \ No newline at end of file diff --git a/Moonlight.ApiServer/Mappers/ThemeMapper.cs b/Moonlight.ApiServer/Mappers/ThemeMapper.cs index 3523f9db..4b0e7659 100644 --- a/Moonlight.ApiServer/Mappers/ThemeMapper.cs +++ b/Moonlight.ApiServer/Mappers/ThemeMapper.cs @@ -8,7 +8,12 @@ namespace Moonlight.ApiServer.Mappers; [Mapper] public static partial class ThemeMapper { + // Mappers public static partial ThemeResponse ToResponse(Theme theme); public static partial Theme ToTheme(CreateThemeRequest request); public static partial void Merge([MappingTarget] Theme theme, UpdateThemeRequest request); + + // EF Relations + + public static partial IQueryable ProjectToResponse(this IQueryable themes); } \ No newline at end of file diff --git a/Moonlight.ApiServer/Mappers/UserMapper.cs b/Moonlight.ApiServer/Mappers/UserMapper.cs new file mode 100644 index 00000000..35069d31 --- /dev/null +++ b/Moonlight.ApiServer/Mappers/UserMapper.cs @@ -0,0 +1,15 @@ +using Moonlight.ApiServer.Database.Entities; +using Moonlight.Shared.Http.Responses.Admin.Users; +using Riok.Mapperly.Abstractions; + +namespace Moonlight.ApiServer.Mappers; + +[Mapper] +public static partial class UserMapper +{ + // Mappers + public static partial UserResponse ToResponse(User user); + + // EF Relations + public static partial IQueryable ProjectToResponse(this IQueryable users); +} \ No newline at end of file diff --git a/Moonlight.ApiServer/Moonlight.ApiServer.csproj b/Moonlight.ApiServer/Moonlight.ApiServer.csproj index 6bc2b9e7..11ea9d5d 100644 --- a/Moonlight.ApiServer/Moonlight.ApiServer.csproj +++ b/Moonlight.ApiServer/Moonlight.ApiServer.csproj @@ -13,7 +13,7 @@ Moonlight.ApiServer - 2.1.10 + 2.1.11 Moonlight Panel A build of the api server for moonlight development https://github.com/Moonlight-Panel/Moonlight @@ -25,10 +25,12 @@ + - - + + + diff --git a/Moonlight.ApiServer/Plugins/IPluginStartup.cs b/Moonlight.ApiServer/Plugins/IPluginStartup.cs index 91995389..1db8c646 100644 --- a/Moonlight.ApiServer/Plugins/IPluginStartup.cs +++ b/Moonlight.ApiServer/Plugins/IPluginStartup.cs @@ -6,7 +6,7 @@ namespace Moonlight.ApiServer.Plugins; public interface IPluginStartup { - public Task BuildApplication(IServiceProvider serviceProvider, IHostApplicationBuilder builder); - public Task ConfigureApplication(IServiceProvider serviceProvider, IApplicationBuilder app); - public Task ConfigureEndpoints(IServiceProvider serviceProvider, IEndpointRouteBuilder routeBuilder); + public Task BuildApplicationAsync(IServiceProvider serviceProvider, IHostApplicationBuilder builder); + public Task ConfigureApplicationAsync(IServiceProvider serviceProvider, IApplicationBuilder app); + public Task ConfigureEndpointsAsync(IServiceProvider serviceProvider, IEndpointRouteBuilder routeBuilder); } \ No newline at end of file diff --git a/Moonlight.ApiServer/Services/ApiKeyAuthService.cs b/Moonlight.ApiServer/Services/ApiKeyAuthService.cs index 15589fe8..ff6346a0 100644 --- a/Moonlight.ApiServer/Services/ApiKeyAuthService.cs +++ b/Moonlight.ApiServer/Services/ApiKeyAuthService.cs @@ -14,7 +14,7 @@ public class ApiKeyAuthService ApiKeyRepository = apiKeyRepository; } - public async Task Validate(ClaimsPrincipal? principal) + public async Task ValidateAsync(ClaimsPrincipal? principal) { // Ignore malformed claims principal if (principal is not { Identity.IsAuthenticated: true }) diff --git a/Moonlight.ApiServer/Services/ApplicationService.cs b/Moonlight.ApiServer/Services/ApplicationService.cs index 37caef68..db9c9197 100644 --- a/Moonlight.ApiServer/Services/ApplicationService.cs +++ b/Moonlight.ApiServer/Services/ApplicationService.cs @@ -19,7 +19,7 @@ public class ApplicationService Host = host; } - public Task GetOsName() + public Task GetOsNameAsync() { if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { @@ -58,7 +58,7 @@ public class ApplicationService return Task.FromResult("N/A"); } - public async Task GetMemoryUsage() + public async Task GetMemoryUsageAsync() { if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { @@ -87,14 +87,14 @@ public class ApplicationService } } - public Task GetUptime() + public Task GetUptimeAsync() { var process = Process.GetCurrentProcess(); var uptime = DateTime.Now - process.StartTime; return Task.FromResult(uptime); } - public Task GetCpuUsage() + public Task GetCpuUsageAsync() { var process = Process.GetCurrentProcess(); var cpuTime = process.TotalProcessorTime; @@ -105,7 +105,7 @@ public class ApplicationService return Task.FromResult(cpuUsage); } - public Task Shutdown() + public Task ShutdownAsync() { Logger.LogCritical("Restart of api server has been requested"); diff --git a/Moonlight.ApiServer/Services/FrontendService.cs b/Moonlight.ApiServer/Services/FrontendService.cs index bfd72b97..18cfae33 100644 --- a/Moonlight.ApiServer/Services/FrontendService.cs +++ b/Moonlight.ApiServer/Services/FrontendService.cs @@ -40,7 +40,7 @@ public class FrontendService ThemeRepository = themeRepository; } - public Task GetConfiguration() + public Task GetConfigurationAsync() { var configuration = new FrontendConfiguration() { @@ -51,7 +51,7 @@ public class FrontendService return Task.FromResult(configuration); } - public async Task GenerateIndexHtml() // TODO: Cache + public async Task GenerateIndexHtmlAsync() // TODO: Cache { // Load requested theme var theme = await ThemeRepository @@ -70,7 +70,7 @@ public class FrontendService .Distinct() .ToArray(); - return await ComponentHelper.RenderComponent( + return await ComponentHelper.RenderToHtmlAsync( ServiceProvider, parameters => { @@ -82,7 +82,7 @@ public class FrontendService ); } - public async Task GenerateZip() // TODO: Rework to be able to extract everything successfully + public async Task GenerateZipAsync() // TODO: Rework to be able to extract everything successfully { // We only allow the access to this function when we are actually hosting the frontend if (!Configuration.Frontend.EnableHosting) @@ -109,16 +109,16 @@ public class FrontendService var zipArchive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true); // Add wasm application - await ArchiveFsItem(zipArchive, wasmPath, wasmPath); + await ArchiveFsItemAsync(zipArchive, wasmPath, wasmPath); // Add blazor files - await ArchiveFsItem(zipArchive, blazorPath, blazorPath, "_framework/"); + await ArchiveFsItemAsync(zipArchive, blazorPath, blazorPath, "_framework/"); // Add frontend.json - var frontendConfig = await GetConfiguration(); + var frontendConfig = await GetConfigurationAsync(); frontendConfig.HostEnvironment = "Static"; var frontendJson = JsonSerializer.Serialize(frontendConfig); - await ArchiveText(zipArchive, "frontend.json", frontendJson); + await ArchiveTextAsync(zipArchive, "frontend.json", frontendJson); // Finish zip archive and reset stream so the code calling this function can process it zipArchive.Dispose(); @@ -128,7 +128,7 @@ public class FrontendService return memoryStream; } - private async Task ArchiveFsItem(ZipArchive archive, string path, string prefixToRemove, string prefixToAdd = "") + private async Task ArchiveFsItemAsync(ZipArchive archive, string path, string prefixToRemove, string prefixToAdd = "") { if (File.Exists(path)) { @@ -147,17 +147,17 @@ public class FrontendService else { foreach (var directoryItem in Directory.EnumerateFileSystemEntries(path)) - await ArchiveFsItem(archive, directoryItem, prefixToRemove, prefixToAdd); + await ArchiveFsItemAsync(archive, directoryItem, prefixToRemove, prefixToAdd); } } - private async Task ArchiveText(ZipArchive archive, string path, string content) + private async Task ArchiveTextAsync(ZipArchive archive, string path, string content) { var data = Encoding.UTF8.GetBytes(content); - await ArchiveBytes(archive, path, data); + await ArchiveBytesAsync(archive, path, data); } - private async Task ArchiveBytes(ZipArchive archive, string path, byte[] bytes) + private async Task ArchiveBytesAsync(ZipArchive archive, string path, byte[] bytes) { var entry = archive.CreateEntry(path); await using var dataStream = entry.Open(); diff --git a/Moonlight.ApiServer/Services/MetricsBackgroundService.cs b/Moonlight.ApiServer/Services/MetricsBackgroundService.cs index 7aece2ab..0c25ec6d 100644 --- a/Moonlight.ApiServer/Services/MetricsBackgroundService.cs +++ b/Moonlight.ApiServer/Services/MetricsBackgroundService.cs @@ -33,7 +33,7 @@ public class MetricsBackgroundService : BackgroundService Metrics = metrics.ToArray(); } - private async Task Initialize() + private async Task InitializeAsync() { Logger.LogDebug( "Initializing metrics: {names}", @@ -41,12 +41,12 @@ public class MetricsBackgroundService : BackgroundService ); foreach (var metric in Metrics) - await metric.Initialize(Meter); + await metric.InitializeAsync(Meter); } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { - await Initialize(); + await InitializeAsync(); while (!stoppingToken.IsCancellationRequested) { @@ -56,7 +56,7 @@ public class MetricsBackgroundService : BackgroundService { try { - await metric.Run(scope.ServiceProvider, stoppingToken); + await metric.RunAsync(scope.ServiceProvider, stoppingToken); } catch (TaskCanceledException) { diff --git a/Moonlight.ApiServer/Services/UserAuthService.cs b/Moonlight.ApiServer/Services/UserAuthService.cs index 18893e08..d6624430 100644 --- a/Moonlight.ApiServer/Services/UserAuthService.cs +++ b/Moonlight.ApiServer/Services/UserAuthService.cs @@ -33,7 +33,7 @@ public class UserAuthService Extensions = extensions; } - public async Task Sync(ClaimsPrincipal? principal) + public async Task SyncAsync(ClaimsPrincipal? principal) { // Ignore malformed claims principal if (principal is not { Identity.IsAuthenticated: true }) @@ -80,7 +80,7 @@ public class UserAuthService permissions = ["*"]; } - user = await UserRepository.Add(new User() + user = await UserRepository.AddAsync(new User() { Email = email, TokenValidTimestamp = DateTimeOffset.UtcNow.AddMinutes(-1), @@ -94,7 +94,7 @@ public class UserAuthService if (user.Username != username) { user.Username = username; - await UserRepository.Update(user); + await UserRepository.UpdateAsync(user); } // Enrich claims with required metadata @@ -107,7 +107,7 @@ public class UserAuthService // Call extensions foreach (var extension in Extensions) { - var result = await extension.Sync(user, principal); + var result = await extension.SyncAsync(user, principal); if (!result) // Exit immediately if result is false return false; @@ -116,7 +116,7 @@ public class UserAuthService return true; } - public async Task Validate(ClaimsPrincipal? principal) + public async Task ValidateAsync(ClaimsPrincipal? principal) { // Ignore malformed claims principal if (principal is not { Identity.IsAuthenticated: true }) @@ -157,7 +157,7 @@ public class UserAuthService // Call extensions foreach (var extension in Extensions) { - var result = await extension.Validate(user, principal); + var result = await extension.ValidateAsync(user, principal); if (!result) // Exit immediately if result is false return false; diff --git a/Moonlight.ApiServer/Services/UserDeletionService.cs b/Moonlight.ApiServer/Services/UserDeletionService.cs index a58324d5..4e85b8fd 100644 --- a/Moonlight.ApiServer/Services/UserDeletionService.cs +++ b/Moonlight.ApiServer/Services/UserDeletionService.cs @@ -19,11 +19,11 @@ public class UserDeletionService Handlers = handlers.ToArray(); } - public async Task Validate(User user) + public async Task ValidateAsync(User user) { foreach (var handler in Handlers) { - var result = await handler.Validate(user); + var result = await handler.ValidateAsync(user); if (!result.IsAllowed) return result; @@ -32,11 +32,11 @@ public class UserDeletionService return UserDeleteValidationResult.Allow(); } - public async Task Delete(User user, bool force) + public async Task DeleteAsync(User user, bool force) { foreach (var handler in Handlers) - await Delete(user, force); + await handler.DeleteAsync(user, force); - await UserRepository.Remove(user); + await UserRepository.RemoveAsync(user); } } \ No newline at end of file diff --git a/Moonlight.ApiServer/Startup/Startup.Auth.cs b/Moonlight.ApiServer/Startup/Startup.Auth.cs index 08d4f561..d2362204 100644 --- a/Moonlight.ApiServer/Startup/Startup.Auth.cs +++ b/Moonlight.ApiServer/Startup/Startup.Auth.cs @@ -13,7 +13,7 @@ namespace Moonlight.ApiServer.Startup; public partial class Startup { - private Task RegisterAuth() + private Task RegisterAuthAsync() { WebApplicationBuilder.Services .AddAuthentication(options => { options.DefaultScheme = "MainScheme"; }) @@ -62,7 +62,7 @@ public partial class Startup .RequestServices .GetRequiredService(); - var result = await apiKeyAuthService.Validate(context.Principal); + var result = await apiKeyAuthService.ValidateAsync(context.Principal); if (!result) context.Fail("API key has been deleted"); @@ -120,7 +120,7 @@ public partial class Startup .RequestServices .GetRequiredService(); - var result = await userSyncService.Sync(context.Principal); + var result = await userSyncService.SyncAsync(context.Principal); if (!result) context.Principal = new(); @@ -135,7 +135,7 @@ public partial class Startup .RequestServices .GetRequiredService(); - var result = await userSyncService.Validate(context.Principal); + var result = await userSyncService.ValidateAsync(context.Principal); if (!result) context.RejectPrincipal(); @@ -178,7 +178,7 @@ public partial class Startup return Task.CompletedTask; } - private Task UseAuth() + private Task UseAuthAsync() { WebApplication.UseAuthentication(); diff --git a/Moonlight.ApiServer/Startup/Startup.Base.cs b/Moonlight.ApiServer/Startup/Startup.Base.cs index 6ee388e2..48be2b28 100644 --- a/Moonlight.ApiServer/Startup/Startup.Base.cs +++ b/Moonlight.ApiServer/Startup/Startup.Base.cs @@ -9,7 +9,7 @@ namespace Moonlight.ApiServer.Startup; public partial class Startup { - private Task RegisterBase() + private Task RegisterBaseAsync() { WebApplicationBuilder.Services.AutoAddServices(); WebApplicationBuilder.Services.AddHttpClient(); @@ -29,7 +29,7 @@ public partial class Startup return Task.CompletedTask; } - private Task UseBase() + private Task UseBaseAsync() { WebApplication.UseRouting(); WebApplication.UseExceptionHandler(); @@ -37,7 +37,7 @@ public partial class Startup return Task.CompletedTask; } - private Task MapBase() + private Task MapBaseAsync() { WebApplication.MapControllers(); @@ -47,7 +47,7 @@ public partial class Startup return Task.CompletedTask; } - private Task ConfigureKestrel() + private Task ConfigureKestrelAsync() { WebApplicationBuilder.WebHost.ConfigureKestrel(kestrelOptions => { diff --git a/Moonlight.ApiServer/Startup/Startup.Config.cs b/Moonlight.ApiServer/Startup/Startup.Config.cs index 1ec38500..3631ad8f 100644 --- a/Moonlight.ApiServer/Startup/Startup.Config.cs +++ b/Moonlight.ApiServer/Startup/Startup.Config.cs @@ -8,11 +8,11 @@ namespace Moonlight.ApiServer.Startup; public partial class Startup { - private async Task SetupAppConfiguration() + private async Task SetupAppConfigurationAsync() { var configPath = Path.Combine("storage", "config.yml"); - await YamlDefaultGenerator.Generate(configPath); + await YamlDefaultGenerator.GenerateAsync(configPath); // Configure configuration (wow) var configurationBuilder = new ConfigurationBuilder(); @@ -27,7 +27,7 @@ public partial class Startup configurationRoot.Bind(Configuration); } - private Task RegisterAppConfiguration() + private Task RegisterAppConfigurationAsync() { WebApplicationBuilder.Services.AddSingleton(Configuration); return Task.CompletedTask; diff --git a/Moonlight.ApiServer/Startup/Startup.Database.cs b/Moonlight.ApiServer/Startup/Startup.Database.cs index 5f5dcb51..6c50f914 100644 --- a/Moonlight.ApiServer/Startup/Startup.Database.cs +++ b/Moonlight.ApiServer/Startup/Startup.Database.cs @@ -6,7 +6,7 @@ namespace Moonlight.ApiServer.Startup; public partial class Startup { - private Task RegisterDatabase() + private Task RegisterDatabaseAsync() { WebApplicationBuilder.Services.AddDatabaseMappings(); WebApplicationBuilder.Services.AddServiceCollectionAccessor(); @@ -16,9 +16,9 @@ public partial class Startup return Task.CompletedTask; } - private async Task PrepareDatabase() + private async Task PrepareDatabaseAsync() { - await WebApplication.Services.EnsureDatabaseMigrated(); + await WebApplication.Services.EnsureDatabaseMigratedAsync(); WebApplication.Services.GenerateDatabaseMappings(); } diff --git a/Moonlight.ApiServer/Startup/Startup.Hangfire.cs b/Moonlight.ApiServer/Startup/Startup.Hangfire.cs index b06583f6..2f8922fd 100644 --- a/Moonlight.ApiServer/Startup/Startup.Hangfire.cs +++ b/Moonlight.ApiServer/Startup/Startup.Hangfire.cs @@ -9,7 +9,7 @@ namespace Moonlight.ApiServer.Startup; public partial class Startup { - private Task RegisterHangfire() + private Task RegisterHangfireAsync() { WebApplicationBuilder.Services.AddHangfire((provider, configuration) => { @@ -38,7 +38,7 @@ public partial class Startup return Task.CompletedTask; } - private Task UseHangfire() + private Task UseHangfireAsync() { if (WebApplication.Environment.IsDevelopment()) WebApplication.UseHangfireDashboard(); diff --git a/Moonlight.ApiServer/Startup/Startup.Logging.cs b/Moonlight.ApiServer/Startup/Startup.Logging.cs index 2bc18b69..2eb51ed9 100644 --- a/Moonlight.ApiServer/Startup/Startup.Logging.cs +++ b/Moonlight.ApiServer/Startup/Startup.Logging.cs @@ -6,7 +6,7 @@ namespace Moonlight.ApiServer.Startup; public partial class Startup { - private Task SetupLogging() + private Task SetupLoggingAsync() { var loggerFactory = new LoggerFactory(); loggerFactory.AddAnsiConsole(); @@ -16,7 +16,7 @@ public partial class Startup return Task.CompletedTask; } - private async Task RegisterLogging() + private async Task RegisterLoggingAsync() { // Configure application logging WebApplicationBuilder.Logging.ClearProviders(); diff --git a/Moonlight.ApiServer/Startup/Startup.Misc.cs b/Moonlight.ApiServer/Startup/Startup.Misc.cs index f8392f05..d106500a 100644 --- a/Moonlight.ApiServer/Startup/Startup.Misc.cs +++ b/Moonlight.ApiServer/Startup/Startup.Misc.cs @@ -6,7 +6,7 @@ namespace Moonlight.ApiServer.Startup; public partial class Startup { - private Task PrintVersion() + private Task PrintVersionAsync() { // Fancy start console output... yes very fancy :> var rainbow = new Crayon.Rainbow(0.5); @@ -25,7 +25,7 @@ public partial class Startup return Task.CompletedTask; } - private Task CreateStorage() + private Task CreateStorageAsync() { Directory.CreateDirectory("storage"); Directory.CreateDirectory(Path.Combine("storage", "logs")); @@ -33,7 +33,7 @@ public partial class Startup return Task.CompletedTask; } - private Task RegisterCors() + private Task RegisterCorsAsync() { var allowedOrigins = Configuration.Kestrel.AllowedOrigins; @@ -64,7 +64,7 @@ public partial class Startup return Task.CompletedTask; } - private Task UseCors() + private Task UseCorsAsync() { WebApplication.UseCors(); diff --git a/Moonlight.ApiServer/Startup/Startup.Plugins.cs b/Moonlight.ApiServer/Startup/Startup.Plugins.cs index ec01272c..f6644859 100644 --- a/Moonlight.ApiServer/Startup/Startup.Plugins.cs +++ b/Moonlight.ApiServer/Startup/Startup.Plugins.cs @@ -10,7 +10,7 @@ public partial class Startup private IServiceProvider PluginLoadServiceProvider; private IPluginStartup[] PluginStartups; - private Task InitializePlugins() + private Task InitializePluginsAsync() { // Create service provider for starting up var serviceCollection = new ServiceCollection(); @@ -28,13 +28,13 @@ public partial class Startup return Task.CompletedTask; } - private async Task HookPluginBuild() + private async Task HookPluginBuildAsync() { foreach (var pluginAppStartup in PluginStartups) { try { - await pluginAppStartup.BuildApplication(PluginLoadServiceProvider, WebApplicationBuilder); + await pluginAppStartup.BuildApplicationAsync(PluginLoadServiceProvider, WebApplicationBuilder); } catch (Exception e) { @@ -47,13 +47,13 @@ public partial class Startup } } - private async Task HookPluginConfigure() + private async Task HookPluginConfigureAsync() { foreach (var pluginAppStartup in PluginStartups) { try { - await pluginAppStartup.ConfigureApplication(PluginLoadServiceProvider, WebApplication); + await pluginAppStartup.ConfigureApplicationAsync(PluginLoadServiceProvider, WebApplication); } catch (Exception e) { @@ -66,13 +66,13 @@ public partial class Startup } } - private async Task HookPluginEndpoints() + private async Task HookPluginEndpointsAsync() { foreach (var pluginEndpointStartup in PluginStartups) { try { - await pluginEndpointStartup.ConfigureEndpoints(PluginLoadServiceProvider, WebApplication); + await pluginEndpointStartup.ConfigureEndpointsAsync(PluginLoadServiceProvider, WebApplication); } catch (Exception e) { diff --git a/Moonlight.ApiServer/Startup/Startup.SignalR.cs b/Moonlight.ApiServer/Startup/Startup.SignalR.cs index ab4bf2a3..f1feef1d 100644 --- a/Moonlight.ApiServer/Startup/Startup.SignalR.cs +++ b/Moonlight.ApiServer/Startup/Startup.SignalR.cs @@ -6,7 +6,7 @@ namespace Moonlight.ApiServer.Startup; public partial class Startup { - public Task RegisterSignalR() + public Task RegisterSignalRAsync() { var signalRBuilder = WebApplicationBuilder.Services.AddSignalR(); @@ -16,7 +16,7 @@ public partial class Startup return Task.CompletedTask; } - public Task MapSignalR() + public Task MapSignalRAsync() { WebApplication.MapHub("/api/admin/system/diagnose/ws"); diff --git a/Moonlight.ApiServer/Startup/Startup.cs b/Moonlight.ApiServer/Startup/Startup.cs index 64268b0a..e612581f 100644 --- a/Moonlight.ApiServer/Startup/Startup.cs +++ b/Moonlight.ApiServer/Startup/Startup.cs @@ -19,7 +19,7 @@ public partial class Startup public WebApplication WebApplication { get; private set; } public WebApplicationBuilder WebApplicationBuilder { get; private set; } - public Task Initialize(string[] args, IPluginStartup[]? plugins = null) + public Task InitializeAsync(string[] args, IPluginStartup[]? plugins = null) { Args = args; PluginStartups = plugins ?? []; @@ -27,43 +27,43 @@ public partial class Startup return Task.CompletedTask; } - public async Task AddMoonlight(WebApplicationBuilder builder) + public async Task AddMoonlightAsync(WebApplicationBuilder builder) { WebApplicationBuilder = builder; - await PrintVersion(); + await PrintVersionAsync(); - await CreateStorage(); - await SetupAppConfiguration(); - await SetupLogging(); - await InitializePlugins(); + await CreateStorageAsync(); + await SetupAppConfigurationAsync(); + await SetupLoggingAsync(); + await InitializePluginsAsync(); - await ConfigureKestrel(); - await RegisterAppConfiguration(); - await RegisterLogging(); - await RegisterBase(); - await RegisterDatabase(); - await RegisterAuth(); - await RegisterCors(); - await RegisterHangfire(); - await RegisterSignalR(); - await HookPluginBuild(); + await ConfigureKestrelAsync(); + await RegisterAppConfigurationAsync(); + await RegisterLoggingAsync(); + await RegisterBaseAsync(); + await RegisterDatabaseAsync(); + await RegisterAuthAsync(); + await RegisterCorsAsync(); + await RegisterHangfireAsync(); + await RegisterSignalRAsync(); + await HookPluginBuildAsync(); } - public async Task AddMoonlight(WebApplication application) + public async Task AddMoonlightAsync(WebApplication application) { WebApplication = application; - await PrepareDatabase(); + await PrepareDatabaseAsync(); - await UseCors(); - await UseBase(); - await UseAuth(); - await UseHangfire(); - await HookPluginConfigure(); + await UseCorsAsync(); + await UseBaseAsync(); + await UseAuthAsync(); + await UseHangfireAsync(); + await HookPluginConfigureAsync(); - await MapBase(); - await MapSignalR(); - await HookPluginEndpoints(); + await MapBaseAsync(); + await MapSignalRAsync(); + await HookPluginEndpointsAsync(); } } \ No newline at end of file diff --git a/Moonlight.Client.Runtime/Moonlight.Client.Runtime.csproj b/Moonlight.Client.Runtime/Moonlight.Client.Runtime.csproj index a56848a9..4c67e785 100644 --- a/Moonlight.Client.Runtime/Moonlight.Client.Runtime.csproj +++ b/Moonlight.Client.Runtime/Moonlight.Client.Runtime.csproj @@ -15,8 +15,8 @@ - - + + diff --git a/Moonlight.Client.Runtime/Program.cs b/Moonlight.Client.Runtime/Program.cs index 16b1f2f4..a45cb934 100644 --- a/Moonlight.Client.Runtime/Program.cs +++ b/Moonlight.Client.Runtime/Program.cs @@ -7,14 +7,14 @@ pluginLoader.Initialize(); var startup = new Startup(); -await startup.Initialize(pluginLoader.Instances); +await startup.InitializeAsync(pluginLoader.Instances); var wasmHostBuilder = WebAssemblyHostBuilder.CreateDefault(args); -await startup.AddMoonlight(wasmHostBuilder); +await startup.AddMoonlightAsync(wasmHostBuilder); var wasmApp = wasmHostBuilder.Build(); -await startup.AddMoonlight(wasmApp); +await startup.AddMoonlightAsync(wasmApp); await wasmApp.RunAsync(); \ No newline at end of file diff --git a/Moonlight.Client.Runtime/Styles/styles.css b/Moonlight.Client.Runtime/Styles/styles.css index 2d30b0c7..768baede 100644 --- a/Moonlight.Client.Runtime/Styles/styles.css +++ b/Moonlight.Client.Runtime/Styles/styles.css @@ -100,18 +100,38 @@ .select { @apply !border-base-content/20 border-2 ring-0! outline-0! focus:border-primary! focus-within:border-primary! bg-base-200/50; } - + .table { :where(th, td) { @apply py-1.5; } } - + .dropdown-item { @apply px-2.5 py-1.5 text-sm; } - + .dropdown-menu { @apply bg-base-150; } + + .advance-select-menu { + @apply !border-base-content/20 border-2 ring-0! outline-0! bg-base-200/50 !px-0; + } + + .advance-select-option { + @apply !rounded-none hover:!bg-primary; + } + + .advance-select-option.selected { + @apply !bg-primary !text-primary-content; + } + + .advance-select-toggle { + @apply !border-base-content/20 border-2 ring-0! outline-0! focus:border-primary! focus-within:border-primary! bg-base-200/50; + } + + .table thead { + @apply !normal-case; + } } \ No newline at end of file diff --git a/Moonlight.Client/Implementations/CoreStartup.cs b/Moonlight.Client/Implementations/CoreStartup.cs index 06ae3249..b8307bc6 100644 --- a/Moonlight.Client/Implementations/CoreStartup.cs +++ b/Moonlight.Client/Implementations/CoreStartup.cs @@ -7,7 +7,7 @@ namespace Moonlight.Client.Implementations; public class CoreStartup : IPluginStartup { - public Task BuildApplication(IServiceProvider serviceProvider, WebAssemblyHostBuilder builder) + public Task BuildApplicationAsync(IServiceProvider serviceProvider, WebAssemblyHostBuilder builder) { builder.Services.AddSingleton(); builder.Services.AddSingleton(); @@ -15,6 +15,6 @@ public class CoreStartup : IPluginStartup return Task.CompletedTask; } - public Task ConfigureApplication(IServiceProvider serviceProvider, WebAssemblyHost app) + public Task ConfigureApplicationAsync(IServiceProvider serviceProvider, WebAssemblyHost app) => Task.CompletedTask; } \ No newline at end of file diff --git a/Moonlight.Client/Implementations/LogErrorFilter.cs b/Moonlight.Client/Implementations/LogErrorFilter.cs index 5ce50bef..d2c1bc5a 100644 --- a/Moonlight.Client/Implementations/LogErrorFilter.cs +++ b/Moonlight.Client/Implementations/LogErrorFilter.cs @@ -11,7 +11,7 @@ public class LogErrorFilter : IGlobalErrorFilter Logger = logger; } - public Task HandleException(Exception ex) + public Task HandleExceptionAsync(Exception ex) { Logger.LogError(ex, "Global error processed"); return Task.FromResult(false); diff --git a/Moonlight.Client/Implementations/SystemFsAccess.cs b/Moonlight.Client/Implementations/SystemFsAccess.cs index bacc9866..350fa21c 100644 --- a/Moonlight.Client/Implementations/SystemFsAccess.cs +++ b/Moonlight.Client/Implementations/SystemFsAccess.cs @@ -18,21 +18,21 @@ public class SystemFsAccess : IFsAccess, ICombineAccess, IArchiveAccess, IDownlo ApiClient = apiClient; } - public async Task CreateFile(string path) + public async Task CreateFileAsync(string path) { await ApiClient.Post( $"{BaseApiUrl}/touch?path={path}" ); } - public async Task CreateDirectory(string path) + public async Task CreateDirectoryAsync(string path) { await ApiClient.Post( $"{BaseApiUrl}/mkdir?path={path}" ); } - public async Task List(string path) + public async Task ListAsync(string path) { var entries = await ApiClient.GetJson( $"{BaseApiUrl}/list?path={path}" @@ -48,14 +48,14 @@ public class SystemFsAccess : IFsAccess, ICombineAccess, IArchiveAccess, IDownlo }).ToArray(); } - public async Task Move(string oldPath, string newPath) + public async Task MoveAsync(string oldPath, string newPath) { await ApiClient.Post( $"{BaseApiUrl}/move?oldPath={oldPath}&newPath={newPath}" ); } - public async Task Read(string path, Func onHandleData) + public async Task ReadAsync(string path, Func onHandleData) { await using var stream = await ApiClient.GetStream( $"{BaseApiUrl}/download?path={path}" @@ -66,7 +66,7 @@ public class SystemFsAccess : IFsAccess, ICombineAccess, IArchiveAccess, IDownlo stream.Close(); } - public async Task Write(string path, Stream dataStream) + public async Task WriteAsync(string path, Stream dataStream) { using var multiPartForm = new MultipartFormDataContent(); @@ -78,14 +78,14 @@ public class SystemFsAccess : IFsAccess, ICombineAccess, IArchiveAccess, IDownlo ); } - public async Task Delete(string path) + public async Task DeleteAsync(string path) { await ApiClient.Delete( $"{BaseApiUrl}/delete?path={path}" ); } - public async Task Combine(string destination, string[] files) + public async Task CombineAsync(string destination, string[] files) { await ApiClient.Post( $"{BaseApiUrl}/combine", @@ -103,7 +103,7 @@ public class SystemFsAccess : IFsAccess, ICombineAccess, IArchiveAccess, IDownlo new("tar.gz", ["tar.gz"], "Tar.gz Archive") ]; - public async Task Archive( + public async Task ArchiveAsync( string destination, ArchiveFormat format, string root, @@ -120,7 +120,7 @@ public class SystemFsAccess : IFsAccess, ICombineAccess, IArchiveAccess, IDownlo }); } - public async Task Unarchive( + public async Task UnarchiveAsync( string path, ArchiveFormat format, string destination, @@ -137,13 +137,13 @@ public class SystemFsAccess : IFsAccess, ICombineAccess, IArchiveAccess, IDownlo ); } - public async Task GetFileUrl(string path) - => await GetDownloadUrl(path); + public async Task GetFileUrlAsync(string path) + => await GetDownloadUrlAsync(path); - public async Task GetFolderUrl(string path) - => await GetDownloadUrl(path); + public async Task GetFolderUrlAsync(string path) + => await GetDownloadUrlAsync(path); - private async Task GetDownloadUrl(string path) + private async Task GetDownloadUrlAsync(string path) { var response = await ApiClient.PostJson( $"{BaseApiUrl}/downloadUrl?path={path}" diff --git a/Moonlight.Client/Implementations/UnauthenticatedErrorFilter.cs b/Moonlight.Client/Implementations/UnauthenticatedErrorFilter.cs index 1824f86f..6796d589 100644 --- a/Moonlight.Client/Implementations/UnauthenticatedErrorFilter.cs +++ b/Moonlight.Client/Implementations/UnauthenticatedErrorFilter.cs @@ -1,5 +1,6 @@ using Microsoft.AspNetCore.Components; using MoonCore.Blazor.FlyonUi.Exceptions; +using MoonCore.Blazor.FlyonUi.Toasts; using MoonCore.Exceptions; namespace Moonlight.Client.Implementations; @@ -7,18 +8,25 @@ namespace Moonlight.Client.Implementations; public class UnauthenticatedErrorFilter : IGlobalErrorFilter { private readonly NavigationManager Navigation; + private readonly ToastService ToastService; - public UnauthenticatedErrorFilter(NavigationManager navigation) + public UnauthenticatedErrorFilter( + NavigationManager navigation, + ToastService toastService + ) { Navigation = navigation; + ToastService = toastService; } - public Task HandleException(Exception ex) + public async Task HandleExceptionAsync(Exception ex) { if (ex is not HttpApiException { Status: 401 }) - return Task.FromResult(false); + return false; + await ToastService.InfoAsync("Session expired", "Your session has expired. Reloading.."); + Navigation.NavigateTo("/api/auth/logout", true); - return Task.FromResult(true); + return true; } } \ No newline at end of file diff --git a/Moonlight.Client/Moonlight.Client.csproj b/Moonlight.Client/Moonlight.Client.csproj index 2dc84988..5777e3c2 100644 --- a/Moonlight.Client/Moonlight.Client.csproj +++ b/Moonlight.Client/Moonlight.Client.csproj @@ -12,7 +12,7 @@ frontend Moonlight.Client - 2.1.10 + 2.1.11 Moonlight Panel A build of the client for moonlight development https://github.com/Moonlight-Panel/Moonlight @@ -22,11 +22,11 @@ + - - - + + diff --git a/Moonlight.Client/Plugins/IPluginStartup.cs b/Moonlight.Client/Plugins/IPluginStartup.cs index 68f5532e..213d5cd8 100644 --- a/Moonlight.Client/Plugins/IPluginStartup.cs +++ b/Moonlight.Client/Plugins/IPluginStartup.cs @@ -4,6 +4,6 @@ namespace Moonlight.Client.Plugins; public interface IPluginStartup { - public Task BuildApplication(IServiceProvider serviceProvider, WebAssemblyHostBuilder builder); - public Task ConfigureApplication(IServiceProvider serviceProvider, WebAssemblyHost app); + public Task BuildApplicationAsync(IServiceProvider serviceProvider, WebAssemblyHostBuilder builder); + public Task ConfigureApplicationAsync(IServiceProvider serviceProvider, WebAssemblyHost app); } \ No newline at end of file diff --git a/Moonlight.Client/Services/ThemeService.cs b/Moonlight.Client/Services/ThemeService.cs index a6628fbc..638f743b 100644 --- a/Moonlight.Client/Services/ThemeService.cs +++ b/Moonlight.Client/Services/ThemeService.cs @@ -17,21 +17,14 @@ public class ThemeService ApiClient = apiClient; } - public async Task> Get(int page, int pageSize) - { - return await ApiClient.GetJson>( - $"api/admin/system/customisation/themes?page={page}&pageSize={pageSize}" - ); - } - - public async Task Get(int id) + public async Task GetAsync(int id) { return await ApiClient.GetJson( $"api/admin/system/customisation/themes/{id}" ); } - public async Task Create(CreateThemeRequest request) + public async Task CreateAsync(CreateThemeRequest request) { return await ApiClient.PostJson( "api/admin/system/customisation/themes", @@ -39,7 +32,7 @@ public class ThemeService ); } - public async Task Update(int id, UpdateThemeRequest request) + public async Task UpdateAsync(int id, UpdateThemeRequest request) { return await ApiClient.PatchJson( $"api/admin/system/customisation/themes/{id}", @@ -47,7 +40,7 @@ public class ThemeService ); } - public async Task Delete(int id) + public async Task DeleteAsync(int id) { await ApiClient.Delete( $"api/admin/system/customisation/themes/{id}" diff --git a/Moonlight.Client/Services/WindowService.cs b/Moonlight.Client/Services/WindowService.cs index 692706c3..7e3cda67 100644 --- a/Moonlight.Client/Services/WindowService.cs +++ b/Moonlight.Client/Services/WindowService.cs @@ -9,9 +9,9 @@ public class WindowService JsRuntime = jsRuntime; } - public async Task Open(string url, string title, int height, int width) + public async Task OpenAsync(string url, string title, int height, int width) => await JsRuntime.InvokeVoidAsync("moonlight.window.open", url, title, height, width); - public async Task Close() + public async Task CloseAsync() => await JsRuntime.InvokeVoidAsync("moonlight.window.closeCurrent"); } \ No newline at end of file diff --git a/Moonlight.Client/Startup/Startup.Auth.cs b/Moonlight.Client/Startup/Startup.Auth.cs index 6f95b4ba..d3217534 100644 --- a/Moonlight.Client/Startup/Startup.Auth.cs +++ b/Moonlight.Client/Startup/Startup.Auth.cs @@ -9,7 +9,7 @@ namespace Moonlight.Client.Startup; public partial class Startup { - private Task RegisterAuthentication() + private Task RegisterAuthenticationAsync() { WebAssemblyHostBuilder.Services.AddAuthorizationCore(); WebAssemblyHostBuilder.Services.AddCascadingAuthenticationState(); diff --git a/Moonlight.Client/Startup/Startup.Base.cs b/Moonlight.Client/Startup/Startup.Base.cs index 407ac44b..6188d904 100644 --- a/Moonlight.Client/Startup/Startup.Base.cs +++ b/Moonlight.Client/Startup/Startup.Base.cs @@ -1,6 +1,5 @@ using Microsoft.Extensions.DependencyInjection; using MoonCore.Blazor.FlyonUi; -using MoonCore.Blazor.Services; using MoonCore.Extensions; using MoonCore.Helpers; using Moonlight.Client.Services; @@ -10,7 +9,7 @@ namespace Moonlight.Client.Startup; public partial class Startup { - private Task RegisterBase() + private Task RegisterBaseAsync() { WebAssemblyHostBuilder.RootComponents.Add("#app"); WebAssemblyHostBuilder.RootComponents.Add("head::after"); diff --git a/Moonlight.Client/Startup/Startup.Logging.cs b/Moonlight.Client/Startup/Startup.Logging.cs index 1b2a2714..f3ae38e3 100644 --- a/Moonlight.Client/Startup/Startup.Logging.cs +++ b/Moonlight.Client/Startup/Startup.Logging.cs @@ -7,7 +7,7 @@ namespace Moonlight.Client.Startup; public partial class Startup { - private Task SetupLogging() + private Task SetupLoggingAsync() { var loggerFactory = new LoggerFactory(); @@ -18,7 +18,7 @@ public partial class Startup return Task.CompletedTask; } - private Task RegisterLogging() + private Task RegisterLoggingAsync() { WebAssemblyHostBuilder.Logging.ClearProviders(); WebAssemblyHostBuilder.Logging.AddAnsiConsole(); diff --git a/Moonlight.Client/Startup/Startup.Misc.cs b/Moonlight.Client/Startup/Startup.Misc.cs index 38b3bd27..b93c48f0 100644 --- a/Moonlight.Client/Startup/Startup.Misc.cs +++ b/Moonlight.Client/Startup/Startup.Misc.cs @@ -6,7 +6,7 @@ namespace Moonlight.Client.Startup; public partial class Startup { - private Task PrintVersion() + private Task PrintVersionAsync() { // Fancy start console output... yes very fancy :> Console.Write("Running "); @@ -27,7 +27,7 @@ public partial class Startup return Task.CompletedTask; } - private async Task LoadConfiguration() + private async Task LoadConfigurationAsync() { try { diff --git a/Moonlight.Client/Startup/Startup.Plugins.cs b/Moonlight.Client/Startup/Startup.Plugins.cs index 2b97e7eb..128d11cb 100644 --- a/Moonlight.Client/Startup/Startup.Plugins.cs +++ b/Moonlight.Client/Startup/Startup.Plugins.cs @@ -10,7 +10,7 @@ public partial class Startup private IPluginStartup[] PluginStartups; private IServiceProvider PluginLoadServiceProvider; - private Task InitializePlugins() + private Task InitializePluginsAsync() { // Define minimal service collection var startupSc = new ServiceCollection(); @@ -38,13 +38,13 @@ public partial class Startup return Task.CompletedTask; } - private async Task HookPluginBuild() + private async Task HookPluginBuildAsync() { foreach (var pluginAppStartup in PluginStartups) { try { - await pluginAppStartup.BuildApplication(PluginLoadServiceProvider, WebAssemblyHostBuilder); + await pluginAppStartup.BuildApplicationAsync(PluginLoadServiceProvider, WebAssemblyHostBuilder); } catch (Exception e) { @@ -57,13 +57,13 @@ public partial class Startup } } - private async Task HookPluginConfigure() + private async Task HookPluginConfigureAsync() { foreach (var pluginAppStartup in PluginStartups) { try { - await pluginAppStartup.ConfigureApplication(PluginLoadServiceProvider, WebAssemblyHost); + await pluginAppStartup.ConfigureApplicationAsync(PluginLoadServiceProvider, WebAssemblyHost); } catch (Exception e) { diff --git a/Moonlight.Client/Startup/Startup.cs b/Moonlight.Client/Startup/Startup.cs index 08e0bab5..eb60f0d4 100644 --- a/Moonlight.Client/Startup/Startup.cs +++ b/Moonlight.Client/Startup/Startup.cs @@ -16,33 +16,33 @@ public partial class Startup public FrontendConfiguration Configuration { get; private set; } - public Task Initialize(IPluginStartup[]? plugins = null) + public Task InitializeAsync(IPluginStartup[]? plugins = null) { PluginStartups = plugins ?? []; return Task.CompletedTask; } - public async Task AddMoonlight(WebAssemblyHostBuilder builder) + public async Task AddMoonlightAsync(WebAssemblyHostBuilder builder) { WebAssemblyHostBuilder = builder; - await PrintVersion(); + await PrintVersionAsync(); - await SetupLogging(); - await LoadConfiguration(); - await InitializePlugins(); + await SetupLoggingAsync(); + await LoadConfigurationAsync(); + await InitializePluginsAsync(); - await RegisterLogging(); - await RegisterBase(); - await RegisterAuthentication(); - await HookPluginBuild(); + await RegisterLoggingAsync(); + await RegisterBaseAsync(); + await RegisterAuthenticationAsync(); + await HookPluginBuildAsync(); } - public async Task AddMoonlight(WebAssemblyHost assemblyHost) + public async Task AddMoonlightAsync(WebAssemblyHost assemblyHost) { WebAssemblyHost = assemblyHost; - await HookPluginConfigure(); + await HookPluginConfigureAsync(); } } \ No newline at end of file diff --git a/Moonlight.Client/Styles/MoonCore.Blazor.FlyonUi/mappings/mooncore.map b/Moonlight.Client/Styles/MoonCore.Blazor.FlyonUi/mappings/mooncore.map deleted file mode 100755 index 44e291f2..00000000 --- a/Moonlight.Client/Styles/MoonCore.Blazor.FlyonUi/mappings/mooncore.map +++ /dev/null @@ -1,518 +0,0 @@ -!bg-base-100 -!border-base-content/40 -!border-none -!flex -!font-medium -!font-semibold -!h-2.5 -!justify-between -!me-1.5 -!ms-auto -!px-2.5 -!rounded-full -!text-sm -!w-2.5 -*:[grid-area:1/1] -*:first:rounded-tl-lg -*:last:rounded-tr-lg --left-4 --ml-4 --translate-x-full --translate-y-1/2 -absolute -accordion -accordion-bordered -accordion-toggle -active -active-tab:bg-primary -active-tab:text-base-content -advance-select-menu -advance-select-option -advance-select-tag -advance-select-toggle -alert -alert-error -alert-outline -alert-soft -align-bottom -align-middle -animate-bounce -animate-ping -aria-[current='page']:text-bg-soft-primary -avatar -avatar-away-bottom -avatar-away-top -avatar-busy-bottom -avatar-busy-top -avatar-offline-bottom -avatar-offline-top -avatar-online-bottom -avatar-online-top -avatar-placeholder -badge -badge-error -badge-info -badge-outline -badge-primary -badge-soft -badge-success -bg-background -bg-background/60 -bg-base-100 -bg-base-150 -bg-base-200 -bg-base-200/50 -bg-base-300 -bg-base-300/45 -bg-base-300/50 -bg-base-300/60 -bg-error -bg-info -bg-primary -bg-primary/5 -bg-success -bg-transparent -bg-warning -block -blur -border -border-0 -border-2 -border-b -border-base-content -border-base-content/20 -border-base-content/25 -border-base-content/40 -border-base-content/5 -border-dashed -border-t -border-transparent -bottom-0 -bottom-full -break-words -btn -btn-accent -btn-active -btn-circle -btn-disabled -btn-error -btn-info -btn-outline -btn-primary -btn-secondary -btn-sm -btn-soft -btn-square -btn-success -btn-text -btn-warning -card -card-alert -card-body -card-border -card-footer -card-header -card-title -carousel -carousel-body -carousel-next -carousel-prev -carousel-slide -chat -chat-avatar -chat-bubble -chat-footer -chat-header -chat-receiver -chat-sender -checkbox -checkbox-primary -checkbox-xs -col-span-1 -collapse -combo-box-selected:block -combo-box-selected:dropdown-active -complete -container -contents -cursor-default -cursor-not-allowed -cursor-pointer -diff -disabled -divide-base-150/60 -divide-y -drop-shadow -dropdown -dropdown-disabled -dropdown-item -dropdown-menu -dropdown-open:opacity-100 -dropdown-open:rotate-180 -dropdown-toggle -duration-300 -duration-500 -ease-in-out -ease-linear -end-3 -file-upload-complete:progress-success -fill-black -filter -filter-reset -fixed -flex -flex-1 -flex-col -flex-grow -flex-nowrap -flex-row -flex-shrink-0 -flex-wrap -focus-visible:outline-none -focus-within:border-primary -focus:border-primary -focus:outline-1 -focus:outline-none -focus:outline-primary -focus:ring-0 -font-bold -font-inter -font-medium -font-normal -font-semibold -gap-0.5 -gap-1 -gap-1.5 -gap-2 -gap-3 -gap-4 -gap-5 -gap-6 -gap-x-1 -gap-x-2 -gap-x-3 -gap-y-1 -gap-y-3 -grid -grid-cols-1 -grid-flow-col -grow -grow-0 -h-12 -h-2 -h-32 -h-64 -h-8 -h-auto -h-full -h-screen -helper-text -hidden -hover:bg-primary/5 -hover:bg-transparent -hover:text-base-content -hover:text-base-content/60 -hover:text-primary -image-full -inline -inline-block -inline-flex -inline-grid -input -input-floating -input-floating-label -input-lg -input-md -input-sm -input-xl -inset-0 -inset-y-0 -inset-y-2 -invisible -is-invalid -is-valid -isolate -italic -items-center -items-end -items-start -join -join-item -justify-between -justify-center -justify-end -justify-start -justify-stretch -label-text -leading-3 -leading-3.5 -leading-6 -left-0 -lg:bg-base-100/20 -lg:flex -lg:gap-y-0 -lg:grid-cols-2 -lg:hidden -lg:justify-end -lg:justify-start -lg:min-w-0 -lg:p-10 -lg:pb-5 -lg:pl-64 -lg:pr-3.5 -lg:pt-5 -lg:ring-1 -lg:ring-base-content/10 -lg:rounded-lg -lg:shadow-xs -list-disc -list-inside -loading -loading-lg -loading-sm -loading-spinner -loading-xl -loading-xs -lowercase -m-10 -mask -max-h-52 -max-lg:flex-col -max-lg:hidden -max-w-7xl -max-w-80 -max-w-full -max-w-lg -max-w-sm -max-w-xl -mb-0.5 -mb-1 -mb-2 -mb-3 -mb-4 -mb-5 -md:table-cell -md:text-3xl -me-1 -me-1.5 -me-2 -me-2.5 -me-5 -menu -menu-active -menu-disabled -menu-dropdown -menu-dropdown-show -menu-focus -menu-horizontal -menu-title -min-h-0 -min-h-svh -min-w-0 -min-w-28 -min-w-48 -min-w-60 -min-w-[100px] -ml-3 -ml-4 -modal -modal-content -modal-dialog -modal-middle -modal-title -mr-4 -ms-1 -ms-2 -ms-3 -mt-1 -mt-1.5 -mt-10 -mt-12 -mt-2 -mt-2.5 -mt-3 -mt-4 -mt-5 -mt-8 -mx-1 -mx-auto -my-3 -my-auto -opacity-0 -opacity-100 -open -origin-top-left -outline -outline-0 -overflow-hidden -overflow-x-auto -overflow-y-auto -overlay-open:duration-50 -overlay-open:opacity-100 -p-0.5 -p-1 -p-2 -p-3 -p-4 -p-5 -p-6 -p-8 -pin-input -pin-input-underline -placeholder-base-content/60 -pointer-events-auto -pointer-events-none -progress -progress-bar -progress-indeterminate -progress-primary -pt-0 -pt-0.5 -pt-3 -px-1.5 -px-2 -px-2.5 -px-3 -px-4 -px-5 -py-0.5 -py-1.5 -py-2 -py-2.5 -py-6 -radio -range -relative -resize -ring-0 -ring-1 -ring-white/10 -rounded-box -rounded-field -rounded-full -rounded-lg -rounded-md -rounded-t-lg -row-active -row-hover -rtl:!mr-0 -select -select-disabled:opacity-40 -select-disabled:pointer-events-none -select-floating -select-floating-label -selected -selected:select-active -shadow-base-300/20 -shadow-lg -shadow-xs -shrink-0 -size-10 -size-4 -size-5 -size-8 -skeleton -skeleton-animated -sm:auto-cols-max -sm:flex -sm:items-center -sm:items-end -sm:justify-between -sm:justify-end -sm:max-w-2xl -sm:max-w-3xl -sm:max-w-4xl -sm:max-w-5xl -sm:max-w-6xl -sm:max-w-7xl -sm:max-w-lg -sm:max-w-md -sm:max-w-xl -sm:mb-0 -sm:mt-5 -sm:mt-6 -sm:p-6 -sm:py-2 -sm:text-sm/5 -space-x-1 -space-y-1 -space-y-4 -sr-only -static -status -status-error -sticky -switch -tab -tab-active -table -table-pin-cols -table-pin-rows -tabs -tabs-bordered -tabs-lg -tabs-lifted -tabs-md -tabs-sm -tabs-xl -tabs-xs -text-2xl -text-4xl -text-accent -text-base -text-base-content -text-base-content/40 -text-base-content/50 -text-base-content/60 -text-base-content/70 -text-base-content/80 -text-base/6 -text-center -text-error -text-error-content -text-gray-400 -text-info -text-info-content -text-left -text-lg -text-primary -text-primary-content -text-sm -text-sm/5 -text-success -text-success-content -text-warning -text-warning-content -text-xl -text-xs -text-xs/5 -textarea -textarea-floating -textarea-floating-label -theme-controller -tooltip -tooltip-content -top-0 -top-1/2 -top-full -transform -transition -transition-all -transition-opacity -translate-x-0 -truncate -underline -uppercase -validate -w-0 -w-0.5 -w-12 -w-4 -w-56 -w-64 -w-fit -w-full -whitespace-nowrap -z-10 -z-40 -z-50 \ No newline at end of file diff --git a/Moonlight.Client/Styles/MoonCore.Blazor.FlyonUi/mooncore.map b/Moonlight.Client/Styles/MoonCore.Blazor.FlyonUi/mooncore.map index 67f69d38..691427a6 100755 --- a/Moonlight.Client/Styles/MoonCore.Blazor.FlyonUi/mooncore.map +++ b/Moonlight.Client/Styles/MoonCore.Blazor.FlyonUi/mooncore.map @@ -21,6 +21,8 @@ -ml-4 -translate-x-full -translate-y-1/2 +[animation-duration:0.8s] +[animation-timing-function:ease] absolute accordion accordion-bordered @@ -42,8 +44,10 @@ align-bottom align-middle animate-bounce animate-ping +animate-spin aria-[current='page']:text-bg-soft-primary avatar +avatar-placeholder badge badge-error badge-info @@ -73,18 +77,33 @@ block blur border border-0 +border-1 border-2 +border-3 border-b +border-b-2 +border-b-base-content/20 +border-b-primary border-base-content border-base-content/20 border-base-content/25 border-base-content/40 border-base-content/5 border-dashed +border-dotted +border-error/30 +border-info/30 +border-l-transparent +border-r-transparent +border-solid +border-success/30 border-t +border-t-transparent border-transparent +border-warning/30 bottom-0 bottom-full +breadcrumbs break-words btn btn-accent @@ -93,7 +112,6 @@ btn-circle btn-disabled btn-error btn-info -btn-outline btn-primary btn-secondary btn-sm @@ -153,6 +171,7 @@ duration-500 ease-in-out ease-linear end-3 +error-message file-upload-complete:progress-success fill-base-content fill-black @@ -180,6 +199,8 @@ font-inter font-medium font-normal font-semibold +footer +footer-center gap-0.5 gap-1 gap-1.5 @@ -200,7 +221,7 @@ grid-cols-4 grid-flow-col grow grow-0 -h-12 +h-10 h-2 h-3 h-32 @@ -213,10 +234,13 @@ helper-text hidden hover:bg-primary/5 hover:bg-transparent +hover:cursor-pointer hover:text-base-content hover:text-base-content/60 hover:text-primary image-full +indicator +indicator-item inline inline-block inline-flex @@ -245,11 +269,9 @@ justify-between justify-center justify-end justify-start -justify-stretch label-text leading-3 leading-3.5 -leading-6 leading-none left-0 lg:bg-base-100/20 @@ -276,7 +298,6 @@ list-disc list-inside list-none loading -loading-lg loading-sm loading-spinner loading-xl @@ -287,7 +308,11 @@ mask max-h-52 max-lg:flex-col max-lg:hidden +max-md:flex-wrap +max-md:justify-center +max-sm:hidden max-w-7xl +max-w-8 max-w-80 max-w-full max-w-lg @@ -323,6 +348,7 @@ min-w-28 min-w-48 min-w-60 min-w-[100px] +min-w-full min-w-sm ml-3 ml-4 @@ -330,10 +356,10 @@ modal modal-content modal-dialog modal-middle -modal-title mr-4 ms-0.5 ms-1 +ms-1.5 ms-2 ms-3 ms-auto @@ -351,10 +377,12 @@ mt-8 mx-1 mx-auto my-3 +my-5 my-auto object-cover opacity-0 opacity-100 +opacity-75 open origin-top-left outline @@ -368,11 +396,13 @@ p-0.5 p-1 p-1.5 p-2 +p-2.5 p-3 p-4 p-5 p-6 p-8 +pb-1 pin-input placeholder-base-content/60 pointer-events-auto @@ -383,6 +413,7 @@ progress-indeterminate progress-primary pt-0 pt-0.5 +pt-2 pt-3 px-1.5 px-2 @@ -418,6 +449,7 @@ select-disabled:opacity-40 select-disabled:pointer-events-none select-floating select-floating-label +select-sm selected selected:select-active shadow-base-300/20 @@ -426,6 +458,7 @@ shadow-md shadow-xs shrink-0 size-10 +size-12 size-4 size-5 size-8 @@ -448,18 +481,20 @@ sm:max-w-md sm:max-w-xl sm:mb-0 sm:mt-5 -sm:mt-6 sm:p-6 sm:py-2 sm:text-sm/5 space-x-1 +space-x-2.5 space-y-1 space-y-4 sr-only +stack static status status-error sticky +success-message switch tab tab-active @@ -495,6 +530,7 @@ text-left text-lg text-primary text-primary-content +text-right text-sm text-sm/5 text-success @@ -525,6 +561,7 @@ validate w-0 w-0.5 w-12 +w-13 w-4 w-56 w-64 diff --git a/Moonlight.Client/Styles/mappings/classes.map b/Moonlight.Client/Styles/mappings/classes.map index a6584dd3..599f38f7 100644 --- a/Moonlight.Client/Styles/mappings/classes.map +++ b/Moonlight.Client/Styles/mappings/classes.map @@ -9,7 +9,9 @@ !me-1.5 !ms-auto !px-2.5 +!py-0.5 !rounded-full +!rounded-xs !text-sm !w-2.5 *:[grid-area:1/1] @@ -19,13 +21,17 @@ -ml-4 -translate-x-full -translate-y-1/2 +[animation-duration:0.8s] +[animation-timing-function:ease] absolute accordion accordion-bordered accordion-toggle active active-tab:bg-primary +active-tab:hover:text-primary-content active-tab:text-base-content +active-tab:text-primary-content advance-select-menu advance-select-option advance-select-tag @@ -39,6 +45,7 @@ align-bottom align-middle animate-bounce animate-ping +animate-spin aria-[current='page']:text-bg-soft-primary avatar avatar-away-bottom @@ -62,6 +69,7 @@ bg-background/60 bg-base-100 bg-base-150 bg-base-200 +bg-base-200! bg-base-200/50 bg-base-300 bg-base-300/45 @@ -85,9 +93,14 @@ block blur border border-0 +border-1 border-2 +border-3 border-accent border-b +border-b-2 +border-b-base-content/20 +border-b-primary border-base-content border-base-content/20 border-base-content/25 @@ -96,15 +109,26 @@ border-base-content/5 border-base-content/60 border-base-content/70 border-dashed +border-dotted border-e-2 +border-error/30 +border-info/30 border-l-4 +border-l-transparent border-primary +border-r-transparent +border-solid border-success +border-success/30 border-t border-t-2 +border-t-transparent border-transparent +border-warning/30 bottom-0 bottom-full +breadcrumbs +breadcrumbs-separator break-words btn btn-accent @@ -164,8 +188,10 @@ diff disabled divide-base-150/60 divide-y +divider drop-shadow dropdown +dropdown-active dropdown-disabled dropdown-item dropdown-menu @@ -179,7 +205,9 @@ ease-linear end-3 error-message file-upload-complete:progress-success +fill-base-content fill-black +fill-gray-200 filter filter-reset fixed @@ -210,6 +238,8 @@ font-inter font-medium font-normal font-semibold +footer +footer-center from-violet-400 gap-0.5 gap-1 @@ -226,6 +256,7 @@ gap-x-6 gap-y-1 gap-y-1.5 gap-y-2 +gap-y-2.5 gap-y-3 gap-y-8 grid @@ -233,10 +264,12 @@ grid-cols-1 grid-cols-12 grid-cols-2 grid-cols-3 +grid-cols-4 grid-flow-col grow grow-0 h-0 +h-10 h-12 h-14 h-2 @@ -253,11 +286,14 @@ hidden hover:bg-indigo-500 hover:bg-primary/5 hover:bg-transparent +hover:cursor-pointer hover:text-base-content hover:text-base-content/60 hover:text-indigo-500 hover:text-primary image-full +indicator +indicator-item inline inline-block inline-flex @@ -294,6 +330,7 @@ leading-3.5 leading-6 leading-9 leading-[1.1] +leading-none left-0 lg:bg-base-100/20 lg:flex @@ -320,6 +357,7 @@ link-hover link-primary list-disc list-inside +list-none loading loading-lg loading-sm @@ -332,7 +370,11 @@ mask max-h-52 max-lg:flex-col max-lg:hidden +max-md:flex-wrap +max-md:justify-center +max-sm:hidden max-w-7xl +max-w-8 max-w-80 max-w-full max-w-lg @@ -376,6 +418,7 @@ min-w-28 min-w-48 min-w-60 min-w-[100px] +min-w-full min-w-sm mix-blend-exclusion ml-3 @@ -387,10 +430,13 @@ modal-middle modal-title mr-2 mr-4 +ms-0.5 ms-1 +ms-1.5 ms-2 ms-2.5 ms-3 +ms-auto mt-1 mt-1.5 mt-10 @@ -398,6 +444,7 @@ mt-12 mt-2 mt-2.5 mt-3 +mt-3.5 mt-4 mt-5 mt-6 @@ -407,9 +454,12 @@ mx-auto my-2.5 my-3 my-5 +my-8 my-auto +object-cover opacity-0 opacity-100 +opacity-75 open origin-top-left outline @@ -430,6 +480,7 @@ p-4 p-5 p-6 p-8 +pb-1 pe-1.5 pin-input pin-input-underline @@ -444,6 +495,7 @@ progress-primary pt-0 pt-0.5 pt-1.5 +pt-2 pt-3 px-1.5 px-2 @@ -459,10 +511,12 @@ py-12 py-2 py-2.5 py-6 +radial-progress radio range relative resize +ring ring-0 ring-1 ring-gray-700 @@ -543,10 +597,12 @@ sm:text-sm/5 sm:w-1/2 sm:w-full space-x-1 +space-x-2.5 space-y-1 space-y-4 space-y-6 sr-only +stack stat stat-actions stat-desc @@ -602,6 +658,7 @@ text-left text-lg text-primary text-primary-content +text-right text-slate-100 text-sm text-sm/5 @@ -645,6 +702,7 @@ via-sky-400 w-0 w-0.5 w-12 +w-13 w-32 w-4 w-56 diff --git a/Moonlight.Client/UI/Components/ColorSelector.razor b/Moonlight.Client/UI/Components/ColorSelector.razor index b86e5f00..f3ba0c60 100644 --- a/Moonlight.Client/UI/Components/ColorSelector.razor +++ b/Moonlight.Client/UI/Components/ColorSelector.razor @@ -1,7 +1,7 @@ - + @code { @@ -37,7 +37,7 @@ Id = $"color-selector-{GetHashCode()}"; } - private async Task Update(ChangeEventArgs args) + private async Task UpdateAsync(ChangeEventArgs args) { Value = args.Value?.ToString() ?? "#FFFFFF"; await InvokeAsync(StateHasChanged); diff --git a/Moonlight.Client/UI/Components/SignalRDebug.razor b/Moonlight.Client/UI/Components/SignalRDebug.razor index 270b2bb1..807e3b3f 100644 --- a/Moonlight.Client/UI/Components/SignalRDebug.razor +++ b/Moonlight.Client/UI/Components/SignalRDebug.razor @@ -17,7 +17,7 @@ for all SignalR Hubs to be synced.

- + Send broadcast @@ -30,9 +30,9 @@ { private HubConnection? Connection; - private async Task Load(LazyLoader lazyLoader) + private async Task LoadAsync(LazyLoader lazyLoader) { - await lazyLoader.UpdateText("Connecting to SignalR endpoint"); + await lazyLoader.UpdateTextAsync("Connecting to SignalR endpoint"); Connection = new HubConnectionBuilder() .WithUrl(Navigation.ToAbsoluteUri("/api/admin/system/diagnose/ws")) @@ -41,7 +41,7 @@ Connection.On( "Pong", - async () => await ToastService.Success("Received broadcast") + async () => await ToastService.SuccessAsync("Received broadcast") ); await Connection.StartAsync(); diff --git a/Moonlight.Client/UI/Components/ThemeEditor.razor b/Moonlight.Client/UI/Components/ThemeEditor.razor index a7b8e7ea..25ee2098 100644 --- a/Moonlight.Client/UI/Components/ThemeEditor.razor +++ b/Moonlight.Client/UI/Components/ThemeEditor.razor @@ -137,7 +137,7 @@ } else { - + } @@ -121,7 +121,7 @@
Moonlight v2.1
- @@ -180,7 +180,7 @@
+ href="#" @onclick:preventDefault @onclick="LogoutAsync"> Logout @@ -252,13 +252,13 @@ if (!Layout.ShowMobileNavigation) return; - await Layout.ToggleMobileNavigation(); + await Layout.ToggleMobileNavigationAsync(); }; return Task.CompletedTask; } - private Task Logout() + private Task LogoutAsync() { Navigation.NavigateTo("/api/auth/logout", true); return Task.CompletedTask; diff --git a/Moonlight.Client/UI/Partials/LoginSelector.razor b/Moonlight.Client/UI/Partials/LoginSelector.razor index 35e09a36..559a220a 100644 --- a/Moonlight.Client/UI/Partials/LoginSelector.razor +++ b/Moonlight.Client/UI/Partials/LoginSelector.razor @@ -7,7 +7,7 @@
- + @if (ShowSelection) {
@@ -34,7 +34,7 @@ if (config == null) // Ignore all schemes which have no ui configured continue; -
- + + + + + + @Formatter.FormatDate(context.ExpiresAt.UtcDateTime) + + + + + @Formatter.FormatDate(context.CreatedAt.UtcDateTime) + + + + +
+ + + - - - - - - - - - @(Formatter.FormatDate(context.ExpiresAt.UtcDateTime)) - - - - -
- - - - - - - -
-
-
-
-
+ + + +
+ +
+ + + + Create + + +
@code { - private DataTable Table; + private DataGrid Grid; - private async Task> LoadData(PaginationOptions options) - => await ApiClient.GetJson>($"api/admin/apikeys?page={options.Page}&pageSize={options.PerPage}"); - - private async Task Delete(ApiKeyResponse apiKeyResponse) + private async Task> ItemsProviderAsync(DataGridItemRequest request) { - await AlertService.ConfirmDanger( - "API Key deletion", + var query = $"?startIndex={request.StartIndex}&count={request.Count}"; + + if (!string.IsNullOrEmpty(request.SortColumn)) + { + var dir = request.SortDirection == SortState.Descending ? "desc" : "asc"; + query += $"&orderBy={request.SortColumn}&orderByDir={dir}"; + } + + if (!string.IsNullOrEmpty(request.Filter)) + query += $"&filter={request.Filter}"; + + var data = await ApiClient.GetJson>($"api/admin/apikeys{query}"); + + return new() + { + Items = data.Items, + TotalCount = data.TotalCount + }; + } + + private async Task DeleteAsync(ApiKeyResponse apiKeyResponse) + { + await AlertService.ConfirmDangerAsync( + "API Key Deletion", $"Do you really want to delete the api key '{apiKeyResponse.Description}'", async () => { await ApiClient.Delete($"api/admin/apikeys/{apiKeyResponse.Id}"); - await ToastService.Success("Successfully deleted api key"); + await ToastService.SuccessAsync("Successfully deleted api key"); - await Table.Refresh(); + await Grid.RefreshAsync(); } ); } diff --git a/Moonlight.Client/UI/Views/Admin/Api/Update.razor b/Moonlight.Client/UI/Views/Admin/Api/Update.razor index 27ba1786..86130647 100644 --- a/Moonlight.Client/UI/Views/Admin/Api/Update.razor +++ b/Moonlight.Client/UI/Views/Admin/Api/Update.razor @@ -7,13 +7,13 @@ @inject NavigationManager Navigation @inject ToastService ToastService - + Back - + Update @@ -40,7 +40,7 @@ private HandleForm Form; private UpdateApiKeyRequest Request; - private async Task Load(LazyLoader _) + private async Task LoadAsync(LazyLoader _) { var detail = await ApiClient.GetJson($"api/admin/apikeys/{Id}"); @@ -54,7 +54,7 @@ { await ApiClient.Patch($"api/admin/apikeys/{Id}", Request); - await ToastService.Success("Successfully updated api key"); + await ToastService.SuccessAsync("Successfully updated api key"); Navigation.NavigateTo("/admin/api"); } } \ No newline at end of file diff --git a/Moonlight.Client/UI/Views/Admin/Sys/Advanced.razor b/Moonlight.Client/UI/Views/Admin/Sys/Advanced.razor index 66b5c5fa..77aa45ad 100644 --- a/Moonlight.Client/UI/Views/Admin/Sys/Advanced.razor +++ b/Moonlight.Client/UI/Views/Admin/Sys/Advanced.razor @@ -26,17 +26,17 @@ includes your installed theme and plugins. For more information, have a look at our docs

- Generate frontend.zip + Generate frontend.zip
@code { - private async Task GenerateFrontend(WButton _) + private async Task GenerateFrontendAsync(WButton _) { var stream = await ApiClient.GetStream("api/admin/system/advanced/frontend"); - await DownloadService.Download("frontend.zip", stream); + await DownloadService.DownloadAsync("frontend.zip", stream); } } diff --git a/Moonlight.Client/UI/Views/Admin/Sys/Customisation/Index.razor b/Moonlight.Client/UI/Views/Admin/Sys/Customisation/Index.razor index f2544cbb..615f6fd6 100644 --- a/Moonlight.Client/UI/Views/Admin/Sys/Customisation/Index.razor +++ b/Moonlight.Client/UI/Views/Admin/Sys/Customisation/Index.razor @@ -2,7 +2,9 @@ @using System.Text.Json @using Microsoft.AspNetCore.Authorization -@using MoonCore.Blazor.FlyonUi.DataTables +@using MoonCore.Blazor.FlyonUi.Grid +@using MoonCore.Blazor.FlyonUi.Grid.Columns +@using MoonCore.Blazor.FlyonUi.Grid.ToolbarItems @using MoonCore.Blazor.FlyonUi.Helpers @using MoonCore.Helpers @using MoonCore.Models @@ -16,6 +18,7 @@ @inject ThemeService ThemeService @inject AlertService AlertService @inject ToastService ToastService +@inject HttpApiClient ApiClient @inject DownloadService DownloadService @inject ILogger Logger @@ -25,72 +28,72 @@ Themes -
-
- -
-
+
+ + + + + +
+ @context.Name -
- - - - - -
- @context.Name - - @if (context.IsEnabled) - { - - } -
-
-
- - - - -
- @if (!string.IsNullOrEmpty(context.DonateUrl)) - { - - - Donate - - } - - @if (!string.IsNullOrEmpty(context.UpdateUrl)) - { - - - Update - - } - - - - Export - - - - + @if (context.IsEnabled) + { + + } +
+ + + + + + + + -
-
- -
-
+ } + + + + Export + + + + + + + + + +
+ + + + + + +
@@ -100,12 +103,31 @@ @code { - private DataTable Table; + private DataGrid Grid; - private async Task> LoadItems(PaginationOptions options) - => await ThemeService.Get(options.Page, options.PerPage); + private async Task> ItemsProviderAsync(DataGridItemRequest request) + { + var query = $"?startIndex={request.StartIndex}&count={request.Count}"; + + if (!string.IsNullOrEmpty(request.SortColumn)) + { + var dir = request.SortDirection == SortState.Descending ? "desc" : "asc"; + query += $"&orderBy={request.SortColumn}&orderByDir={dir}"; + } + + if (!string.IsNullOrEmpty(request.Filter)) + query += $"&filter={request.Filter}"; + + var data = await ApiClient.GetJson>($"api/admin/system/customisation/themes{query}"); + + return new() + { + Items = data.Items, + TotalCount = data.TotalCount + }; + } - private async Task Import(InputFileChangeEventArgs eventArgs) + private async Task ImportAsync(InputFileChangeEventArgs eventArgs) { if(eventArgs.FileCount < 1) return; @@ -120,13 +142,13 @@ { if (!file.Name.EndsWith(".json")) { - await ToastService.Error($"Unable to import {file.Name}", "Only .json files are supported"); + await ToastService.ErrorAsync($"Unable to import {file.Name}", "Only .json files are supported"); continue; } if (file.Size > maxFileSize) { - await ToastService.Error($"Unable to import {file.Name}", "Exceeded the maximum file limit of 1MB"); + await ToastService.ErrorAsync($"Unable to import {file.Name}", "Exceeded the maximum file limit of 1MB"); continue; } @@ -137,11 +159,11 @@ if (themeTransfer == null) { - await ToastService.Error($"Unable to import {file.Name}", "Failed to deserialize the content"); + await ToastService.ErrorAsync($"Unable to import {file.Name}", "Failed to deserialize the content"); continue; } - var theme = await ThemeService.Create(new CreateThemeRequest() + var theme = await ThemeService.CreateAsync(new CreateThemeRequest() { Name = themeTransfer.Name, Author = themeTransfer.Author, @@ -151,9 +173,9 @@ Version = themeTransfer.Version }); - await ToastService.Success("Successfully imported theme", theme.Name); + await ToastService.SuccessAsync("Successfully imported theme", theme.Name); - await Table.Refresh(); + await Grid.RefreshAsync(); } catch (Exception e) { @@ -162,7 +184,7 @@ } } - private async Task Export(ThemeResponse theme) + private async Task ExportAsync(ThemeResponse theme) { var transfer = new ThemeTransferModel() { @@ -181,20 +203,20 @@ var fileName = $"{transfer.Name.Replace(" ", string.Empty).Trim()}.json"; - await DownloadService.Download(fileName, json); + await DownloadService.DownloadAsync(fileName, json); } - private async Task Delete(ThemeResponse response) + private async Task DeleteAsync(ThemeResponse response) { - await AlertService.ConfirmDanger( + await AlertService.ConfirmDangerAsync( "Theme deletion", $"Do you really want to delete the theme: {response.Name}", async () => { - await ThemeService.Delete(response.Id); + await ThemeService.DeleteAsync(response.Id); - await ToastService.Success("Successfully deleted theme"); - await Table.Refresh(); + await ToastService.SuccessAsync("Successfully deleted theme"); + await Grid.RefreshAsync(); } ); } diff --git a/Moonlight.Client/UI/Views/Admin/Sys/Customisation/Themes/Create.razor b/Moonlight.Client/UI/Views/Admin/Sys/Customisation/Themes/Create.razor index a4131705..11296a88 100644 --- a/Moonlight.Client/UI/Views/Admin/Sys/Customisation/Themes/Create.razor +++ b/Moonlight.Client/UI/Views/Admin/Sys/Customisation/Themes/Create.razor @@ -14,7 +14,7 @@ Back - + Create @@ -58,8 +58,8 @@ private async Task OnValidSubmit() { - await ThemeService.Create(Request); - await ToastService.Success("Successfully created theme"); + await ThemeService.CreateAsync(Request); + await ToastService.SuccessAsync("Successfully created theme"); NavigationManager.NavigateTo("/admin/system/customisation"); } diff --git a/Moonlight.Client/UI/Views/Admin/Sys/Customisation/Themes/Update.razor b/Moonlight.Client/UI/Views/Admin/Sys/Customisation/Themes/Update.razor index faa9c860..928646dd 100644 --- a/Moonlight.Client/UI/Views/Admin/Sys/Customisation/Themes/Update.razor +++ b/Moonlight.Client/UI/Views/Admin/Sys/Customisation/Themes/Update.razor @@ -9,13 +9,13 @@ @inject ToastService ToastService @inject NavigationManager Navigation - + Back - + Update @@ -80,9 +80,9 @@ private HandleForm Form; - private async Task Load(LazyLoader _) + private async Task LoadAsync(LazyLoader _) { - Response = await ThemeService.Get(Id); + Response = await ThemeService.GetAsync(Id); Request = new() { @@ -98,9 +98,9 @@ private async Task OnValidSubmit() { - await ThemeService.Update(Id, Request); + await ThemeService.UpdateAsync(Id, Request); - await ToastService.Success("Successfully updated theme"); + await ToastService.SuccessAsync("Successfully updated theme"); Navigation.NavigateTo("/admin/system/customisation"); } } diff --git a/Moonlight.Client/UI/Views/Admin/Sys/Diagnose.razor b/Moonlight.Client/UI/Views/Admin/Sys/Diagnose.razor index 911f7037..4f3a2dbc 100644 --- a/Moonlight.Client/UI/Views/Admin/Sys/Diagnose.razor +++ b/Moonlight.Client/UI/Views/Admin/Sys/Diagnose.razor @@ -28,11 +28,11 @@ If you only want to export specific parts of the diagnose report, click on "Advanced" and select the desired providers

- Generate report + Generate report
+ @onclick="ToggleDropDownAsync"> Advanced @if (DropdownOpen) { @@ -45,7 +45,7 @@
- +
@@ -86,7 +86,7 @@ } } - private async Task Load(LazyLoader arg) + private async Task LoadAsync(LazyLoader arg) { var providers = await ApiClient.GetJson( "api/admin/system/diagnose/providers" @@ -96,7 +96,7 @@ .ToDictionary(x => x, _ => true); } - private async Task GenerateDiagnose(WButton _) + private async Task GenerateDiagnoseAsync(WButton button) { var request = new GenerateDiagnoseRequest(); @@ -111,11 +111,11 @@ var stream = await ApiClient.PostStream("api/admin/system/diagnose", request); - await DownloadService.Download("diagnose.zip", stream); + await DownloadService.DownloadAsync("diagnose.zip", stream); } - private async Task ToggleDropDown() + private async Task ToggleDropDownAsync() { DropdownOpen = !DropdownOpen; await InvokeAsync(StateHasChanged); diff --git a/Moonlight.Client/UI/Views/Admin/Sys/Hangfire.razor b/Moonlight.Client/UI/Views/Admin/Sys/Hangfire.razor index 9d6d31ec..e05e3bac 100644 --- a/Moonlight.Client/UI/Views/Admin/Sys/Hangfire.razor +++ b/Moonlight.Client/UI/Views/Admin/Sys/Hangfire.razor @@ -19,7 +19,7 @@
- +
@@ -39,7 +39,7 @@ { private HangfireStatsResponse Stats; - private async Task Load(LazyLoader _) + private async Task LoadAsync(LazyLoader _) { Stats = await ApiClient.GetJson( "api/admin/system/hangfire/stats" diff --git a/Moonlight.Client/UI/Views/Admin/Sys/Index.razor b/Moonlight.Client/UI/Views/Admin/Sys/Index.razor index f60a062a..23d82185 100644 --- a/Moonlight.Client/UI/Views/Admin/Sys/Index.razor +++ b/Moonlight.Client/UI/Views/Admin/Sys/Index.razor @@ -13,7 +13,7 @@
- +
@@ -28,7 +28,7 @@
- + Restart/Shutdown @@ -41,12 +41,12 @@ { private SystemOverviewResponse OverviewData; - private async Task LoadOverview(LazyLoader arg) + private async Task LoadOverviewAsync(LazyLoader arg) { OverviewData = await ApiClient.GetJson("api/admin/system"); } - private async Task Restart(WButton _) + private async Task RestartAsync(WButton _) { await ApiClient.Post("api/admin/system/shutdown"); } diff --git a/Moonlight.Client/UI/Views/Admin/Users/Create.razor b/Moonlight.Client/UI/Views/Admin/Users/Create.razor index a08c8183..1377e908 100644 --- a/Moonlight.Client/UI/Views/Admin/Users/Create.razor +++ b/Moonlight.Client/UI/Views/Admin/Users/Create.razor @@ -12,7 +12,7 @@ Back - + Create @@ -67,7 +67,7 @@ await ApiClient.Post("api/admin/users", Request); - await ToastService.Success("Successfully created user"); + await ToastService.SuccessAsync("Successfully created user"); Navigation.NavigateTo("/admin/users"); } } \ No newline at end of file diff --git a/Moonlight.Client/UI/Views/Admin/Users/Index.razor b/Moonlight.Client/UI/Views/Admin/Users/Index.razor index 11b13a2e..4c71edea 100644 --- a/Moonlight.Client/UI/Views/Admin/Users/Index.razor +++ b/Moonlight.Client/UI/Views/Admin/Users/Index.razor @@ -3,13 +3,14 @@ @using MoonCore.Helpers @using MoonCore.Models @using Moonlight.Shared.Http.Responses.Admin.Users -@using MoonCore.Blazor.FlyonUi.DataTables +@using MoonCore.Blazor.FlyonUi.Grid +@using MoonCore.Blazor.FlyonUi.Grid.Columns @inject HttpApiClient ApiClient @inject AlertService AlertService @inject ToastService ToastService -
+ - - - - - - - - - -
- - - + + + + + + + + - - - - + + + +
+ + + @code { - private DataTable Table; + private DataGrid Grid; - private async Task> LoadData(PaginationOptions options) - => await ApiClient.GetJson>($"api/admin/users?page={options.Page}&pageSize={options.PerPage}"); - - private async Task Delete(UserResponse response) + private async Task> ItemsProviderAsync(DataGridItemRequest request) { - await AlertService.ConfirmDanger( + var query = $"?startIndex={request.StartIndex}&count={request.Count}"; + + if (!string.IsNullOrEmpty(request.SortColumn)) + { + var dir = request.SortDirection == SortState.Descending ? "desc" : "asc"; + query += $"&orderBy={request.SortColumn}&orderByDir={dir}"; + } + + if (!string.IsNullOrEmpty(request.Filter)) + query += $"&filter={request.Filter}"; + + var data = await ApiClient.GetJson>($"api/admin/users{query}"); + + return new() + { + Items = data.Items, + TotalCount = data.TotalCount + }; + } + + private async Task DeleteAsync(UserResponse response) + { + await AlertService.ConfirmDangerAsync( "User deletion", $"Do you really want to delete the user '{response.Username}'", async () => { await ApiClient.Delete($"api/admin/users/{response.Id}"); - await ToastService.Success("Successfully deleted user"); + await ToastService.SuccessAsync("Successfully deleted user"); - await Table.Refresh(); + await Grid.RefreshAsync(); } ); } diff --git a/Moonlight.Client/UI/Views/Admin/Users/Update.razor b/Moonlight.Client/UI/Views/Admin/Users/Update.razor index 67faadd1..39b8db35 100644 --- a/Moonlight.Client/UI/Views/Admin/Users/Update.razor +++ b/Moonlight.Client/UI/Views/Admin/Users/Update.razor @@ -8,13 +8,13 @@ @inject NavigationManager Navigation @inject ToastService ToastService - + Back - + Update @@ -62,7 +62,7 @@ private List Permissions = []; - private async Task Load(LazyLoader _) + private async Task LoadAsync(LazyLoader _) { var detail = await ApiClient.GetJson($"api/admin/users/{Id}"); @@ -82,7 +82,7 @@ await ApiClient.Patch($"api/admin/users/{Id}", Request); - await ToastService.Success("Successfully updated user"); + await ToastService.SuccessAsync("Successfully updated user"); Navigation.NavigateTo("/admin/users"); } } \ No newline at end of file diff --git a/Moonlight.Shared/Http/Responses/Admin/ApiKeys/ApiKeyResponse.cs b/Moonlight.Shared/Http/Responses/Admin/ApiKeys/ApiKeyResponse.cs index 28fe84a5..f30ae182 100644 --- a/Moonlight.Shared/Http/Responses/Admin/ApiKeys/ApiKeyResponse.cs +++ b/Moonlight.Shared/Http/Responses/Admin/ApiKeys/ApiKeyResponse.cs @@ -6,4 +6,5 @@ public class ApiKeyResponse public string Description { get; set; } public string[] Permissions { get; set; } = []; public DateTimeOffset ExpiresAt { get; set; } + public DateTimeOffset CreatedAt { get; set; } } \ No newline at end of file diff --git a/Moonlight.Shared/Moonlight.Shared.csproj b/Moonlight.Shared/Moonlight.Shared.csproj index b5246fec..83079495 100644 --- a/Moonlight.Shared/Moonlight.Shared.csproj +++ b/Moonlight.Shared/Moonlight.Shared.csproj @@ -9,7 +9,7 @@ Moonlight.Shared shared Moonlight.Shared - 2.1.10 + 2.1.11 Moonlight Panel A build of the shared classes for moonlight development https://github.com/Moonlight-Panel/Moonlight