refactor(db): migrate email connections

Move Gmail, Microsoft Graph, and IMAP connection creation into an additive provider-aware migration. Preserve encrypted credentials and retain scoped historical repairs.
This commit is contained in:
cesnimda
2026-08-30 16:56:14 +02:00
parent 2e2d649f6f
commit 6c50e406b5
9 changed files with 274 additions and 163 deletions
@@ -0,0 +1,164 @@
using System;
using JobTrackerApi.Data;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace JobTrackerApi.Migrations;
/// <summary>
/// Moves provider connection-table creation into the migration chain while preserving encrypted
/// credentials and sync state stored by earlier startup-reconciled installations.
/// </summary>
[DbContext(typeof(JobTrackerContext))]
[Migration("20260830124000_AdoptEmailConnectionSchema")]
public sealed class AdoptEmailConnectionSchema : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
if (ActiveProvider.Contains("MySql", StringComparison.OrdinalIgnoreCase))
{
migrationBuilder.Sql("""
CREATE TABLE IF NOT EXISTS `GmailConnections` (
`Id` int NOT NULL AUTO_INCREMENT,
`OwnerUserId` varchar(255) NOT NULL,
`GmailAddress` varchar(512) NOT NULL,
`EncryptedRefreshToken` longtext NOT NULL,
`EncryptedAccessToken` longtext NULL,
`AccessTokenExpiresAt` datetime(6) NULL,
`Scope` longtext NOT NULL,
`ConnectedAt` datetime(6) NOT NULL,
`LastSyncedAt` datetime(6) NULL,
`LastSyncAttemptedAt` datetime(6) NULL,
`LastSyncSucceededAt` datetime(6) NULL,
`LastSyncMode` varchar(255) NULL,
`LastSyncSource` varchar(255) NULL,
`LastSyncStatus` varchar(255) NULL,
`LastSyncError` longtext NULL,
PRIMARY KEY (`Id`)
) CHARACTER SET=utf8mb4;
CREATE INDEX IF NOT EXISTS `IX_GmailConnections_OwnerUserId`
ON `GmailConnections` (`OwnerUserId`(191));
CREATE UNIQUE INDEX IF NOT EXISTS `IX_GmailConnections_OwnerUserId_GmailAddress`
ON `GmailConnections` (`OwnerUserId`(191), `GmailAddress`(191));
CREATE TABLE IF NOT EXISTS `MicrosoftGraphConnections` (
`Id` int NOT NULL AUTO_INCREMENT,
`OwnerUserId` varchar(255) NOT NULL,
`MailAddress` varchar(512) NOT NULL,
`EncryptedRefreshToken` longtext NOT NULL,
`EncryptedAccessToken` longtext NULL,
`AccessTokenExpiresAt` datetime(6) NULL,
`Scope` longtext NOT NULL,
`ConnectedAt` datetime(6) NOT NULL,
`LastSyncedAt` datetime(6) NULL,
`LastSyncAttemptedAt` datetime(6) NULL,
`LastSyncSucceededAt` datetime(6) NULL,
`LastSyncMode` varchar(255) NULL,
`LastSyncSource` varchar(255) NULL,
`LastSyncStatus` varchar(255) NULL,
`LastSyncError` longtext NULL,
PRIMARY KEY (`Id`)
) CHARACTER SET=utf8mb4;
CREATE INDEX IF NOT EXISTS `IX_MicrosoftGraphConnections_OwnerUserId`
ON `MicrosoftGraphConnections` (`OwnerUserId`(191));
CREATE UNIQUE INDEX IF NOT EXISTS `IX_MicrosoftGraphConnections_OwnerUserId_MailAddress`
ON `MicrosoftGraphConnections` (`OwnerUserId`(191), `MailAddress`(191));
CREATE TABLE IF NOT EXISTS `ImapConnections` (
`Id` int NOT NULL AUTO_INCREMENT,
`OwnerUserId` varchar(255) NOT NULL,
`Host` varchar(255) NOT NULL,
`Port` int NOT NULL,
`UseSsl` tinyint(1) NOT NULL,
`Username` varchar(255) NOT NULL,
`EncryptedPassword` longtext NOT NULL,
`ConnectedAt` datetime(6) NOT NULL,
`LastSyncedAt` datetime(6) NULL,
`LastSyncAttemptedAt` datetime(6) NULL,
`LastSyncSucceededAt` datetime(6) NULL,
`LastSyncMode` varchar(255) NULL,
`LastSyncSource` varchar(255) NULL,
`LastSyncStatus` varchar(255) NULL,
`LastSyncError` longtext NULL,
PRIMARY KEY (`Id`)
) CHARACTER SET=utf8mb4;
CREATE UNIQUE INDEX IF NOT EXISTS `IX_ImapConnections_OwnerUserId`
ON `ImapConnections` (`OwnerUserId`(191));
""");
return;
}
migrationBuilder.Sql("""
CREATE TABLE IF NOT EXISTS "GmailConnections" (
"Id" INTEGER NOT NULL CONSTRAINT "PK_GmailConnections" PRIMARY KEY AUTOINCREMENT,
"OwnerUserId" TEXT NOT NULL,
"GmailAddress" TEXT NOT NULL,
"EncryptedRefreshToken" TEXT NOT NULL,
"EncryptedAccessToken" TEXT NULL,
"AccessTokenExpiresAt" TEXT NULL,
"Scope" TEXT NOT NULL,
"ConnectedAt" TEXT NOT NULL,
"LastSyncedAt" TEXT NULL,
"LastSyncAttemptedAt" TEXT NULL,
"LastSyncSucceededAt" TEXT NULL,
"LastSyncMode" TEXT NULL,
"LastSyncSource" TEXT NULL,
"LastSyncStatus" TEXT NULL,
"LastSyncError" TEXT NULL
);
CREATE INDEX IF NOT EXISTS "IX_GmailConnections_OwnerUserId"
ON "GmailConnections" ("OwnerUserId");
CREATE UNIQUE INDEX IF NOT EXISTS "IX_GmailConnections_OwnerUserId_GmailAddress"
ON "GmailConnections" ("OwnerUserId", "GmailAddress");
CREATE TABLE IF NOT EXISTS "MicrosoftGraphConnections" (
"Id" INTEGER NOT NULL CONSTRAINT "PK_MicrosoftGraphConnections" PRIMARY KEY AUTOINCREMENT,
"OwnerUserId" TEXT NOT NULL,
"MailAddress" TEXT NOT NULL,
"EncryptedRefreshToken" TEXT NOT NULL,
"EncryptedAccessToken" TEXT NULL,
"AccessTokenExpiresAt" TEXT NULL,
"Scope" TEXT NOT NULL,
"ConnectedAt" TEXT NOT NULL,
"LastSyncedAt" TEXT NULL,
"LastSyncAttemptedAt" TEXT NULL,
"LastSyncSucceededAt" TEXT NULL,
"LastSyncMode" TEXT NULL,
"LastSyncSource" TEXT NULL,
"LastSyncStatus" TEXT NULL,
"LastSyncError" TEXT NULL
);
CREATE INDEX IF NOT EXISTS "IX_MicrosoftGraphConnections_OwnerUserId"
ON "MicrosoftGraphConnections" ("OwnerUserId");
CREATE UNIQUE INDEX IF NOT EXISTS "IX_MicrosoftGraphConnections_OwnerUserId_MailAddress"
ON "MicrosoftGraphConnections" ("OwnerUserId", "MailAddress");
CREATE TABLE IF NOT EXISTS "ImapConnections" (
"Id" INTEGER NOT NULL CONSTRAINT "PK_ImapConnections" PRIMARY KEY AUTOINCREMENT,
"OwnerUserId" TEXT NOT NULL,
"Host" TEXT NOT NULL,
"Port" INTEGER NOT NULL,
"UseSsl" INTEGER NOT NULL,
"Username" TEXT NOT NULL,
"EncryptedPassword" TEXT NOT NULL,
"ConnectedAt" TEXT NOT NULL,
"LastSyncedAt" TEXT NULL,
"LastSyncAttemptedAt" TEXT NULL,
"LastSyncSucceededAt" TEXT NULL,
"LastSyncMode" TEXT NULL,
"LastSyncSource" TEXT NULL,
"LastSyncStatus" TEXT NULL,
"LastSyncError" TEXT NULL
);
CREATE UNIQUE INDEX IF NOT EXISTS "IX_ImapConnections_OwnerUserId"
ON "ImapConnections" ("OwnerUserId");
""");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
// Preserve encrypted credentials and sync state from pre-migration installations.
}
}