refactor(backend): replace coravel with hangfire for background jobs

for *some reason*, coravel locks a persistent job queue behind a
paywall. this means that if the server ever crashes, all pending jobs
are lost. this is... not good, so we're switching to hangfire for that
instead.

coravel is still used for emails, though.

BREAKING CHANGE: Foxnouns.NET now requires Redis to work. the EFCore
storage for hangfire doesn't work well enough, unfortunately.
This commit is contained in:
sam 2025-03-04 17:03:39 +01:00
parent cd24196cd1
commit 7759225428
Signed by: sam
GPG key ID: B4EF20DDE721CAA1
24 changed files with 272 additions and 269 deletions

View file

@ -64,7 +64,6 @@ public class DatabaseContext(DbContextOptions options) : DbContext(options)
public DbSet<FediverseApplication> FediverseApplications { get; init; } = null!;
public DbSet<Token> Tokens { get; init; } = null!;
public DbSet<Application> Applications { get; init; } = null!;
public DbSet<TemporaryKey> TemporaryKeys { get; init; } = null!;
public DbSet<DataExport> DataExports { get; init; } = null!;
public DbSet<PrideFlag> PrideFlags { get; init; } = null!;
@ -87,7 +86,6 @@ public class DatabaseContext(DbContextOptions options) : DbContext(options)
modelBuilder.Entity<User>().HasIndex(u => u.Sid).IsUnique();
modelBuilder.Entity<Member>().HasIndex(m => new { m.UserId, m.Name }).IsUnique();
modelBuilder.Entity<Member>().HasIndex(m => m.Sid).IsUnique();
modelBuilder.Entity<TemporaryKey>().HasIndex(k => k.Key).IsUnique();
modelBuilder.Entity<DataExport>().HasIndex(d => d.Filename).IsUnique();
// Two indexes on auth_methods, one for fediverse auth and one for all other types.

View file

@ -0,0 +1,55 @@
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using NodaTime;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
namespace Foxnouns.Backend.Database.Migrations
{
/// <inheritdoc />
[DbContext(typeof(DatabaseContext))]
[Migration("20250304155708_RemoveTemporaryKeys")]
public partial class RemoveTemporaryKeys : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(name: "temporary_keys");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "temporary_keys",
columns: table => new
{
id = table
.Column<long>(type: "bigint", nullable: false)
.Annotation(
"Npgsql:ValueGenerationStrategy",
NpgsqlValueGenerationStrategy.IdentityByDefaultColumn
),
expires = table.Column<Instant>(
type: "timestamp with time zone",
nullable: false
),
key = table.Column<string>(type: "text", nullable: false),
value = table.Column<string>(type: "text", nullable: false),
},
constraints: table =>
{
table.PrimaryKey("pk_temporary_keys", x => x.id);
}
);
migrationBuilder.CreateIndex(
name: "ix_temporary_keys_key",
table: "temporary_keys",
column: "key",
unique: true
);
}
}
}

View file

@ -19,7 +19,7 @@ namespace Foxnouns.Backend.Database.Migrations
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "9.0.0")
.HasAnnotation("ProductVersion", "9.0.2")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "hstore");
@ -479,39 +479,6 @@ namespace Foxnouns.Backend.Database.Migrations
b.ToTable("reports", (string)null);
});
modelBuilder.Entity("Foxnouns.Backend.Database.Models.TemporaryKey", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint")
.HasColumnName("id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<Instant>("Expires")
.HasColumnType("timestamp with time zone")
.HasColumnName("expires");
b.Property<string>("Key")
.IsRequired()
.HasColumnType("text")
.HasColumnName("key");
b.Property<string>("Value")
.IsRequired()
.HasColumnType("text")
.HasColumnName("value");
b.HasKey("Id")
.HasName("pk_temporary_keys");
b.HasIndex("Key")
.IsUnique()
.HasDatabaseName("ix_temporary_keys_key");
b.ToTable("temporary_keys", (string)null);
});
modelBuilder.Entity("Foxnouns.Backend.Database.Models.Token", b =>
{
b.Property<long>("Id")

View file

@ -1,25 +0,0 @@
// Copyright (C) 2023-present sam/u1f320 (vulpine.solutions)
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
using NodaTime;
namespace Foxnouns.Backend.Database.Models;
public class TemporaryKey
{
public long Id { get; init; }
public required string Key { get; init; }
public required string Value { get; set; }
public Instant Expires { get; init; }
}