diff --git a/JobTrackerApi/Program.cs b/JobTrackerApi/Program.cs index 331d66c..64fdfe5 100644 --- a/JobTrackerApi/Program.cs +++ b/JobTrackerApi/Program.cs @@ -314,6 +314,99 @@ using (var scope = app.Services.CreateScope()) var provider = (app.Configuration["Database:Provider"] ?? "sqlite").Trim().ToLowerInvariant(); var useSqliteBootstrap = provider is not "mysql" and not "mariadb"; + static void EnsureIdentityTablesMySql(DbConnection c) + { + using var cmd = c.CreateCommand(); + cmd.CommandText = @" +CREATE TABLE IF NOT EXISTS `AspNetRoles` ( + `Id` varchar(255) NOT NULL, + `Name` varchar(256) NULL, + `NormalizedName` varchar(256) NULL, + `ConcurrencyStamp` longtext NULL, + PRIMARY KEY (`Id`) +) CHARACTER SET=utf8mb4; + +CREATE TABLE IF NOT EXISTS `AspNetUsers` ( + `Id` varchar(255) NOT NULL, + `UserName` varchar(256) NULL, + `NormalizedUserName` varchar(256) NULL, + `Email` varchar(256) NULL, + `NormalizedEmail` varchar(256) NULL, + `EmailConfirmed` tinyint(1) NOT NULL, + `PasswordHash` longtext NULL, + `SecurityStamp` longtext NULL, + `ConcurrencyStamp` longtext NULL, + `PhoneNumber` longtext NULL, + `PhoneNumberConfirmed` tinyint(1) NOT NULL, + `TwoFactorEnabled` tinyint(1) NOT NULL, + `LockoutEnd` datetime(6) NULL, + `LockoutEnabled` tinyint(1) NOT NULL, + `AccessFailedCount` int NOT NULL, + `FirstName` longtext NULL, + `LastName` longtext NULL, + `DisplayName` longtext NULL, + `ProfileCvText` longtext NULL, + `GoogleSubject` longtext NULL, + `GoogleEmail` longtext NULL, + `GoogleLinkedAt` datetime(6) NULL, + PRIMARY KEY (`Id`) +) CHARACTER SET=utf8mb4; + +CREATE TABLE IF NOT EXISTS `AspNetRoleClaims` ( + `Id` int NOT NULL AUTO_INCREMENT, + `RoleId` varchar(255) NOT NULL, + `ClaimType` longtext NULL, + `ClaimValue` longtext NULL, + PRIMARY KEY (`Id`), + CONSTRAINT `FK_AspNetRoleClaims_AspNetRoles_RoleId` FOREIGN KEY (`RoleId`) REFERENCES `AspNetRoles` (`Id`) ON DELETE CASCADE +) CHARACTER SET=utf8mb4; + +CREATE TABLE IF NOT EXISTS `AspNetUserClaims` ( + `Id` int NOT NULL AUTO_INCREMENT, + `UserId` varchar(255) NOT NULL, + `ClaimType` longtext NULL, + `ClaimValue` longtext NULL, + PRIMARY KEY (`Id`), + CONSTRAINT `FK_AspNetUserClaims_AspNetUsers_UserId` FOREIGN KEY (`UserId`) REFERENCES `AspNetUsers` (`Id`) ON DELETE CASCADE +) CHARACTER SET=utf8mb4; + +CREATE TABLE IF NOT EXISTS `AspNetUserLogins` ( + `LoginProvider` varchar(255) NOT NULL, + `ProviderKey` varchar(255) NOT NULL, + `ProviderDisplayName` longtext NULL, + `UserId` varchar(255) NOT NULL, + PRIMARY KEY (`LoginProvider`, `ProviderKey`), + CONSTRAINT `FK_AspNetUserLogins_AspNetUsers_UserId` FOREIGN KEY (`UserId`) REFERENCES `AspNetUsers` (`Id`) ON DELETE CASCADE +) CHARACTER SET=utf8mb4; + +CREATE TABLE IF NOT EXISTS `AspNetUserRoles` ( + `UserId` varchar(255) NOT NULL, + `RoleId` varchar(255) NOT NULL, + PRIMARY KEY (`UserId`, `RoleId`), + CONSTRAINT `FK_AspNetUserRoles_AspNetRoles_RoleId` FOREIGN KEY (`RoleId`) REFERENCES `AspNetRoles` (`Id`) ON DELETE CASCADE, + CONSTRAINT `FK_AspNetUserRoles_AspNetUsers_UserId` FOREIGN KEY (`UserId`) REFERENCES `AspNetUsers` (`Id`) ON DELETE CASCADE +) CHARACTER SET=utf8mb4; + +CREATE TABLE IF NOT EXISTS `AspNetUserTokens` ( + `UserId` varchar(255) NOT NULL, + `LoginProvider` varchar(255) NOT NULL, + `Name` varchar(255) NOT NULL, + `Value` longtext NULL, + PRIMARY KEY (`UserId`, `LoginProvider`, `Name`), + CONSTRAINT `FK_AspNetUserTokens_AspNetUsers_UserId` FOREIGN KEY (`UserId`) REFERENCES `AspNetUsers` (`Id`) ON DELETE CASCADE +) CHARACTER SET=utf8mb4; + +CREATE UNIQUE INDEX IF NOT EXISTS `RoleNameIndex` ON `AspNetRoles` (`NormalizedName`); +CREATE INDEX IF NOT EXISTS `IX_AspNetRoleClaims_RoleId` ON `AspNetRoleClaims` (`RoleId`); +CREATE INDEX IF NOT EXISTS `EmailIndex` ON `AspNetUsers` (`NormalizedEmail`); +CREATE UNIQUE INDEX IF NOT EXISTS `UserNameIndex` ON `AspNetUsers` (`NormalizedUserName`); +CREATE INDEX IF NOT EXISTS `IX_AspNetUserClaims_UserId` ON `AspNetUserClaims` (`UserId`); +CREATE INDEX IF NOT EXISTS `IX_AspNetUserLogins_UserId` ON `AspNetUserLogins` (`UserId`); +CREATE INDEX IF NOT EXISTS `IX_AspNetUserRoles_RoleId` ON `AspNetUserRoles` (`RoleId`); +"; + cmd.ExecuteNonQuery(); + } + if (useSqliteBootstrap) { // Bridge older dev DBs that were modified via ad-hoc ALTER TABLE (before migrations were applied). @@ -578,6 +671,12 @@ CREATE TABLE IF NOT EXISTS "GmailConnections" ( // Ensure data folder exists before creating/opening SQLite files. Directory.CreateDirectory(paths.DataRoot); } + else + { + using DbConnection conn = db.Database.GetDbConnection(); + conn.Open(); + EnsureIdentityTablesMySql(conn); + } db.Database.Migrate(); diff --git a/deploy/deploy.sh b/deploy/deploy.sh index f388beb..68bf9d6 100644 --- a/deploy/deploy.sh +++ b/deploy/deploy.sh @@ -12,9 +12,9 @@ if [ ! -f "$ENV_SOURCE" ]; then fi # Keep runtime secrets outside the repo checkout so workflow uploads cannot wipe them. -ln -sf "$ENV_SOURCE" "$ENV_TARGET" +ln -snf "$ENV_SOURCE" "$ENV_TARGET" -if [ ! -f "$ENV_TARGET" ]; then +if [ ! -L "$ENV_TARGET" ] && [ ! -f "$ENV_TARGET" ]; then echo "Failed to link deployment env file into $(pwd)/$ENV_TARGET" exit 1 fi