feat/Update_Controllers_to_Allow_for_Premium_Membership

This commit is contained in:
cesnimda
2026-08-03 09:17:28 +02:00
parent de937d25dc
commit c3f4a57195
187 changed files with 26062 additions and 991 deletions
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,41 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace JobTrackerApi.Migrations
{
/// <inheritdoc />
public partial class AddPendingEmailChange : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
var mysql = ActiveProvider.Contains("MySql", StringComparison.OrdinalIgnoreCase);
migrationBuilder.AddColumn<string>(
name: "PendingEmail",
table: "AspNetUsers",
type: mysql ? "varchar(320)" : "TEXT",
maxLength: 320,
nullable: true);
migrationBuilder.AddColumn<DateTimeOffset>(
name: "PendingEmailRequestedAtUtc",
table: "AspNetUsers",
type: mysql ? "datetime(6)" : "TEXT",
nullable: true);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "PendingEmail",
table: "AspNetUsers");
migrationBuilder.DropColumn(
name: "PendingEmailRequestedAtUtc",
table: "AspNetUsers");
}
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,51 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace JobTrackerApi.Migrations
{
/// <inheritdoc />
public partial class AddCanonicalMicrosoftIdentity : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
var stringType = ActiveProvider.Contains("MySql", StringComparison.OrdinalIgnoreCase) ? "varchar(36)" : "TEXT";
migrationBuilder.AddColumn<string>(
name: "MicrosoftObjectId",
table: "AspNetUsers",
type: stringType,
maxLength: 36,
nullable: true);
migrationBuilder.AddColumn<string>(
name: "MicrosoftTenantId",
table: "AspNetUsers",
type: stringType,
maxLength: 36,
nullable: true);
migrationBuilder.CreateIndex(
name: "IX_AspNetUsers_MicrosoftTenantId_MicrosoftObjectId",
table: "AspNetUsers",
columns: new[] { "MicrosoftTenantId", "MicrosoftObjectId" },
unique: true);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropIndex(
name: "IX_AspNetUsers_MicrosoftTenantId_MicrosoftObjectId",
table: "AspNetUsers");
migrationBuilder.DropColumn(
name: "MicrosoftObjectId",
table: "AspNetUsers");
migrationBuilder.DropColumn(
name: "MicrosoftTenantId",
table: "AspNetUsers");
}
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,112 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace JobTrackerApi.Migrations
{
/// <inheritdoc />
public partial class AddUserOperations : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
// This table is EF-owned. The migration branches explicitly because SQLite-scaffolded
// TEXT/INTEGER types are not a safe MariaDB contract; do not duplicate it in the startup reconciler.
if (ActiveProvider.Contains("MySql", StringComparison.OrdinalIgnoreCase))
{
migrationBuilder.Sql("""
CREATE TABLE `UserOperations` (
`Id` char(36) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL,
`OwnerUserId` varchar(255) NOT NULL,
`TaskType` varchar(64) NOT NULL,
`IdempotencyKey` varchar(128) NOT NULL,
`Status` varchar(32) NOT NULL,
`Priority` int NOT NULL,
`EntitlementDecision` varchar(32) NOT NULL,
`PrivacyPolicy` varchar(32) NOT NULL,
`SubjectType` varchar(64) NULL,
`SubjectId` varchar(128) NULL,
`Provider` varchar(128) NULL,
`Model` varchar(128) NULL,
`AttemptCount` int NOT NULL,
`MaxAttempts` int NOT NULL,
`CreatedAtUtc` datetime(6) NOT NULL,
`AvailableAtUtc` datetime(6) NOT NULL,
`StartedAtUtc` datetime(6) NULL,
`CompletedAtUtc` datetime(6) NULL,
`DeadlineAtUtc` datetime(6) NULL,
`CancellationRequestedAtUtc` datetime(6) NULL,
`LeaseToken` char(32) CHARACTER SET ascii COLLATE ascii_general_ci NULL,
`LeaseExpiresAtUtc` datetime(6) NULL,
`LastHeartbeatAtUtc` datetime(6) NULL,
`ProgressStage` varchar(64) NULL,
`ProgressPercent` int NULL,
`FailureCategory` varchar(64) NULL,
`FailureMessage` varchar(512) NULL,
`ResultReference` varchar(256) NULL,
CONSTRAINT `PK_UserOperations` PRIMARY KEY (`Id`)
) CHARACTER SET=utf8mb4;
""");
}
else
{
migrationBuilder.CreateTable(
name: "UserOperations",
columns: table => new
{
Id = table.Column<Guid>(type: "TEXT", nullable: false),
OwnerUserId = table.Column<string>(type: "TEXT", maxLength: 255, nullable: false),
TaskType = table.Column<string>(type: "TEXT", maxLength: 64, nullable: false),
IdempotencyKey = table.Column<string>(type: "TEXT", maxLength: 128, nullable: false),
Status = table.Column<string>(type: "TEXT", maxLength: 32, nullable: false),
Priority = table.Column<int>(type: "INTEGER", nullable: false),
EntitlementDecision = table.Column<string>(type: "TEXT", maxLength: 32, nullable: false),
PrivacyPolicy = table.Column<string>(type: "TEXT", maxLength: 32, nullable: false),
SubjectType = table.Column<string>(type: "TEXT", maxLength: 64, nullable: true),
SubjectId = table.Column<string>(type: "TEXT", maxLength: 128, nullable: true),
Provider = table.Column<string>(type: "TEXT", maxLength: 128, nullable: true),
Model = table.Column<string>(type: "TEXT", maxLength: 128, nullable: true),
AttemptCount = table.Column<int>(type: "INTEGER", nullable: false),
MaxAttempts = table.Column<int>(type: "INTEGER", nullable: false),
CreatedAtUtc = table.Column<DateTime>(type: "TEXT", nullable: false),
AvailableAtUtc = table.Column<DateTime>(type: "TEXT", nullable: false),
StartedAtUtc = table.Column<DateTime>(type: "TEXT", nullable: true),
CompletedAtUtc = table.Column<DateTime>(type: "TEXT", nullable: true),
DeadlineAtUtc = table.Column<DateTime>(type: "TEXT", nullable: true),
CancellationRequestedAtUtc = table.Column<DateTime>(type: "TEXT", nullable: true),
LeaseToken = table.Column<string>(type: "TEXT", maxLength: 32, nullable: true),
LeaseExpiresAtUtc = table.Column<DateTime>(type: "TEXT", nullable: true),
LastHeartbeatAtUtc = table.Column<DateTime>(type: "TEXT", nullable: true),
ProgressStage = table.Column<string>(type: "TEXT", maxLength: 64, nullable: true),
ProgressPercent = table.Column<int>(type: "INTEGER", nullable: true),
FailureCategory = table.Column<string>(type: "TEXT", maxLength: 64, nullable: true),
FailureMessage = table.Column<string>(type: "TEXT", maxLength: 512, nullable: true),
ResultReference = table.Column<string>(type: "TEXT", maxLength: 256, nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_UserOperations", x => x.Id);
});
}
migrationBuilder.CreateIndex(
name: "IX_UserOperations_OwnerUserId_TaskType_IdempotencyKey",
table: "UserOperations",
columns: new[] { "OwnerUserId", "TaskType", "IdempotencyKey" },
unique: true);
migrationBuilder.CreateIndex(
name: "IX_UserOperations_Status_AvailableAtUtc_Priority",
table: "UserOperations",
columns: new[] { "Status", "AvailableAtUtc", "Priority" });
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "UserOperations");
}
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,84 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace JobTrackerApi.Migrations
{
/// <inheritdoc />
public partial class AddUserNotifications : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
// EF owns this additive table. Keep MariaDB columns bounded instead of applying
// the SQLite-scaffolded TEXT types, and do not duplicate it in startup reconciliation.
if (ActiveProvider.Contains("MySql", StringComparison.OrdinalIgnoreCase))
{
migrationBuilder.Sql("""
CREATE TABLE `UserNotifications` (
`Id` char(36) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL,
`OwnerUserId` varchar(255) NOT NULL,
`OperationId` char(36) CHARACTER SET ascii COLLATE ascii_general_ci NULL,
`Kind` varchar(64) NOT NULL,
`Title` varchar(160) NOT NULL,
`Message` varchar(512) NOT NULL,
`LinkPath` varchar(256) NULL,
`CreatedAtUtc` datetime(6) NOT NULL,
`ReadAtUtc` datetime(6) NULL,
`DismissedAtUtc` datetime(6) NULL,
CONSTRAINT `PK_UserNotifications` PRIMARY KEY (`Id`),
CONSTRAINT `FK_UserNotifications_UserOperations_OperationId`
FOREIGN KEY (`OperationId`) REFERENCES `UserOperations` (`Id`) ON DELETE SET NULL
) CHARACTER SET=utf8mb4;
""");
}
else
{
migrationBuilder.CreateTable(
name: "UserNotifications",
columns: table => new
{
Id = table.Column<Guid>(type: "TEXT", nullable: false),
OwnerUserId = table.Column<string>(type: "TEXT", maxLength: 255, nullable: false),
OperationId = table.Column<Guid>(type: "TEXT", nullable: true),
Kind = table.Column<string>(type: "TEXT", maxLength: 64, nullable: false),
Title = table.Column<string>(type: "TEXT", maxLength: 160, nullable: false),
Message = table.Column<string>(type: "TEXT", maxLength: 512, nullable: false),
LinkPath = table.Column<string>(type: "TEXT", maxLength: 256, nullable: true),
CreatedAtUtc = table.Column<DateTime>(type: "TEXT", nullable: false),
ReadAtUtc = table.Column<DateTime>(type: "TEXT", nullable: true),
DismissedAtUtc = table.Column<DateTime>(type: "TEXT", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_UserNotifications", x => x.Id);
table.ForeignKey(
name: "FK_UserNotifications_UserOperations_OperationId",
column: x => x.OperationId,
principalTable: "UserOperations",
principalColumn: "Id",
onDelete: ReferentialAction.SetNull);
});
}
migrationBuilder.CreateIndex(
name: "IX_UserNotifications_OperationId",
table: "UserNotifications",
column: "OperationId",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_UserNotifications_OwnerUserId_DismissedAtUtc_ReadAtUtc_CreatedAtUtc",
table: "UserNotifications",
columns: new[] { "OwnerUserId", "DismissedAtUtc", "ReadAtUtc", "CreatedAtUtc" });
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "UserNotifications");
}
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,49 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace JobTrackerApi.Migrations
{
/// <inheritdoc />
public partial class AddAiPrivacyPreferences : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
if (ActiveProvider.Contains("MySql", System.StringComparison.OrdinalIgnoreCase))
{
migrationBuilder.Sql("ALTER TABLE `AspNetUsers` ADD COLUMN `AiEnabled` tinyint(1) NOT NULL DEFAULT 1;");
migrationBuilder.Sql("ALTER TABLE `AspNetUsers` ADD COLUMN `ExternalAiProcessingAllowed` tinyint(1) NOT NULL DEFAULT 0;");
}
else
{
// Preserve existing AI behaviour while external processing remains opt-in.
migrationBuilder.AddColumn<bool>(
name: "AiEnabled",
table: "AspNetUsers",
type: "INTEGER",
nullable: false,
defaultValue: true);
migrationBuilder.AddColumn<bool>(
name: "ExternalAiProcessingAllowed",
table: "AspNetUsers",
type: "INTEGER",
nullable: false,
defaultValue: false);
}
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "AiEnabled",
table: "AspNetUsers");
migrationBuilder.DropColumn(
name: "ExternalAiProcessingAllowed",
table: "AspNetUsers");
}
}
}
@@ -190,6 +190,9 @@ namespace JobTrackerApi.Migrations
b.Property<int>("AccessFailedCount")
.HasColumnType("INTEGER");
b.Property<bool>("AiEnabled")
.HasColumnType("INTEGER");
b.Property<string>("AvatarImageDataUrl")
.HasColumnType("TEXT");
@@ -216,6 +219,9 @@ namespace JobTrackerApi.Migrations
b.Property<bool>("EmailConfirmed")
.HasColumnType("INTEGER");
b.Property<bool>("ExternalAiProcessingAllowed")
.HasColumnType("INTEGER");
b.Property<string>("FirstName")
.HasColumnType("TEXT");
@@ -243,9 +249,17 @@ namespace JobTrackerApi.Migrations
b.Property<DateTimeOffset?>("MicrosoftLinkedAt")
.HasColumnType("TEXT");
b.Property<string>("MicrosoftObjectId")
.HasMaxLength(36)
.HasColumnType("TEXT");
b.Property<string>("MicrosoftSubject")
.HasColumnType("TEXT");
b.Property<string>("MicrosoftTenantId")
.HasMaxLength(36)
.HasColumnType("TEXT");
b.Property<string>("NormalizedEmail")
.HasMaxLength(256)
.HasColumnType("TEXT");
@@ -257,6 +271,13 @@ namespace JobTrackerApi.Migrations
b.Property<string>("PasswordHash")
.HasColumnType("TEXT");
b.Property<string>("PendingEmail")
.HasMaxLength(320)
.HasColumnType("TEXT");
b.Property<DateTimeOffset?>("PendingEmailRequestedAtUtc")
.HasColumnType("TEXT");
b.Property<string>("PhoneNumber")
.HasColumnType("TEXT");
@@ -309,6 +330,9 @@ namespace JobTrackerApi.Migrations
.IsUnique()
.HasDatabaseName("UserNameIndex");
b.HasIndex("MicrosoftTenantId", "MicrosoftObjectId")
.IsUnique();
b.ToTable("AspNetUsers", (string)null);
});
@@ -1828,6 +1852,176 @@ namespace JobTrackerApi.Migrations
b.ToTable("TwoFactorRecoveryCodes");
});
modelBuilder.Entity("JobTrackerApi.Models.UserNotification", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<DateTime>("CreatedAtUtc")
.HasColumnType("TEXT");
b.Property<DateTime?>("DismissedAtUtc")
.HasColumnType("TEXT");
b.Property<string>("Kind")
.IsRequired()
.HasMaxLength(64)
.HasColumnType("TEXT");
b.Property<string>("LinkPath")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<string>("Message")
.IsRequired()
.HasMaxLength(512)
.HasColumnType("TEXT");
b.Property<Guid?>("OperationId")
.HasColumnType("TEXT");
b.Property<string>("OwnerUserId")
.IsRequired()
.HasMaxLength(255)
.HasColumnType("TEXT");
b.Property<DateTime?>("ReadAtUtc")
.HasColumnType("TEXT");
b.Property<string>("Title")
.IsRequired()
.HasMaxLength(160)
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("OperationId")
.IsUnique();
b.HasIndex("OwnerUserId", "DismissedAtUtc", "ReadAtUtc", "CreatedAtUtc");
b.ToTable("UserNotifications");
});
modelBuilder.Entity("JobTrackerApi.Models.UserOperation", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<int>("AttemptCount")
.HasColumnType("INTEGER");
b.Property<DateTime>("AvailableAtUtc")
.HasColumnType("TEXT");
b.Property<DateTime?>("CancellationRequestedAtUtc")
.HasColumnType("TEXT");
b.Property<DateTime?>("CompletedAtUtc")
.HasColumnType("TEXT");
b.Property<DateTime>("CreatedAtUtc")
.HasColumnType("TEXT");
b.Property<DateTime?>("DeadlineAtUtc")
.HasColumnType("TEXT");
b.Property<string>("EntitlementDecision")
.IsRequired()
.HasMaxLength(32)
.HasColumnType("TEXT");
b.Property<string>("FailureCategory")
.HasMaxLength(64)
.HasColumnType("TEXT");
b.Property<string>("FailureMessage")
.HasMaxLength(512)
.HasColumnType("TEXT");
b.Property<string>("IdempotencyKey")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("TEXT");
b.Property<DateTime?>("LastHeartbeatAtUtc")
.HasColumnType("TEXT");
b.Property<DateTime?>("LeaseExpiresAtUtc")
.HasColumnType("TEXT");
b.Property<string>("LeaseToken")
.HasMaxLength(32)
.HasColumnType("TEXT");
b.Property<int>("MaxAttempts")
.HasColumnType("INTEGER");
b.Property<string>("Model")
.HasMaxLength(128)
.HasColumnType("TEXT");
b.Property<string>("OwnerUserId")
.IsRequired()
.HasMaxLength(255)
.HasColumnType("TEXT");
b.Property<int>("Priority")
.HasColumnType("INTEGER");
b.Property<string>("PrivacyPolicy")
.IsRequired()
.HasMaxLength(32)
.HasColumnType("TEXT");
b.Property<int?>("ProgressPercent")
.HasColumnType("INTEGER");
b.Property<string>("ProgressStage")
.HasMaxLength(64)
.HasColumnType("TEXT");
b.Property<string>("Provider")
.HasMaxLength(128)
.HasColumnType("TEXT");
b.Property<string>("ResultReference")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<DateTime?>("StartedAtUtc")
.HasColumnType("TEXT");
b.Property<string>("Status")
.IsRequired()
.HasMaxLength(32)
.HasColumnType("TEXT");
b.Property<string>("SubjectId")
.HasMaxLength(128)
.HasColumnType("TEXT");
b.Property<string>("SubjectType")
.HasMaxLength(64)
.HasColumnType("TEXT");
b.Property<string>("TaskType")
.IsRequired()
.HasMaxLength(64)
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("OwnerUserId", "TaskType", "IdempotencyKey")
.IsUnique();
b.HasIndex("Status", "AvailableAtUtc", "Priority");
b.ToTable("UserOperations");
});
modelBuilder.Entity("JobTrackerApi.Models.UserRuleSettings", b =>
{
b.Property<string>("OwnerUserId")
@@ -2262,6 +2456,16 @@ namespace JobTrackerApi.Migrations
b.Navigation("JobApplication");
});
modelBuilder.Entity("JobTrackerApi.Models.UserNotification", b =>
{
b.HasOne("JobTrackerApi.Models.UserOperation", "Operation")
.WithOne()
.HasForeignKey("JobTrackerApi.Models.UserNotification", "OperationId")
.OnDelete(DeleteBehavior.SetNull);
b.Navigation("Operation");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)