Make migrations idempotent and add missing tables
Build and Push Docker Image / build (push) Successful in 9m41s Details

- Make all pending migrations idempotent (safe to re-run)
- Disable decouple_projects_from_rounds migration (schema not changed)
- Add ProjectTag table for AI tagging
- Add AssignmentJob table for AI assignment progress

On server deployment, run: npx prisma migrate deploy

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Matt 2026-02-05 11:09:37 +01:00
parent 05862f1e55
commit e37154d812
7 changed files with 86 additions and 102 deletions

View File

@ -1,2 +1,4 @@
-- AlterTable -- AlterTable
DO $$ BEGIN
ALTER TABLE "SpecialAward" ADD COLUMN "useAiEligibility" BOOLEAN NOT NULL DEFAULT true; ALTER TABLE "SpecialAward" ADD COLUMN "useAiEligibility" BOOLEAN NOT NULL DEFAULT true;
EXCEPTION WHEN duplicate_column THEN NULL; END $$;

View File

@ -1,69 +1,4 @@
-- Step 1: Add sortOrder to Round -- This migration was created but reverted.
ALTER TABLE "Round" ADD COLUMN "sortOrder" INTEGER NOT NULL DEFAULT 0; -- The schema still uses Project.roundId and Project.status.
-- Keeping as no-op to maintain migration history.
-- Set initial sort order by creation date within each program SELECT 1;
UPDATE "Round" r SET "sortOrder" = sub.rn - 1
FROM (
SELECT id, ROW_NUMBER() OVER (PARTITION BY "programId" ORDER BY "createdAt") as rn
FROM "Round"
) sub
WHERE r.id = sub.id;
-- Step 2: Add programId to Project (nullable initially)
ALTER TABLE "Project" ADD COLUMN "programId" TEXT;
-- Populate programId from the round's program
UPDATE "Project" p SET "programId" = r."programId"
FROM "Round" r WHERE p."roundId" = r."id";
-- Make programId required
ALTER TABLE "Project" ALTER COLUMN "programId" SET NOT NULL;
-- Add foreign key constraint
ALTER TABLE "Project" ADD CONSTRAINT "Project_programId_fkey"
FOREIGN KEY ("programId") REFERENCES "Program"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- Step 3: Create RoundProject table
CREATE TABLE "RoundProject" (
"id" TEXT NOT NULL,
"roundId" TEXT NOT NULL,
"projectId" TEXT NOT NULL,
"status" "ProjectStatus" NOT NULL DEFAULT 'SUBMITTED',
"addedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "RoundProject_pkey" PRIMARY KEY ("id")
);
-- Populate RoundProject from existing Project.roundId and status
INSERT INTO "RoundProject" ("id", "roundId", "projectId", "status", "addedAt")
SELECT gen_random_uuid(), p."roundId", p."id", p."status", p."createdAt"
FROM "Project" p;
-- Add indexes and unique constraint
CREATE UNIQUE INDEX "RoundProject_roundId_projectId_key" ON "RoundProject"("roundId", "projectId");
CREATE INDEX "RoundProject_roundId_idx" ON "RoundProject"("roundId");
CREATE INDEX "RoundProject_projectId_idx" ON "RoundProject"("projectId");
CREATE INDEX "RoundProject_status_idx" ON "RoundProject"("status");
-- Add foreign keys
ALTER TABLE "RoundProject" ADD CONSTRAINT "RoundProject_roundId_fkey"
FOREIGN KEY ("roundId") REFERENCES "Round"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "RoundProject" ADD CONSTRAINT "RoundProject_projectId_fkey"
FOREIGN KEY ("projectId") REFERENCES "Project"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- Step 4: Drop old columns from Project
-- Drop the roundId foreign key constraint first
ALTER TABLE "Project" DROP CONSTRAINT "Project_roundId_fkey";
-- Drop the roundId index
DROP INDEX IF EXISTS "Project_roundId_idx";
-- Drop status index
DROP INDEX IF EXISTS "Project_status_idx";
-- Drop the columns
ALTER TABLE "Project" DROP COLUMN "roundId";
ALTER TABLE "Project" DROP COLUMN "status";
-- Add programId index
CREATE INDEX "Project_programId_idx" ON "Project"("programId");

View File

