diff --git a/Moonlight.ApiServer/Database/CoreDataContext.cs b/Moonlight.ApiServer/Database/CoreDataContext.cs index 447f716a..63f17ea0 100644 --- a/Moonlight.ApiServer/Database/CoreDataContext.cs +++ b/Moonlight.ApiServer/Database/CoreDataContext.cs @@ -1,5 +1,4 @@ -using Hangfire.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; using MoonCore.Extended.SingleDb; using Moonlight.ApiServer.Configuration; using Moonlight.ApiServer.Database.Entities; @@ -30,7 +29,6 @@ public class CoreDataContext : DatabaseContext protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); - modelBuilder.OnHangfireModelCreating(); modelBuilder.Ignore(); modelBuilder.Entity() diff --git a/Moonlight.ApiServer/Database/Migrations/20250819195904_RemovedHangfire.Designer.cs b/Moonlight.ApiServer/Database/Migrations/20250819195904_RemovedHangfire.Designer.cs new file mode 100644 index 00000000..315b30ec --- /dev/null +++ b/Moonlight.ApiServer/Database/Migrations/20250819195904_RemovedHangfire.Designer.cs @@ -0,0 +1,260 @@ +// +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("20250819195904_RemovedHangfire")] + partial class RemovedHangfire + { + /// + 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("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.Theme", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Author") + .IsRequired() + .HasColumnType("text"); + + b.Property("DonateUrl") + .HasColumnType("text"); + + b.Property("IsEnabled") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdateUrl") + .HasColumnType("text"); + + b.Property("Version") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Core_Themes", (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("Moonlight.ApiServer.Database.Entities.Theme", b => + { + b.OwnsOne("Moonlight.ApiServer.Models.ApplicationTheme", "Content", b1 => + { + b1.Property("ThemeId") + .HasColumnType("integer"); + + b1.Property("Border") + .HasColumnType("real"); + + b1.Property("ColorAccent") + .IsRequired() + .HasColumnType("text"); + + b1.Property("ColorAccentContent") + .IsRequired() + .HasColumnType("text"); + + b1.Property("ColorBackground") + .IsRequired() + .HasColumnType("text"); + + b1.Property("ColorBase100") + .IsRequired() + .HasColumnType("text"); + + b1.Property("ColorBase150") + .IsRequired() + .HasColumnType("text"); + + b1.Property("ColorBase200") + .IsRequired() + .HasColumnType("text"); + + b1.Property("ColorBase250") + .IsRequired() + .HasColumnType("text"); + + b1.Property("ColorBase300") + .IsRequired() + .HasColumnType("text"); + + b1.Property("ColorBaseContent") + .IsRequired() + .HasColumnType("text"); + + b1.Property("ColorError") + .IsRequired() + .HasColumnType("text"); + + b1.Property("ColorErrorContent") + .IsRequired() + .HasColumnType("text"); + + b1.Property("ColorInfo") + .IsRequired() + .HasColumnType("text"); + + b1.Property("ColorInfoContent") + .IsRequired() + .HasColumnType("text"); + + b1.Property("ColorNeutral") + .IsRequired() + .HasColumnType("text"); + + b1.Property("ColorNeutralContent") + .IsRequired() + .HasColumnType("text"); + + b1.Property("ColorPrimary") + .IsRequired() + .HasColumnType("text"); + + b1.Property("ColorPrimaryContent") + .IsRequired() + .HasColumnType("text"); + + b1.Property("ColorSecondary") + .IsRequired() + .HasColumnType("text"); + + b1.Property("ColorSecondaryContent") + .IsRequired() + .HasColumnType("text"); + + b1.Property("ColorSuccess") + .IsRequired() + .HasColumnType("text"); + + b1.Property("ColorSuccessContent") + .IsRequired() + .HasColumnType("text"); + + b1.Property("ColorWarning") + .IsRequired() + .HasColumnType("text"); + + b1.Property("ColorWarningContent") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Depth") + .HasColumnType("integer"); + + b1.Property("Noise") + .HasColumnType("integer"); + + b1.Property("RadiusBox") + .HasColumnType("real"); + + b1.Property("RadiusField") + .HasColumnType("real"); + + b1.Property("RadiusSelector") + .HasColumnType("real"); + + b1.Property("SizeField") + .HasColumnType("real"); + + b1.Property("SizeSelector") + .HasColumnType("real"); + + b1.HasKey("ThemeId"); + + b1.ToTable("Core_Themes"); + + b1.ToJson("Content"); + + b1.WithOwner() + .HasForeignKey("ThemeId"); + }); + + b.Navigation("Content") + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Moonlight.ApiServer/Database/Migrations/20250819195904_RemovedHangfire.cs b/Moonlight.ApiServer/Database/Migrations/20250819195904_RemovedHangfire.cs new file mode 100644 index 00000000..0ed87fd2 --- /dev/null +++ b/Moonlight.ApiServer/Database/Migrations/20250819195904_RemovedHangfire.cs @@ -0,0 +1,290 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Moonlight.ApiServer.Database.Migrations +{ + /// + public partial class RemovedHangfire : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_HangfireJob_HangfireState_StateId", + table: "HangfireJob"); + + migrationBuilder.DropTable( + name: "HangfireCounter"); + + migrationBuilder.DropTable( + name: "HangfireHash"); + + migrationBuilder.DropTable( + name: "HangfireJobParameter"); + + migrationBuilder.DropTable( + name: "HangfireList"); + + migrationBuilder.DropTable( + name: "HangfireLock"); + + migrationBuilder.DropTable( + name: "HangfireQueuedJob"); + + migrationBuilder.DropTable( + name: "HangfireServer"); + + migrationBuilder.DropTable( + name: "HangfireSet"); + + migrationBuilder.DropTable( + name: "HangfireState"); + + migrationBuilder.DropTable( + name: "HangfireJob"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "HangfireCounter", + columns: table => new + { + Id = table.Column(type: "bigint", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + ExpireAt = table.Column(type: "timestamp with time zone", nullable: true), + Key = table.Column(type: "character varying(256)", maxLength: 256, nullable: false), + Value = table.Column(type: "bigint", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_HangfireCounter", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "HangfireHash", + columns: table => new + { + Key = table.Column(type: "character varying(256)", maxLength: 256, nullable: false), + Field = table.Column(type: "character varying(256)", maxLength: 256, nullable: false), + ExpireAt = table.Column(type: "timestamp with time zone", nullable: true), + Value = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_HangfireHash", x => new { x.Key, x.Field }); + }); + + migrationBuilder.CreateTable( + name: "HangfireList", + columns: table => new + { + Key = table.Column(type: "character varying(256)", maxLength: 256, nullable: false), + Position = table.Column(type: "integer", nullable: false), + ExpireAt = table.Column(type: "timestamp with time zone", nullable: true), + Value = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_HangfireList", x => new { x.Key, x.Position }); + }); + + migrationBuilder.CreateTable( + name: "HangfireLock", + columns: table => new + { + Id = table.Column(type: "character varying(256)", maxLength: 256, nullable: false), + AcquiredAt = table.Column(type: "timestamp with time zone", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_HangfireLock", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "HangfireServer", + columns: table => new + { + Id = table.Column(type: "character varying(256)", maxLength: 256, nullable: false), + Heartbeat = table.Column(type: "timestamp with time zone", nullable: false), + Queues = table.Column(type: "text", nullable: false), + StartedAt = table.Column(type: "timestamp with time zone", nullable: false), + WorkerCount = table.Column(type: "integer", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_HangfireServer", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "HangfireSet", + columns: table => new + { + Key = table.Column(type: "character varying(100)", maxLength: 100, nullable: false), + Value = table.Column(type: "character varying(256)", maxLength: 256, nullable: false), + ExpireAt = table.Column(type: "timestamp with time zone", nullable: true), + Score = table.Column(type: "double precision", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_HangfireSet", x => new { x.Key, x.Value }); + }); + + migrationBuilder.CreateTable( + name: "HangfireJob", + columns: table => new + { + Id = table.Column(type: "bigint", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + StateId = table.Column(type: "bigint", nullable: true), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false), + ExpireAt = table.Column(type: "timestamp with time zone", nullable: true), + InvocationData = table.Column(type: "text", nullable: false), + StateName = table.Column(type: "character varying(256)", maxLength: 256, nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_HangfireJob", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "HangfireJobParameter", + columns: table => new + { + JobId = table.Column(type: "bigint", nullable: false), + Name = table.Column(type: "character varying(256)", maxLength: 256, nullable: false), + Value = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_HangfireJobParameter", x => new { x.JobId, x.Name }); + table.ForeignKey( + name: "FK_HangfireJobParameter_HangfireJob_JobId", + column: x => x.JobId, + principalTable: "HangfireJob", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "HangfireQueuedJob", + columns: table => new + { + Id = table.Column(type: "bigint", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + JobId = table.Column(type: "bigint", nullable: false), + FetchedAt = table.Column(type: "timestamp with time zone", nullable: true), + Queue = table.Column(type: "character varying(256)", maxLength: 256, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_HangfireQueuedJob", x => x.Id); + table.ForeignKey( + name: "FK_HangfireQueuedJob_HangfireJob_JobId", + column: x => x.JobId, + principalTable: "HangfireJob", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "HangfireState", + columns: table => new + { + Id = table.Column(type: "bigint", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + JobId = table.Column(type: "bigint", nullable: false), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false), + Data = table.Column(type: "text", nullable: false), + Name = table.Column(type: "character varying(256)", maxLength: 256, nullable: false), + Reason = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_HangfireState", x => x.Id); + table.ForeignKey( + name: "FK_HangfireState_HangfireJob_JobId", + column: x => x.JobId, + principalTable: "HangfireJob", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_HangfireCounter_ExpireAt", + table: "HangfireCounter", + column: "ExpireAt"); + + migrationBuilder.CreateIndex( + name: "IX_HangfireCounter_Key_Value", + table: "HangfireCounter", + columns: new[] { "Key", "Value" }); + + migrationBuilder.CreateIndex( + name: "IX_HangfireHash_ExpireAt", + table: "HangfireHash", + column: "ExpireAt"); + + migrationBuilder.CreateIndex( + name: "IX_HangfireJob_ExpireAt", + table: "HangfireJob", + column: "ExpireAt"); + + migrationBuilder.CreateIndex( + name: "IX_HangfireJob_StateId", + table: "HangfireJob", + column: "StateId"); + + migrationBuilder.CreateIndex( + name: "IX_HangfireJob_StateName", + table: "HangfireJob", + column: "StateName"); + + migrationBuilder.CreateIndex( + name: "IX_HangfireList_ExpireAt", + table: "HangfireList", + column: "ExpireAt"); + + migrationBuilder.CreateIndex( + name: "IX_HangfireQueuedJob_JobId", + table: "HangfireQueuedJob", + column: "JobId"); + + migrationBuilder.CreateIndex( + name: "IX_HangfireQueuedJob_Queue_FetchedAt", + table: "HangfireQueuedJob", + columns: new[] { "Queue", "FetchedAt" }); + + migrationBuilder.CreateIndex( + name: "IX_HangfireServer_Heartbeat", + table: "HangfireServer", + column: "Heartbeat"); + + migrationBuilder.CreateIndex( + name: "IX_HangfireSet_ExpireAt", + table: "HangfireSet", + column: "ExpireAt"); + + migrationBuilder.CreateIndex( + name: "IX_HangfireSet_Key_Score", + table: "HangfireSet", + columns: new[] { "Key", "Score" }); + + migrationBuilder.CreateIndex( + name: "IX_HangfireState_JobId", + table: "HangfireState", + column: "JobId"); + + migrationBuilder.AddForeignKey( + name: "FK_HangfireJob_HangfireState_StateId", + table: "HangfireJob", + column: "StateId", + principalTable: "HangfireState", + principalColumn: "Id"); + } + } +} diff --git a/Moonlight.ApiServer/Database/Migrations/20250819200221_AddedTickerQ.Designer.cs b/Moonlight.ApiServer/Database/Migrations/20250819200221_AddedTickerQ.Designer.cs new file mode 100644 index 00000000..3c1341bd --- /dev/null +++ b/Moonlight.ApiServer/Database/Migrations/20250819200221_AddedTickerQ.Designer.cs @@ -0,0 +1,222 @@ +// +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(TickerDataContext))] + [Migration("20250819200221_AddedTickerQ")] + partial class AddedTickerQ + { + /// + 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("TickerQ.EntityFrameworkCore.Entities.CronTickerEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Expression") + .HasColumnType("text"); + + b.Property("Function") + .HasColumnType("text"); + + b.Property("InitIdentifier") + .HasColumnType("text"); + + b.Property("Request") + .HasColumnType("bytea"); + + b.Property("Retries") + .HasColumnType("integer"); + + b.PrimitiveCollection("RetryIntervals") + .HasColumnType("integer[]"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("Expression") + .HasDatabaseName("IX_CronTickers_Expression"); + + b.ToTable("CronTickers", "ticker"); + }); + + modelBuilder.Entity("TickerQ.EntityFrameworkCore.Entities.CronTickerOccurrenceEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CronTickerId") + .HasColumnType("uuid"); + + b.Property("ElapsedTime") + .HasColumnType("bigint"); + + b.Property("Exception") + .HasColumnType("text"); + + b.Property("ExecutedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ExecutionTime") + .HasColumnType("timestamp with time zone"); + + b.Property("LockHolder") + .HasColumnType("text"); + + b.Property("LockedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("RetryCount") + .HasColumnType("integer"); + + b.Property("Status") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("CronTickerId") + .HasDatabaseName("IX_CronTickerOccurrence_CronTickerId"); + + b.HasIndex("ExecutionTime") + .HasDatabaseName("IX_CronTickerOccurrence_ExecutionTime"); + + b.HasIndex("CronTickerId", "ExecutionTime") + .IsUnique() + .HasDatabaseName("UQ_CronTickerId_ExecutionTime"); + + b.HasIndex("Status", "ExecutionTime") + .HasDatabaseName("IX_CronTickerOccurrence_Status_ExecutionTime"); + + b.ToTable("CronTickerOccurrences", "ticker"); + }); + + modelBuilder.Entity("TickerQ.EntityFrameworkCore.Entities.TimeTickerEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BatchParent") + .HasColumnType("uuid"); + + b.Property("BatchRunCondition") + .HasColumnType("integer"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("ElapsedTime") + .HasColumnType("bigint"); + + b.Property("Exception") + .HasColumnType("text"); + + b.Property("ExecutedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ExecutionTime") + .HasColumnType("timestamp with time zone"); + + b.Property("Function") + .HasColumnType("text"); + + b.Property("InitIdentifier") + .HasColumnType("text"); + + b.Property("LockHolder") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property("LockedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Request") + .HasColumnType("bytea"); + + b.Property("Retries") + .HasColumnType("integer"); + + b.Property("RetryCount") + .HasColumnType("integer"); + + b.PrimitiveCollection("RetryIntervals") + .HasColumnType("integer[]"); + + b.Property("Status") + .HasColumnType("integer"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("BatchParent"); + + b.HasIndex("ExecutionTime") + .HasDatabaseName("IX_TimeTicker_ExecutionTime"); + + b.HasIndex("Status", "ExecutionTime") + .HasDatabaseName("IX_TimeTicker_Status_ExecutionTime"); + + b.ToTable("TimeTickers", "ticker"); + }); + + modelBuilder.Entity("TickerQ.EntityFrameworkCore.Entities.CronTickerOccurrenceEntity", b => + { + b.HasOne("TickerQ.EntityFrameworkCore.Entities.CronTickerEntity", "CronTicker") + .WithMany() + .HasForeignKey("CronTickerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("CronTicker"); + }); + + modelBuilder.Entity("TickerQ.EntityFrameworkCore.Entities.TimeTickerEntity", b => + { + b.HasOne("TickerQ.EntityFrameworkCore.Entities.TimeTickerEntity", "ParentJob") + .WithMany("ChildJobs") + .HasForeignKey("BatchParent") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("ParentJob"); + }); + + modelBuilder.Entity("TickerQ.EntityFrameworkCore.Entities.TimeTickerEntity", b => + { + b.Navigation("ChildJobs"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Moonlight.ApiServer/Database/Migrations/20250819200221_AddedTickerQ.cs b/Moonlight.ApiServer/Database/Migrations/20250819200221_AddedTickerQ.cs new file mode 100644 index 00000000..e5df60b8 --- /dev/null +++ b/Moonlight.ApiServer/Database/Migrations/20250819200221_AddedTickerQ.cs @@ -0,0 +1,169 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Moonlight.ApiServer.Database.Migrations +{ + /// + public partial class AddedTickerQ : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.EnsureSchema( + name: "ticker"); + + migrationBuilder.CreateTable( + name: "CronTickers", + schema: "ticker", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + Expression = table.Column(type: "text", nullable: true), + Request = table.Column(type: "bytea", nullable: true), + Retries = table.Column(type: "integer", nullable: false), + RetryIntervals = table.Column(type: "integer[]", nullable: true), + Function = table.Column(type: "text", nullable: true), + Description = table.Column(type: "text", nullable: true), + InitIdentifier = table.Column(type: "text", nullable: true), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false), + UpdatedAt = table.Column(type: "timestamp with time zone", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_CronTickers", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "TimeTickers", + schema: "ticker", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + Status = table.Column(type: "integer", nullable: false), + LockHolder = table.Column(type: "text", nullable: true), + Request = table.Column(type: "bytea", nullable: true), + ExecutionTime = table.Column(type: "timestamp with time zone", nullable: false), + LockedAt = table.Column(type: "timestamp with time zone", nullable: true), + ExecutedAt = table.Column(type: "timestamp with time zone", nullable: true), + Exception = table.Column(type: "text", nullable: true), + ElapsedTime = table.Column(type: "bigint", nullable: false), + Retries = table.Column(type: "integer", nullable: false), + RetryCount = table.Column(type: "integer", nullable: false), + RetryIntervals = table.Column(type: "integer[]", nullable: true), + BatchParent = table.Column(type: "uuid", nullable: true), + BatchRunCondition = table.Column(type: "integer", nullable: true), + Function = table.Column(type: "text", nullable: true), + Description = table.Column(type: "text", nullable: true), + InitIdentifier = table.Column(type: "text", nullable: true), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false), + UpdatedAt = table.Column(type: "timestamp with time zone", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_TimeTickers", x => x.Id); + table.ForeignKey( + name: "FK_TimeTickers_TimeTickers_BatchParent", + column: x => x.BatchParent, + principalSchema: "ticker", + principalTable: "TimeTickers", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateTable( + name: "CronTickerOccurrences", + schema: "ticker", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + Status = table.Column(type: "integer", nullable: false), + LockHolder = table.Column(type: "text", nullable: true), + ExecutionTime = table.Column(type: "timestamp with time zone", nullable: false), + CronTickerId = table.Column(type: "uuid", nullable: false), + LockedAt = table.Column(type: "timestamp with time zone", nullable: true), + ExecutedAt = table.Column(type: "timestamp with time zone", nullable: true), + Exception = table.Column(type: "text", nullable: true), + ElapsedTime = table.Column(type: "bigint", nullable: false), + RetryCount = table.Column(type: "integer", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_CronTickerOccurrences", x => x.Id); + table.ForeignKey( + name: "FK_CronTickerOccurrences_CronTickers_CronTickerId", + column: x => x.CronTickerId, + principalSchema: "ticker", + principalTable: "CronTickers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_CronTickerOccurrence_CronTickerId", + schema: "ticker", + table: "CronTickerOccurrences", + column: "CronTickerId"); + + migrationBuilder.CreateIndex( + name: "IX_CronTickerOccurrence_ExecutionTime", + schema: "ticker", + table: "CronTickerOccurrences", + column: "ExecutionTime"); + + migrationBuilder.CreateIndex( + name: "IX_CronTickerOccurrence_Status_ExecutionTime", + schema: "ticker", + table: "CronTickerOccurrences", + columns: new[] { "Status", "ExecutionTime" }); + + migrationBuilder.CreateIndex( + name: "UQ_CronTickerId_ExecutionTime", + schema: "ticker", + table: "CronTickerOccurrences", + columns: new[] { "CronTickerId", "ExecutionTime" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_CronTickers_Expression", + schema: "ticker", + table: "CronTickers", + column: "Expression"); + + migrationBuilder.CreateIndex( + name: "IX_TimeTicker_ExecutionTime", + schema: "ticker", + table: "TimeTickers", + column: "ExecutionTime"); + + migrationBuilder.CreateIndex( + name: "IX_TimeTicker_Status_ExecutionTime", + schema: "ticker", + table: "TimeTickers", + columns: new[] { "Status", "ExecutionTime" }); + + migrationBuilder.CreateIndex( + name: "IX_TimeTickers_BatchParent", + schema: "ticker", + table: "TimeTickers", + column: "BatchParent"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "CronTickerOccurrences", + schema: "ticker"); + + migrationBuilder.DropTable( + name: "TimeTickers", + schema: "ticker"); + + migrationBuilder.DropTable( + name: "CronTickers", + schema: "ticker"); + } + } +} diff --git a/Moonlight.ApiServer/Database/Migrations/CoreDataContextModelSnapshot.cs b/Moonlight.ApiServer/Database/Migrations/CoreDataContextModelSnapshot.cs index e743555e..d24ddcbb 100644 --- a/Moonlight.ApiServer/Database/Migrations/CoreDataContextModelSnapshot.cs +++ b/Moonlight.ApiServer/Database/Migrations/CoreDataContextModelSnapshot.cs @@ -22,259 +22,6 @@ namespace Moonlight.ApiServer.Database.Migrations 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") @@ -368,48 +115,6 @@ namespace Moonlight.ApiServer.Database.Migrations 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("Moonlight.ApiServer.Database.Entities.Theme", b => { b.OwnsOne("Moonlight.ApiServer.Models.ApplicationTheme", "Content", b1 => @@ -512,11 +217,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"); @@ -546,15 +251,6 @@ namespace Moonlight.ApiServer.Database.Migrations b.Navigation("Content") .IsRequired(); }); - - 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/TickerDataContextModelSnapshot.cs b/Moonlight.ApiServer/Database/Migrations/TickerDataContextModelSnapshot.cs new file mode 100644 index 00000000..4c1324e7 --- /dev/null +++ b/Moonlight.ApiServer/Database/Migrations/TickerDataContextModelSnapshot.cs @@ -0,0 +1,219 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Moonlight.ApiServer.Database; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Moonlight.ApiServer.Database.Migrations +{ + [DbContext(typeof(TickerDataContext))] + partial class TickerDataContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.7") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("TickerQ.EntityFrameworkCore.Entities.CronTickerEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Expression") + .HasColumnType("text"); + + b.Property("Function") + .HasColumnType("text"); + + b.Property("InitIdentifier") + .HasColumnType("text"); + + b.Property("Request") + .HasColumnType("bytea"); + + b.Property("Retries") + .HasColumnType("integer"); + + b.PrimitiveCollection("RetryIntervals") + .HasColumnType("integer[]"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("Expression") + .HasDatabaseName("IX_CronTickers_Expression"); + + b.ToTable("CronTickers", "ticker"); + }); + + modelBuilder.Entity("TickerQ.EntityFrameworkCore.Entities.CronTickerOccurrenceEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CronTickerId") + .HasColumnType("uuid"); + + b.Property("ElapsedTime") + .HasColumnType("bigint"); + + b.Property("Exception") + .HasColumnType("text"); + + b.Property("ExecutedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ExecutionTime") + .HasColumnType("timestamp with time zone"); + + b.Property("LockHolder") + .HasColumnType("text"); + + b.Property("LockedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("RetryCount") + .HasColumnType("integer"); + + b.Property("Status") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("CronTickerId") + .HasDatabaseName("IX_CronTickerOccurrence_CronTickerId"); + + b.HasIndex("ExecutionTime") + .HasDatabaseName("IX_CronTickerOccurrence_ExecutionTime"); + + b.HasIndex("CronTickerId", "ExecutionTime") + .IsUnique() + .HasDatabaseName("UQ_CronTickerId_ExecutionTime"); + + b.HasIndex("Status", "ExecutionTime") + .HasDatabaseName("IX_CronTickerOccurrence_Status_ExecutionTime"); + + b.ToTable("CronTickerOccurrences", "ticker"); + }); + + modelBuilder.Entity("TickerQ.EntityFrameworkCore.Entities.TimeTickerEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BatchParent") + .HasColumnType("uuid"); + + b.Property("BatchRunCondition") + .HasColumnType("integer"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("ElapsedTime") + .HasColumnType("bigint"); + + b.Property("Exception") + .HasColumnType("text"); + + b.Property("ExecutedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ExecutionTime") + .HasColumnType("timestamp with time zone"); + + b.Property("Function") + .HasColumnType("text"); + + b.Property("InitIdentifier") + .HasColumnType("text"); + + b.Property("LockHolder") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property("LockedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Request") + .HasColumnType("bytea"); + + b.Property("Retries") + .HasColumnType("integer"); + + b.Property("RetryCount") + .HasColumnType("integer"); + + b.PrimitiveCollection("RetryIntervals") + .HasColumnType("integer[]"); + + b.Property("Status") + .HasColumnType("integer"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("BatchParent"); + + b.HasIndex("ExecutionTime") + .HasDatabaseName("IX_TimeTicker_ExecutionTime"); + + b.HasIndex("Status", "ExecutionTime") + .HasDatabaseName("IX_TimeTicker_Status_ExecutionTime"); + + b.ToTable("TimeTickers", "ticker"); + }); + + modelBuilder.Entity("TickerQ.EntityFrameworkCore.Entities.CronTickerOccurrenceEntity", b => + { + b.HasOne("TickerQ.EntityFrameworkCore.Entities.CronTickerEntity", "CronTicker") + .WithMany() + .HasForeignKey("CronTickerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("CronTicker"); + }); + + modelBuilder.Entity("TickerQ.EntityFrameworkCore.Entities.TimeTickerEntity", b => + { + b.HasOne("TickerQ.EntityFrameworkCore.Entities.TimeTickerEntity", "ParentJob") + .WithMany("ChildJobs") + .HasForeignKey("BatchParent") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("ParentJob"); + }); + + modelBuilder.Entity("TickerQ.EntityFrameworkCore.Entities.TimeTickerEntity", b => + { + b.Navigation("ChildJobs"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Moonlight.ApiServer/Database/TickerDataContext.cs b/Moonlight.ApiServer/Database/TickerDataContext.cs new file mode 100644 index 00000000..46766841 --- /dev/null +++ b/Moonlight.ApiServer/Database/TickerDataContext.cs @@ -0,0 +1,31 @@ +using Microsoft.EntityFrameworkCore; +using MoonCore.Extended.SingleDb; +using Moonlight.ApiServer.Configuration; +using TickerQ.EntityFrameworkCore.Configurations; + +namespace Moonlight.ApiServer.Database; + +public class TickerDataContext : DatabaseContext +{ + public override string Prefix => "Ticker"; + + public TickerDataContext(AppConfiguration configuration) + { + Options = new() + { + Host = configuration.Database.Host, + Port = configuration.Database.Port, + Username = configuration.Database.Username, + Password = configuration.Database.Password, + Database = configuration.Database.Database + }; + } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + // Apply TickerQ entity configurations explicitly + modelBuilder.ApplyConfiguration(new TimeTickerConfigurations()); + modelBuilder.ApplyConfiguration(new CronTickerConfigurations()); + modelBuilder.ApplyConfiguration(new CronTickerOccurrenceConfigurations()); + } +} \ No newline at end of file diff --git a/Moonlight.ApiServer/Http/Controllers/Admin/Sys/HangfireController.cs b/Moonlight.ApiServer/Http/Controllers/Admin/Sys/HangfireController.cs deleted file mode 100644 index 319042a9..00000000 --- a/Moonlight.ApiServer/Http/Controllers/Admin/Sys/HangfireController.cs +++ /dev/null @@ -1,40 +0,0 @@ -using Hangfire; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; -using Moonlight.Shared.Http.Responses.Admin.Hangfire; - -namespace Moonlight.ApiServer.Http.Controllers.Admin.Sys; - -[ApiController] -[Route("api/admin/system/hangfire")] -[Authorize(Policy = "permissions:admin.system.hangfire")] -public class HangfireController : Controller -{ - private readonly JobStorage JobStorage; - - public HangfireController(JobStorage jobStorage) - { - JobStorage = jobStorage; - } - - [HttpGet("stats")] - public Task GetStats() - { - var statistics = JobStorage.GetMonitoringApi().GetStatistics(); - - return Task.FromResult(new HangfireStatsResponse() - { - Awaiting = statistics.Awaiting, - Deleted = statistics.Deleted, - Enqueued = statistics.Enqueued, - Failed = statistics.Failed, - Processing = statistics.Processing, - Queues = statistics.Queues, - Recurring = statistics.Recurring, - Retries = statistics.Retries, - Scheduled = statistics.Scheduled, - Servers = statistics.Servers, - Succeeded = statistics.Succeeded - }); - } -} \ No newline at end of file diff --git a/Moonlight.ApiServer/Implementations/TickerExceptionHandler.cs b/Moonlight.ApiServer/Implementations/TickerExceptionHandler.cs new file mode 100644 index 00000000..20b3f5a9 --- /dev/null +++ b/Moonlight.ApiServer/Implementations/TickerExceptionHandler.cs @@ -0,0 +1,29 @@ +using Microsoft.Extensions.Logging; +using TickerQ.Utilities.Enums; +using TickerQ.Utilities.Interfaces; + +namespace Moonlight.ApiServer.Implementations; + +public class TickerExceptionHandler : ITickerExceptionHandler +{ + private readonly ILogger Logger; + + public TickerExceptionHandler(ILogger logger) + { + Logger = logger; + } + + public Task HandleExceptionAsync(Exception exception, Guid tickerId, TickerType tickerType) + { + Logger.LogError(exception, "An unhandled error occured while running ticker {id} ({type})", tickerId, tickerType); + + return Task.CompletedTask; + } + + public Task HandleCanceledExceptionAsync(Exception exception, Guid tickerId, TickerType tickerType) + { + Logger.LogError(exception, "An unhandled error occured while handling canceled ticker {id} ({type})", tickerId, tickerType); + + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/Moonlight.ApiServer/Moonlight.ApiServer.csproj b/Moonlight.ApiServer/Moonlight.ApiServer.csproj index e5fee3df..c5622424 100644 --- a/Moonlight.ApiServer/Moonlight.ApiServer.csproj +++ b/Moonlight.ApiServer/Moonlight.ApiServer.csproj @@ -23,9 +23,6 @@ true - - - @@ -37,6 +34,8 @@ + + diff --git a/Moonlight.ApiServer/Startup/Startup.Hangfire.cs b/Moonlight.ApiServer/Startup/Startup.Hangfire.cs deleted file mode 100644 index b06583f6..00000000 --- a/Moonlight.ApiServer/Startup/Startup.Hangfire.cs +++ /dev/null @@ -1,48 +0,0 @@ -using Hangfire; -using Hangfire.EntityFrameworkCore; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; -using Moonlight.ApiServer.Database; - -namespace Moonlight.ApiServer.Startup; - -public partial class Startup -{ - private Task RegisterHangfire() - { - WebApplicationBuilder.Services.AddHangfire((provider, configuration) => - { - configuration.SetDataCompatibilityLevel(CompatibilityLevel.Version_180); - configuration.UseSimpleAssemblyNameTypeSerializer(); - configuration.UseRecommendedSerializerSettings(); - configuration.UseEFCoreStorage(() => - { - var scope = provider.CreateScope(); - return scope.ServiceProvider.GetRequiredService(); - }, new EFCoreStorageOptions()); - }); - - WebApplicationBuilder.Services.AddHangfireServer(); - - WebApplicationBuilder.Logging.AddFilter( - "Hangfire.Server.BackgroundServerProcess", - LogLevel.Warning - ); - - WebApplicationBuilder.Logging.AddFilter( - "Hangfire.BackgroundJobServer", - LogLevel.Warning - ); - - return Task.CompletedTask; - } - - private Task UseHangfire() - { - if (WebApplication.Environment.IsDevelopment()) - WebApplication.UseHangfireDashboard(); - - return Task.CompletedTask; - } -} \ No newline at end of file diff --git a/Moonlight.ApiServer/Startup/Startup.TickerQ.cs b/Moonlight.ApiServer/Startup/Startup.TickerQ.cs new file mode 100644 index 00000000..b1a7b2cf --- /dev/null +++ b/Moonlight.ApiServer/Startup/Startup.TickerQ.cs @@ -0,0 +1,34 @@ +using Microsoft.Extensions.DependencyInjection; +using Moonlight.ApiServer.Database; +using Moonlight.ApiServer.Implementations; +using TickerQ.DependencyInjection; +using TickerQ.EntityFrameworkCore.DependencyInjection; + +namespace Moonlight.ApiServer.Startup; + +public partial class Startup +{ + private Task RegisterTickerQ() + { + WebApplicationBuilder.Services.AddTickerQ(builder => + { + builder.SetExceptionHandler(); + + builder.AddOperationalStore(optionBuilder => + { + optionBuilder.CancelMissedTickersOnApplicationRestart(); + }); + }); + + WebApplicationBuilder.Services.AddDbContext(); + + return Task.CompletedTask; + } + + private Task UseTickerQ() + { + WebApplication.UseTickerQ(); + + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/Moonlight.ApiServer/Startup/Startup.cs b/Moonlight.ApiServer/Startup/Startup.cs index 731ca06b..921e581b 100644 --- a/Moonlight.ApiServer/Startup/Startup.cs +++ b/Moonlight.ApiServer/Startup/Startup.cs @@ -45,7 +45,7 @@ public partial class Startup await RegisterDatabase(); await RegisterAuth(); await RegisterCors(); - await RegisterHangfire(); + await RegisterTickerQ(); await HookPluginBuild(); } @@ -58,7 +58,7 @@ public partial class Startup await UseCors(); await UseBase(); await UseAuth(); - await UseHangfire(); + await UseTickerQ(); await HookPluginConfigure(); await MapBase(); diff --git a/Moonlight.Client/UI/Components/HelperMessage.razor b/Moonlight.Client/UI/Components/HelperMessage.razor deleted file mode 100644 index b8dfba13..00000000 --- a/Moonlight.Client/UI/Components/HelperMessage.razor +++ /dev/null @@ -1,15 +0,0 @@ - - -@code -{ - [Parameter] public RenderFragment ChildContent { get; set; } -} \ 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..48fc45cc 100644 --- a/Moonlight.Client/UI/Views/Admin/Sys/Advanced.razor +++ b/Moonlight.Client/UI/Views/Admin/Sys/Advanced.razor @@ -10,7 +10,7 @@ @inject DownloadService DownloadService
- +
diff --git a/Moonlight.Client/UI/Views/Admin/Sys/Diagnose.razor b/Moonlight.Client/UI/Views/Admin/Sys/Diagnose.razor index 92a872d5..39083ded 100644 --- a/Moonlight.Client/UI/Views/Admin/Sys/Diagnose.razor +++ b/Moonlight.Client/UI/Views/Admin/Sys/Diagnose.razor @@ -12,7 +12,7 @@ @inject DownloadService DownloadService
- +
diff --git a/Moonlight.Client/UI/Views/Admin/Sys/Hangfire.razor b/Moonlight.Client/UI/Views/Admin/Sys/Hangfire.razor deleted file mode 100644 index 9d6d31ec..00000000 --- a/Moonlight.Client/UI/Views/Admin/Sys/Hangfire.razor +++ /dev/null @@ -1,48 +0,0 @@ -@page "/admin/system/hangfire" - -@using Microsoft.AspNetCore.Authorization -@using MoonCore.Helpers -@using Moonlight.Shared.Http.Responses.Admin.Hangfire -@using Moonlight.Client.UI.Components - -@attribute [Authorize(Policy = "permissions:admin.system.hangfire")] - -@inject HttpApiClient ApiClient - -
- -
- -
- - Hangfire is used to run scheduled and repeating tasks scalable via multiple instances. Here you can see a bunch of stats of the integrated hangfire instance - -
- - -
- - - - - - - - - - - -
-
- -@code -{ - private HangfireStatsResponse Stats; - - private async Task Load(LazyLoader _) - { - Stats = await ApiClient.GetJson( - "api/admin/system/hangfire/stats" - ); - } -} diff --git a/Moonlight.Client/UiConstants.cs b/Moonlight.Client/UiConstants.cs index f6c6432f..893958c3 100644 --- a/Moonlight.Client/UiConstants.cs +++ b/Moonlight.Client/UiConstants.cs @@ -4,12 +4,12 @@ public static class UiConstants { public static readonly string[] AdminNavNames = [ - "Overview", "Customisation", "Files", "Hangfire", "Advanced", "Diagnose" + "Overview", "Customisation", "Files", "Advanced", "Diagnose" ]; public static readonly string[] AdminNavLinks = [ - "/admin/system", "/admin/system/customisation", "/admin/system/files", "/admin/system/hangfire", + "/admin/system", "/admin/system/customisation", "/admin/system/files", "/admin/system/advanced", "/admin/system/diagnose" ]; } \ No newline at end of file diff --git a/Moonlight.Shared/Http/Responses/Admin/Hangfire/HangfireStatsResponse.cs b/Moonlight.Shared/Http/Responses/Admin/Hangfire/HangfireStatsResponse.cs deleted file mode 100644 index c925d814..00000000 --- a/Moonlight.Shared/Http/Responses/Admin/Hangfire/HangfireStatsResponse.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace Moonlight.Shared.Http.Responses.Admin.Hangfire; - -public class HangfireStatsResponse -{ - public long Servers { get; set; } - public long Recurring { get; set; } - public long Enqueued { get; set; } - public long Queues { get; set; } - public long Scheduled { get; set; } - public long Processing { get; set; } - public long Succeeded { get; set; } - public long Failed { get; set; } - public long Deleted { get; set; } - public long? Retries { get; set; } - public long? Awaiting { get; set; } -} \ No newline at end of file