From 51a5e305637aae12e27bfe0fea34181f255d0c23 Mon Sep 17 00:00:00 2001
From: zysim <9867871+zysim@users.noreply.github.com>
Date: Mon, 21 Aug 2023 06:12:09 +0000
Subject: [PATCH] Add Account Recovery Model (#173)
* Add model
* Remove erroneous fields and field inits
---
...30806090228_AddAccountRecovery.Designer.cs | 264 ++++++++++++++++++
.../20230806090228_AddAccountRecovery.cs | 49 ++++
.../ApplicationContextModelSnapshot.cs | 49 ++++
.../Models/Entities/AccountRecovery.cs | 49 ++++
.../Models/Entities/ApplicationContext.cs | 1 +
5 files changed, 412 insertions(+)
create mode 100644 LeaderboardBackend/Migrations/20230806090228_AddAccountRecovery.Designer.cs
create mode 100644 LeaderboardBackend/Migrations/20230806090228_AddAccountRecovery.cs
create mode 100644 LeaderboardBackend/Models/Entities/AccountRecovery.cs
diff --git a/LeaderboardBackend/Migrations/20230806090228_AddAccountRecovery.Designer.cs b/LeaderboardBackend/Migrations/20230806090228_AddAccountRecovery.Designer.cs
new file mode 100644
index 00000000..647add74
--- /dev/null
+++ b/LeaderboardBackend/Migrations/20230806090228_AddAccountRecovery.Designer.cs
@@ -0,0 +1,264 @@
+//
+using System;
+using LeaderboardBackend.Models.Entities;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+using NodaTime;
+using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
+
+#nullable disable
+
+namespace LeaderboardBackend.Migrations
+{
+ [DbContext(typeof(ApplicationContext))]
+ [Migration("20230806090228_AddAccountRecovery")]
+ partial class AddAccountRecovery
+ {
+ ///
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasAnnotation("Npgsql:CollationDefinition:case_insensitive", "und-u-ks-level2,und-u-ks-level2,icu,False")
+ .HasAnnotation("ProductVersion", "7.0.9")
+ .HasAnnotation("Relational:MaxIdentifierLength", 63);
+
+ NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "user_role", new[] { "registered", "confirmed", "administrator", "banned" });
+ NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
+
+ modelBuilder.Entity("LeaderboardBackend.Models.Entities.AccountRecovery", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid")
+ .HasColumnName("id");
+
+ b.Property("CreatedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("created_at");
+
+ b.Property("ExpiresAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("expires_at");
+
+ b.Property("UsedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("used_at");
+
+ b.Property("UserId")
+ .HasColumnType("uuid")
+ .HasColumnName("user_id");
+
+ b.HasKey("Id")
+ .HasName("pk_account_recoveries");
+
+ b.HasIndex("UserId")
+ .HasDatabaseName("ix_account_recoveries_user_id");
+
+ b.ToTable("account_recoveries", (string)null);
+ });
+
+ modelBuilder.Entity("LeaderboardBackend.Models.Entities.Category", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("bigint")
+ .HasColumnName("id");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("LeaderboardId")
+ .HasColumnType("bigint")
+ .HasColumnName("leaderboard_id");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("name");
+
+ b.Property("PlayersMax")
+ .HasColumnType("integer")
+ .HasColumnName("players_max");
+
+ b.Property("PlayersMin")
+ .HasColumnType("integer")
+ .HasColumnName("players_min");
+
+ b.Property("Rules")
+ .HasColumnType("text")
+ .HasColumnName("rules");
+
+ b.Property("Slug")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("slug");
+
+ b.HasKey("Id")
+ .HasName("pk_categories");
+
+ b.HasIndex("LeaderboardId")
+ .HasDatabaseName("ix_categories_leaderboard_id");
+
+ b.ToTable("categories", (string)null);
+ });
+
+ modelBuilder.Entity("LeaderboardBackend.Models.Entities.Leaderboard", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("bigint")
+ .HasColumnName("id");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("name");
+
+ b.Property("Rules")
+ .HasColumnType("text")
+ .HasColumnName("rules");
+
+ b.Property("Slug")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("slug");
+
+ b.HasKey("Id")
+ .HasName("pk_leaderboards");
+
+ b.ToTable("leaderboards", (string)null);
+ });
+
+ modelBuilder.Entity("LeaderboardBackend.Models.Entities.Run", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid")
+ .HasColumnName("id");
+
+ b.Property("CategoryId")
+ .HasColumnType("bigint")
+ .HasColumnName("category_id");
+
+ b.Property("PlayedOn")
+ .HasColumnType("date")
+ .HasColumnName("played_on");
+
+ b.Property("SubmittedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("submitted_at");
+
+ b.HasKey("Id")
+ .HasName("pk_runs");
+
+ b.HasIndex("CategoryId")
+ .HasDatabaseName("ix_runs_category_id");
+
+ b.ToTable("runs", (string)null);
+ });
+
+ modelBuilder.Entity("LeaderboardBackend.Models.Entities.User", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid")
+ .HasColumnName("id");
+
+ b.Property("Email")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("email")
+ .UseCollation("case_insensitive");
+
+ b.Property("Password")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("password");
+
+ b.Property("Role")
+ .HasColumnType("user_role")
+ .HasColumnName("role");
+
+ b.Property("Username")
+ .IsRequired()
+ .HasColumnType("text")
+ .HasColumnName("username")
+ .UseCollation("case_insensitive");
+
+ b.HasKey("Id")
+ .HasName("pk_users");
+
+ b.HasIndex("Email")
+ .IsUnique()
+ .HasDatabaseName("ix_users_email");
+
+ b.HasIndex("Username")
+ .IsUnique()
+ .HasDatabaseName("ix_users_username");
+
+ b.ToTable("users", (string)null);
+
+ b.HasData(
+ new
+ {
+ Id = new Guid("421bb896-1990-48c6-8b0c-d69f56d6746a"),
+ Email = "omega@star.com",
+ Password = "$2a$11$tNvA94WqpJ.O7S7D6lVMn.E/UxcFYztl3BkcnBj/hgE8PY/8nCRQe",
+ Role = UserRole.Administrator,
+ Username = "Galactus"
+ });
+ });
+
+ modelBuilder.Entity("LeaderboardBackend.Models.Entities.AccountRecovery", b =>
+ {
+ b.HasOne("LeaderboardBackend.Models.Entities.User", "User")
+ .WithMany("AccountRecoveries")
+ .HasForeignKey("UserId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired()
+ .HasConstraintName("fk_account_recoveries_users_user_id");
+
+ b.Navigation("User");
+ });
+
+ modelBuilder.Entity("LeaderboardBackend.Models.Entities.Category", b =>
+ {
+ b.HasOne("LeaderboardBackend.Models.Entities.Leaderboard", "Leaderboard")
+ .WithMany("Categories")
+ .HasForeignKey("LeaderboardId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired()
+ .HasConstraintName("fk_categories_leaderboards_leaderboard_id");
+
+ b.Navigation("Leaderboard");
+ });
+
+ modelBuilder.Entity("LeaderboardBackend.Models.Entities.Run", b =>
+ {
+ b.HasOne("LeaderboardBackend.Models.Entities.Category", "Category")
+ .WithMany()
+ .HasForeignKey("CategoryId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired()
+ .HasConstraintName("fk_runs_categories_category_id");
+
+ b.Navigation("Category");
+ });
+
+ modelBuilder.Entity("LeaderboardBackend.Models.Entities.Leaderboard", b =>
+ {
+ b.Navigation("Categories");
+ });
+
+ modelBuilder.Entity("LeaderboardBackend.Models.Entities.User", b =>
+ {
+ b.Navigation("AccountRecoveries");
+ });
+#pragma warning restore 612, 618
+ }
+ }
+}
diff --git a/LeaderboardBackend/Migrations/20230806090228_AddAccountRecovery.cs b/LeaderboardBackend/Migrations/20230806090228_AddAccountRecovery.cs
new file mode 100644
index 00000000..7ccca1b1
--- /dev/null
+++ b/LeaderboardBackend/Migrations/20230806090228_AddAccountRecovery.cs
@@ -0,0 +1,49 @@
+using System;
+using Microsoft.EntityFrameworkCore.Migrations;
+using NodaTime;
+
+#nullable disable
+
+namespace LeaderboardBackend.Migrations
+{
+ ///
+ public partial class AddAccountRecovery : Migration
+ {
+ ///
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.CreateTable(
+ name: "account_recoveries",
+ columns: table => new
+ {
+ id = table.Column(type: "uuid", nullable: false),
+ user_id = table.Column(type: "uuid", nullable: false),
+ created_at = table.Column(type: "timestamp with time zone", nullable: false),
+ used_at = table.Column(type: "timestamp with time zone", nullable: true),
+ expires_at = table.Column(type: "timestamp with time zone", nullable: false)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("pk_account_recoveries", x => x.id);
+ table.ForeignKey(
+ name: "fk_account_recoveries_users_user_id",
+ column: x => x.user_id,
+ principalTable: "users",
+ principalColumn: "id",
+ onDelete: ReferentialAction.Cascade);
+ });
+
+ migrationBuilder.CreateIndex(
+ name: "ix_account_recoveries_user_id",
+ table: "account_recoveries",
+ column: "user_id");
+ }
+
+ ///
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropTable(
+ name: "account_recoveries");
+ }
+ }
+}
diff --git a/LeaderboardBackend/Migrations/ApplicationContextModelSnapshot.cs b/LeaderboardBackend/Migrations/ApplicationContextModelSnapshot.cs
index dd6aa8a1..cb09c1f9 100644
--- a/LeaderboardBackend/Migrations/ApplicationContextModelSnapshot.cs
+++ b/LeaderboardBackend/Migrations/ApplicationContextModelSnapshot.cs
@@ -25,6 +25,38 @@ protected override void BuildModel(ModelBuilder modelBuilder)
NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "user_role", new[] { "registered", "confirmed", "administrator", "banned" });
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
+ modelBuilder.Entity("LeaderboardBackend.Models.Entities.AccountRecovery", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid")
+ .HasColumnName("id");
+
+ b.Property("CreatedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("created_at");
+
+ b.Property("ExpiresAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("expires_at");
+
+ b.Property("UsedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("used_at");
+
+ b.Property("UserId")
+ .HasColumnType("uuid")
+ .HasColumnName("user_id");
+
+ b.HasKey("Id")
+ .HasName("pk_account_recoveries");
+
+ b.HasIndex("UserId")
+ .HasDatabaseName("ix_account_recoveries_user_id");
+
+ b.ToTable("account_recoveries", (string)null);
+ });
+
modelBuilder.Entity("LeaderboardBackend.Models.Entities.Category", b =>
{
b.Property("Id")
@@ -178,6 +210,18 @@ protected override void BuildModel(ModelBuilder modelBuilder)
});
});
+ modelBuilder.Entity("LeaderboardBackend.Models.Entities.AccountRecovery", b =>
+ {
+ b.HasOne("LeaderboardBackend.Models.Entities.User", "User")
+ .WithMany("AccountRecoveries")
+ .HasForeignKey("UserId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired()
+ .HasConstraintName("fk_account_recoveries_users_user_id");
+
+ b.Navigation("User");
+ });
+
modelBuilder.Entity("LeaderboardBackend.Models.Entities.Category", b =>
{
b.HasOne("LeaderboardBackend.Models.Entities.Leaderboard", "Leaderboard")
@@ -206,6 +250,11 @@ protected override void BuildModel(ModelBuilder modelBuilder)
{
b.Navigation("Categories");
});
+
+ modelBuilder.Entity("LeaderboardBackend.Models.Entities.User", b =>
+ {
+ b.Navigation("AccountRecoveries");
+ });
#pragma warning restore 612, 618
}
}
diff --git a/LeaderboardBackend/Models/Entities/AccountRecovery.cs b/LeaderboardBackend/Models/Entities/AccountRecovery.cs
new file mode 100644
index 00000000..f4166ecd
--- /dev/null
+++ b/LeaderboardBackend/Models/Entities/AccountRecovery.cs
@@ -0,0 +1,49 @@
+using System.ComponentModel.DataAnnotations;
+using System.Text.Json.Serialization;
+using NodaTime;
+
+namespace LeaderboardBackend.Models.Entities;
+
+///
+/// Represents an account recovery attempt for a `User`.
+///
+public class AccountRecovery
+{
+ ///
+ /// The unique identifier of the `AccountRecovery`.
+ /// Generated on creation.
+ ///
+ public Guid Id { get; set; }
+
+ ///
+ /// The ID of the `User` tied to this `AccountRecovery`.
+ ///
+ [Required]
+ public Guid UserId { get; set; }
+
+ ///
+ /// The `User` relationship model.
+ ///
+ [Required]
+ public User User { get; set; } = null!;
+
+ ///
+ /// The time this `AccountRecovery` was created, i.e. the time the user
+ /// requested an account recovery.
+ ///
+ [Required]
+ public Instant CreatedAt { get; set; }
+
+ ///
+ /// The time this `AccountRecovery` was used.
+ ///
+ public Instant? UsedAt { get; set; }
+
+ ///
+ /// The time this `AccountRecovery` expires. Defaults to an hour from its
+ /// creation.
+ ///
+ /// john.doe@example.com
+ [Required]
+ public Instant ExpiresAt { get; set; }
+}
diff --git a/LeaderboardBackend/Models/Entities/ApplicationContext.cs b/LeaderboardBackend/Models/Entities/ApplicationContext.cs
index 071384dc..00e12842 100644
--- a/LeaderboardBackend/Models/Entities/ApplicationContext.cs
+++ b/LeaderboardBackend/Models/Entities/ApplicationContext.cs
@@ -18,6 +18,7 @@ static ApplicationContext()
public ApplicationContext(DbContextOptions options)
: base(options) { }
+ public DbSet AccountRecoveries { get; set; } = null!;
public DbSet Categories { get; set; } = null!;
public DbSet Leaderboards { get; set; } = null!;
public DbSet Runs { get; set; } = null!;