@ -1,5 +1,5 @@
-- CreateTable -- CreateTable
CREATE TABLE "AIUsageLog" ( CREATE TABLE IF NOT EXISTS "AIUsageLog" (
"id" TEXT NOT NULL, "id" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"userId" TEXT, "userId" TEXT,
@ -21,13 +21,7 @@ CREATE TABLE "AIUsageLog" (
); );
-- CreateIndex -- CreateIndex
CREATE INDEX "AIUsageLog_userId_idx" ON "AIUsageLog"("userId"); CREATE INDEX IF NOT EXISTS "AIUsageLog_userId_idx" ON "AIUsageLog"("userId");
CREATE INDEX IF NOT EXISTS "AIUsageLog_action_idx" ON "AIUsageLog"("action");
-- CreateIndex CREATE INDEX IF NOT EXISTS "AIUsageLog_createdAt_idx" ON "AIUsageLog"("createdAt");
CREATE INDEX "AIUsageLog_action_idx" ON "AIUsageLog"("action"); CREATE INDEX IF NOT EXISTS "AIUsageLog_model_idx" ON "AIUsageLog"("model");
-- CreateIndex
CREATE INDEX "AIUsageLog_createdAt_idx" ON "AIUsageLog"("createdAt");
-- CreateIndex
CREATE INDEX "AIUsageLog_model_idx" ON "AIUsageLog"("model");

View File

@ -1,5 +1,5 @@
-- CreateTable -- CreateTable
CREATE TABLE "InAppNotification" ( CREATE TABLE IF NOT EXISTS "InAppNotification" (
"id" TEXT NOT NULL, "id" TEXT NOT NULL,
"userId" TEXT NOT NULL, "userId" TEXT NOT NULL,
"type" TEXT NOT NULL, "type" TEXT NOT NULL,
@ -20,7 +20,7 @@ CREATE TABLE "InAppNotification" (
); );
-- CreateTable -- CreateTable
CREATE TABLE "NotificationEmailSetting" ( CREATE TABLE IF NOT EXISTS "NotificationEmailSetting" (
"id" TEXT NOT NULL, "id" TEXT NOT NULL,
"notificationType" TEXT NOT NULL, "notificationType" TEXT NOT NULL,
"category" TEXT NOT NULL, "category" TEXT NOT NULL,
@ -30,29 +30,24 @@ CREATE TABLE "NotificationEmailSetting" (
"emailSubject" TEXT, "emailSubject" TEXT,
"emailTemplate" TEXT, "emailTemplate" TEXT,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL, "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedById" TEXT, "updatedById" TEXT,
CONSTRAINT "NotificationEmailSetting_pkey" PRIMARY KEY ("id") CONSTRAINT "NotificationEmailSetting_pkey" PRIMARY KEY ("id")
); );
-- CreateIndex -- CreateIndex
CREATE INDEX "InAppNotification_userId_isRead_idx" ON "InAppNotification"("userId", "isRead"); CREATE INDEX IF NOT EXISTS "InAppNotification_userId_isRead_idx" ON "InAppNotification"("userId", "isRead");
CREATE INDEX IF NOT EXISTS "InAppNotification_userId_createdAt_idx" ON "InAppNotification"("userId", "createdAt");
-- CreateIndex CREATE INDEX IF NOT EXISTS "InAppNotification_groupKey_idx" ON "InAppNotification"("groupKey");
CREATE INDEX "InAppNotification_userId_createdAt_idx" ON "InAppNotification"("userId", "createdAt"); CREATE UNIQUE INDEX IF NOT EXISTS "NotificationEmailSetting_notificationType_key" ON "NotificationEmailSetting"("notificationType");
CREATE INDEX IF NOT EXISTS "NotificationEmailSetting_category_idx" ON "NotificationEmailSetting"("category");
-- CreateIndex
CREATE INDEX "InAppNotification_groupKey_idx" ON "InAppNotification"("groupKey");
-- CreateIndex
CREATE UNIQUE INDEX "NotificationEmailSetting_notificationType_key" ON "NotificationEmailSetting"("notificationType");
-- CreateIndex
CREATE INDEX "NotificationEmailSetting_category_idx" ON "NotificationEmailSetting"("category");
-- AddForeignKey -- AddForeignKey
DO $$ BEGIN
ALTER TABLE "InAppNotification" ADD CONSTRAINT "InAppNotification_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE; ALTER TABLE "InAppNotification" ADD CONSTRAINT "InAppNotification_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
-- AddForeignKey DO $$ BEGIN
ALTER TABLE "NotificationEmailSetting" ADD CONSTRAINT "NotificationEmailSetting_updatedById_fkey" FOREIGN KEY ("updatedById") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE; ALTER TABLE "NotificationEmailSetting" ADD CONSTRAINT "NotificationEmailSetting_updatedById_fkey" FOREIGN KEY ("updatedById") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;

View File

@ -1,4 +1,6 @@
-- AddRoundEntryNotification -- AddRoundEntryNotification
-- Adds the entryNotificationType column to Round table for configurable notifications -- Adds the entryNotificationType column to Round table for configurable notifications
DO $$ BEGIN
ALTER TABLE "Round" ADD COLUMN "entryNotificationType" TEXT; ALTER TABLE "Round" ADD COLUMN "entryNotificationType" TEXT;
EXCEPTION WHEN duplicate_column THEN NULL; END $$;

View File

@ -1,4 +1,6 @@
-- AddRoundSortOrder -- AddRoundSortOrder
-- Adds the sortOrder column to Round table for ordering rounds within a program -- Adds the sortOrder column to Round table for ordering rounds within a program
DO $$ BEGIN
ALTER TABLE "Round" ADD COLUMN "sortOrder" INTEGER NOT NULL DEFAULT 0; ALTER TABLE "Round" ADD COLUMN "sortOrder" INTEGER NOT NULL DEFAULT 0;
EXCEPTION WHEN duplicate_column THEN NULL; END $$;

View File

@ -0,0 +1,54 @@
-- Add ProjectTag table for AI tagging
CREATE TABLE IF NOT EXISTS "ProjectTag" (
"id" TEXT NOT NULL,
"projectId" TEXT NOT NULL,
"tagId" TEXT NOT NULL,
"confidence" DOUBLE PRECISION NOT NULL DEFAULT 1.0,
"source" TEXT NOT NULL DEFAULT 'AI',
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "ProjectTag_pkey" PRIMARY KEY ("id")
);
CREATE UNIQUE INDEX IF NOT EXISTS "ProjectTag_projectId_tagId_key" ON "ProjectTag"("projectId", "tagId");
CREATE INDEX IF NOT EXISTS "ProjectTag_projectId_idx" ON "ProjectTag"("projectId");
CREATE INDEX IF NOT EXISTS "ProjectTag_tagId_idx" ON "ProjectTag"("tagId");
DO $$ BEGIN
ALTER TABLE "ProjectTag" ADD CONSTRAINT "ProjectTag_projectId_fkey"
FOREIGN KEY ("projectId") REFERENCES "Project"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
DO $$ BEGIN
ALTER TABLE "ProjectTag" ADD CONSTRAINT "ProjectTag_tagId_fkey"
FOREIGN KEY ("tagId") REFERENCES "ExpertiseTag"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
-- Add AssignmentJob table for AI assignment progress tracking
DO $$ BEGIN
CREATE TYPE "AssignmentJobStatus" AS ENUM ('PENDING', 'RUNNING', 'COMPLETED', 'FAILED');
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
CREATE TABLE IF NOT EXISTS "AssignmentJob" (
"id" TEXT NOT NULL,
"roundId" TEXT NOT NULL,
"status" "AssignmentJobStatus" NOT NULL DEFAULT 'PENDING',
"totalProjects" INTEGER NOT NULL DEFAULT 0,
"totalBatches" INTEGER NOT NULL DEFAULT 0,
"currentBatch" INTEGER NOT NULL DEFAULT 0,
"processedCount" INTEGER NOT NULL DEFAULT 0,
"suggestionsCount" INTEGER NOT NULL DEFAULT 0,
"errorMessage" TEXT,
"startedAt" TIMESTAMP(3),
"completedAt" TIMESTAMP(3),
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"fallbackUsed" BOOLEAN NOT NULL DEFAULT false,
CONSTRAINT "AssignmentJob_pkey" PRIMARY KEY ("id")
);
CREATE INDEX IF NOT EXISTS "AssignmentJob_roundId_idx" ON "AssignmentJob"("roundId");
CREATE INDEX IF NOT EXISTS "AssignmentJob_status_idx" ON "AssignmentJob"("status");
DO $$ BEGIN
ALTER TABLE "AssignmentJob" ADD CONSTRAINT "AssignmentJob_roundId_fkey"
FOREIGN KEY ("roundId") REFERENCES "Round"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;