feat(account): add deletion lifecycle
CI and Deploy / test (pull_request) Successful in 5m18s
CI and Deploy / deploy (pull_request) Has been skipped

This commit is contained in:
cesnimda
2026-08-15 19:03:54 +02:00
parent 1ec9dd037e
commit 842e793f69
28 changed files with 4418 additions and 129 deletions
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,156 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace JobTrackerApi.Migrations
{
/// <inheritdoc />
public partial class AddAccountDeletionLifecycle : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
if (ActiveProvider.Contains("MySql", StringComparison.OrdinalIgnoreCase))
{
migrationBuilder.Sql("""
ALTER TABLE `AspNetUsers`
ADD COLUMN `DeletionRequestedAtUtc` datetime(6) NULL,
ADD COLUMN `DeletionStatus` varchar(32) NOT NULL DEFAULT 'active';
CREATE TABLE `AccountDeletionRequests` (
`Id` char(36) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL,
`OwnerUserId` varchar(255) NOT NULL,
`OwnerKey` char(64) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL,
`RequestedByUserId` varchar(255) NOT NULL,
`Status` varchar(32) NOT NULL,
`Stage` varchar(32) NOT NULL,
`AttemptCount` int NOT NULL,
`DatabaseRowCount` int NOT NULL,
`FileCount` int NOT NULL,
`WarningJson` longtext NULL,
`LastErrorCategory` varchar(64) NULL,
`LastErrorMessage` varchar(1024) NULL,
`RequestedAtUtc` datetime(6) NOT NULL,
`StartedAtUtc` datetime(6) NULL,
`CompletedAtUtc` datetime(6) NULL,
CONSTRAINT `PK_AccountDeletionRequests` PRIMARY KEY (`Id`)
) CHARACTER SET=utf8mb4;
CREATE TABLE `AccountDeletionFiles` (
`Id` bigint NOT NULL AUTO_INCREMENT,
`AccountDeletionRequestId` char(36) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL,
`Category` varchar(64) NOT NULL,
`OriginalPath` longtext NOT NULL,
`QuarantinePath` longtext NOT NULL,
`Status` varchar(32) NOT NULL,
`ByteSize` bigint NOT NULL,
`Sha256` char(64) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL,
CONSTRAINT `PK_AccountDeletionFiles` PRIMARY KEY (`Id`),
CONSTRAINT `FK_AccountDeletionFiles_AccountDeletionRequests_AccountDeletionRequestId`
FOREIGN KEY (`AccountDeletionRequestId`) REFERENCES `AccountDeletionRequests` (`Id`) ON DELETE CASCADE
) CHARACTER SET=utf8mb4;
""");
}
else
{
migrationBuilder.AddColumn<DateTimeOffset>(
name: "DeletionRequestedAtUtc",
table: "AspNetUsers",
type: "TEXT",
nullable: true);
migrationBuilder.AddColumn<string>(
name: "DeletionStatus",
table: "AspNetUsers",
type: "TEXT",
maxLength: 32,
nullable: false,
defaultValue: "active");
migrationBuilder.CreateTable(
name: "AccountDeletionRequests",
columns: table => new
{
Id = table.Column<Guid>(type: "TEXT", nullable: false),
OwnerUserId = table.Column<string>(type: "TEXT", maxLength: 255, nullable: false),
OwnerKey = table.Column<string>(type: "TEXT", maxLength: 64, nullable: false),
RequestedByUserId = table.Column<string>(type: "TEXT", maxLength: 255, nullable: false),
Status = table.Column<string>(type: "TEXT", maxLength: 32, nullable: false),
Stage = table.Column<string>(type: "TEXT", maxLength: 32, nullable: false),
AttemptCount = table.Column<int>(type: "INTEGER", nullable: false),
DatabaseRowCount = table.Column<int>(type: "INTEGER", nullable: false),
FileCount = table.Column<int>(type: "INTEGER", nullable: false),
WarningJson = table.Column<string>(type: "TEXT", nullable: true),
LastErrorCategory = table.Column<string>(type: "TEXT", maxLength: 64, nullable: true),
LastErrorMessage = table.Column<string>(type: "TEXT", nullable: true),
RequestedAtUtc = table.Column<DateTimeOffset>(type: "TEXT", nullable: false),
StartedAtUtc = table.Column<DateTimeOffset>(type: "TEXT", nullable: true),
CompletedAtUtc = table.Column<DateTimeOffset>(type: "TEXT", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AccountDeletionRequests", x => x.Id);
});
migrationBuilder.CreateTable(
name: "AccountDeletionFiles",
columns: table => new
{
Id = table.Column<long>(type: "INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
AccountDeletionRequestId = table.Column<Guid>(type: "TEXT", nullable: false),
Category = table.Column<string>(type: "TEXT", maxLength: 64, nullable: false),
OriginalPath = table.Column<string>(type: "TEXT", nullable: false),
QuarantinePath = table.Column<string>(type: "TEXT", nullable: false),
Status = table.Column<string>(type: "TEXT", maxLength: 32, nullable: false),
ByteSize = table.Column<long>(type: "INTEGER", nullable: false),
Sha256 = table.Column<string>(type: "TEXT", maxLength: 64, nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_AccountDeletionFiles", x => x.Id);
table.ForeignKey(
name: "FK_AccountDeletionFiles_AccountDeletionRequests_AccountDeletionRequestId",
column: x => x.AccountDeletionRequestId,
principalTable: "AccountDeletionRequests",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
}
migrationBuilder.CreateIndex(
name: "IX_AccountDeletionFiles_AccountDeletionRequestId_Status",
table: "AccountDeletionFiles",
columns: new[] { "AccountDeletionRequestId", "Status" });
migrationBuilder.CreateIndex(
name: "IX_AccountDeletionRequests_OwnerUserId_Status",
table: "AccountDeletionRequests",
columns: new[] { "OwnerUserId", "Status" });
migrationBuilder.CreateIndex(
name: "IX_AccountDeletionRequests_Status_RequestedAtUtc",
table: "AccountDeletionRequests",
columns: new[] { "Status", "RequestedAtUtc" });
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "AccountDeletionFiles");
migrationBuilder.DropTable(
name: "AccountDeletionRequests");
migrationBuilder.DropColumn(
name: "DeletionRequestedAtUtc",
table: "AspNetUsers");
migrationBuilder.DropColumn(
name: "DeletionStatus",
table: "AspNetUsers");
}
}
}
@@ -17,6 +17,116 @@ namespace JobTrackerApi.Migrations
#pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "9.0.14");
modelBuilder.Entity("JobTrackerApi.Models.AccountDeletionFile", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<Guid>("AccountDeletionRequestId")
.HasColumnType("TEXT");
b.Property<long>("ByteSize")
.HasColumnType("INTEGER");
b.Property<string>("Category")
.IsRequired()
.HasMaxLength(64)
.HasColumnType("TEXT");
b.Property<string>("OriginalPath")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("QuarantinePath")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("Sha256")
.IsRequired()
.HasMaxLength(64)
.HasColumnType("TEXT");
b.Property<string>("Status")
.IsRequired()
.HasMaxLength(32)
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("AccountDeletionRequestId", "Status");
b.ToTable("AccountDeletionFiles");
});
modelBuilder.Entity("JobTrackerApi.Models.AccountDeletionRequest", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<int>("AttemptCount")
.HasColumnType("INTEGER");
b.Property<DateTimeOffset?>("CompletedAtUtc")
.HasColumnType("TEXT");
b.Property<int>("DatabaseRowCount")
.HasColumnType("INTEGER");
b.Property<int>("FileCount")
.HasColumnType("INTEGER");
b.Property<string>("LastErrorCategory")
.HasMaxLength(64)
.HasColumnType("TEXT");
b.Property<string>("LastErrorMessage")
.HasColumnType("TEXT");
b.Property<string>("OwnerKey")
.IsRequired()
.HasMaxLength(64)
.HasColumnType("TEXT");
b.Property<string>("OwnerUserId")
.IsRequired()
.HasMaxLength(255)
.HasColumnType("TEXT");
b.Property<DateTimeOffset>("RequestedAtUtc")
.HasColumnType("TEXT");
b.Property<string>("RequestedByUserId")
.IsRequired()
.HasMaxLength(255)
.HasColumnType("TEXT");
b.Property<string>("Stage")
.IsRequired()
.HasMaxLength(32)
.HasColumnType("TEXT");
b.Property<DateTimeOffset?>("StartedAtUtc")
.HasColumnType("TEXT");
b.Property<string>("Status")
.IsRequired()
.HasMaxLength(32)
.HasColumnType("TEXT");
b.Property<string>("WarningJson")
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("OwnerUserId", "Status");
b.HasIndex("Status", "RequestedAtUtc");
b.ToTable("AccountDeletionRequests");
});
modelBuilder.Entity("JobTrackerApi.Models.AiInteraction", b =>
{
b.Property<int>("Id")
@@ -209,6 +319,16 @@ namespace JobTrackerApi.Migrations
b.Property<int?>("CurrentCvUploadArtifactId")
.HasColumnType("INTEGER");
b.Property<DateTimeOffset?>("DeletionRequestedAtUtc")
.HasColumnType("TEXT");
b.Property<string>("DeletionStatus")
.IsRequired()
.ValueGeneratedOnAdd()
.HasMaxLength(32)
.HasColumnType("TEXT")
.HasDefaultValue("active");
b.Property<string>("DisplayName")
.HasColumnType("TEXT");
@@ -2332,6 +2452,17 @@ namespace JobTrackerApi.Migrations
b.ToTable("AspNetUserTokens", (string)null);
});
modelBuilder.Entity("JobTrackerApi.Models.AccountDeletionFile", b =>
{
b.HasOne("JobTrackerApi.Models.AccountDeletionRequest", "Request")
.WithMany("Files")
.HasForeignKey("AccountDeletionRequestId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Request");
});
modelBuilder.Entity("JobTrackerApi.Models.AiInteraction", b =>
{
b.HasOne("JobTrackerApi.Models.JobApplication", "JobApplication")
@@ -2662,6 +2793,11 @@ namespace JobTrackerApi.Migrations
.IsRequired();
});
modelBuilder.Entity("JobTrackerApi.Models.AccountDeletionRequest", b =>
{
b.Navigation("Files");
});
modelBuilder.Entity("JobTrackerApi.Models.CareerProfile", b =>
{
b.Navigation("Certifications");