From f1628a052696950a75dc157cd2fb0aff7a0896f4 Mon Sep 17 00:00:00 2001 From: Kaito Date: Wed, 13 May 2026 06:41:04 +0700 Subject: [PATCH] fix(prisma): support legacy token migration replay --- docs/production-runbook.md | 11 +++++++ .../migration.sql | 20 +++++++++--- .../migration.sql | 32 +++++++++++++------ 3 files changed, 49 insertions(+), 14 deletions(-) diff --git a/docs/production-runbook.md b/docs/production-runbook.md index e8f1e92..28a9719 100644 --- a/docs/production-runbook.md +++ b/docs/production-runbook.md @@ -92,6 +92,17 @@ Because the current image entrypoint always runs `prisma migrate deploy`, every For multi-replica production deployments, avoid migration races by ensuring only one instance starts with the migration-capable entrypoint at a time, or by using platform controls/entrypoint overrides to run a dedicated migration job before scaling the web app. If your platform cannot separate migrations from app startup, document that limitation in the release notes and roll out one replica at a time. +### Legacy pre-token database upgrades + +The migration chain has a compatibility path for older non-empty AgentBridge databases that existed before company-scoped bearer tokens and stable `AgentId` values were introduced. Historical token migrations backfill deterministic placeholder values so `prisma migrate deploy` can complete instead of failing on required non-null columns. + +After upgrading a legacy pre-token database: + +1. Sign in to the dashboard. +2. Review agents and rename any generated `legacy-` AgentIds to the intended stable API identifiers before configuring external agents. +3. Rotate the company bearer token in Settings and update external agent configs with the newly shown token. The migration placeholders are not usable plaintext bearer tokens. +4. Run the Agent API smoke check with the intended `AgentId` and new company token. + ## App start For Docker Compose with an external database: diff --git a/prisma/migrations/20260508210351_add_bearer_token/migration.sql b/prisma/migrations/20260508210351_add_bearer_token/migration.sql index c0103bc..8edae6b 100644 --- a/prisma/migrations/20260508210351_add_bearer_token/migration.sql +++ b/prisma/migrations/20260508210351_add_bearer_token/migration.sql @@ -1,12 +1,22 @@ /* - Warnings: - - - A unique constraint covering the columns `[bearerTokenHash]` on the table `Agent` will be added. If there are existing duplicate values, this will fail. - - Added the required column `bearerTokenHash` to the `Agent` table without a default value. This is not possible if the table is not empty. + Historical compatibility note: + This migration originally added Agent.bearerTokenHash as NOT NULL without a + default, which fails when replayed against pre-token AgentBridge databases + that already contain Agent rows. The column is removed by + 20260510055256_refactor_token, so the placeholder below only makes legacy + migration replay possible and is not used by the current application model. */ -- AlterTable -ALTER TABLE "Agent" ADD COLUMN "bearerTokenHash" TEXT NOT NULL; +ALTER TABLE "Agent" ADD COLUMN "bearerTokenHash" TEXT; + +-- Backfill deterministic unique placeholders for legacy non-empty databases. +UPDATE "Agent" +SET "bearerTokenHash" = 'legacy-agent-token-' || "id"::text +WHERE "bearerTokenHash" IS NULL; + +-- Enforce the intended required constraint after backfill. +ALTER TABLE "Agent" ALTER COLUMN "bearerTokenHash" SET NOT NULL; -- CreateIndex CREATE UNIQUE INDEX "Agent_bearerTokenHash_key" ON "Agent"("bearerTokenHash"); diff --git a/prisma/migrations/20260510055256_refactor_token/migration.sql b/prisma/migrations/20260510055256_refactor_token/migration.sql index dbe1879..f2a2030 100644 --- a/prisma/migrations/20260510055256_refactor_token/migration.sql +++ b/prisma/migrations/20260510055256_refactor_token/migration.sql @@ -1,22 +1,36 @@ /* - Warnings: - - - You are about to drop the column `bearerTokenHash` on the `Agent` table. All the data in the column will be lost. - - A unique constraint covering the columns `[AgentId]` on the table `Agent` will be added. If there are existing duplicate values, this will fail. - - A unique constraint covering the columns `[bearerTokenHash]` on the table `Company` will be added. If there are existing duplicate values, this will fail. - - Added the required column `AgentId` to the `Agent` table without a default value. This is not possible if the table is not empty. - - Added the required column `bearerTokenHash` to the `Company` table without a default value. This is not possible if the table is not empty. + Historical compatibility note: + This migration originally added required Agent.AgentId and + Company.bearerTokenHash columns without defaults, which fails when replayed + against non-empty pre-refactor databases. The deterministic backfill below + preserves migration replay safety without exposing real bearer tokens. + Operators should rotate the generated company token after upgrading any + legacy database that crossed this migration boundary. */ -- DropIndex DROP INDEX "Agent_bearerTokenHash_key"; -- AlterTable ALTER TABLE "Agent" DROP COLUMN "bearerTokenHash", -ADD COLUMN "AgentId" TEXT NOT NULL; +ADD COLUMN "AgentId" TEXT; + +-- Backfill stable AgentId values for legacy rows before enforcing NOT NULL. +UPDATE "Agent" +SET "AgentId" = 'legacy-' || "id"::text +WHERE "AgentId" IS NULL; + +ALTER TABLE "Agent" ALTER COLUMN "AgentId" SET NOT NULL; -- AlterTable -ALTER TABLE "Company" ADD COLUMN "bearerTokenHash" TEXT NOT NULL; +ALTER TABLE "Company" ADD COLUMN "bearerTokenHash" TEXT; + +-- Backfill deterministic unique placeholders for legacy company rows. +UPDATE "Company" +SET "bearerTokenHash" = 'legacy-company-token-' || "id"::text +WHERE "bearerTokenHash" IS NULL; + +ALTER TABLE "Company" ALTER COLUMN "bearerTokenHash" SET NOT NULL; -- CreateIndex CREATE UNIQUE INDEX "Agent_AgentId_key" ON "Agent"("AgentId");