This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
-- 001_legacy_additive.sql — additive upgrades historically shipped as
|
||||
-- db/upgrade.sql, adopted into the ordered ledger (review MIG-01).
|
||||
-- Idempotent: every statement is guarded by an existence check, so first
|
||||
-- ledger adoption on an already-upgraded database is a no-op. From ledger
|
||||
-- adoption onward recorded migrations are immutable; new changes go into
|
||||
-- new numbered files. The runner (db/migrate.sh) applies each file in its
|
||||
-- own transaction with ON_ERROR_STOP.
|
||||
|
||||
-- Review MIG-02: report orphaned bookmark->contact references with an
|
||||
-- actionable error before adding the foreign key, instead of failing with a
|
||||
-- bare constraint violation. Never erase relationships silently — repair is
|
||||
-- a deliberate operator decision.
|
||||
DO $$
|
||||
DECLARE orphans bigint;
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname='Bookmark_contactId_fkey' AND conrelid='"Bookmark"'::regclass) THEN
|
||||
SELECT count(*) INTO orphans FROM "Bookmark" b
|
||||
WHERE b."contactId" IS NOT NULL
|
||||
AND NOT EXISTS (SELECT 1 FROM "Contact" c WHERE c.id=b."contactId");
|
||||
IF orphans > 0 THEN
|
||||
RAISE EXCEPTION 'migration 001: % bookmark(s) reference missing Contact rows; repair them deliberately (e.g. UPDATE "Bookmark" SET "contactId"=NULL WHERE "contactId" IS NOT NULL AND NOT EXISTS (SELECT 1 FROM "Contact" c WHERE c.id="Bookmark"."contactId")) and re-run', orphans;
|
||||
END IF;
|
||||
ALTER TABLE "Bookmark" ADD CONSTRAINT "Bookmark_contactId_fkey" FOREIGN KEY ("contactId") REFERENCES "Contact"(id) ON DELETE SET NULL;
|
||||
END IF;
|
||||
END $$;
|
||||
-- Bring pre-port mailbox switches into the background signal scheduler.
|
||||
-- Existing sources (including paused watches) and modern watchers are preserved.
|
||||
INSERT INTO "SignalSource"(id,type,config,enabled,"bookmarkId","ownerId")
|
||||
SELECT 'legacy-'||b.id,'email',json_build_object('subject',COALESCE(b."imapQuery",''),'legacy',true),true,b.id,b."ownerId"
|
||||
FROM "Bookmark" b
|
||||
WHERE b."imapWatchEnabled" AND NOT EXISTS (
|
||||
SELECT 1 FROM "SignalSource" s WHERE s."bookmarkId"=b.id AND s."ownerId"=b."ownerId" AND s.config->>'legacy'='true'
|
||||
)
|
||||
ON CONFLICT(id) DO NOTHING;
|
||||
CREATE INDEX IF NOT EXISTS "Bookmark_ownerId_updatedAt_id_idx" ON "Bookmark"("ownerId","updatedAt" DESC,id DESC);
|
||||
-- Revocable sessions: cookie JWTs embed this version; signout bumps it.
|
||||
ALTER TABLE "User" ADD COLUMN IF NOT EXISTS "tokenVersion" integer NOT NULL DEFAULT 1;
|
||||
-- Query-pattern indexes: shared-date visibility join and the alerts poll.
|
||||
CREATE INDEX IF NOT EXISTS "SharedDate_creatorId_recipientId_date_idx" ON "SharedDate"("creatorId","recipientId",date);
|
||||
CREATE INDEX IF NOT EXISTS "Bookmark_ownerId_alertAt_unsent_idx" ON "Bookmark"("ownerId","alertAt") WHERE NOT "alertSent";
|
||||
-- Prisma's Json maps to jsonb; enables GIN indexing and dedups keys.
|
||||
-- Re-running on an already-jsonb column is a validated no-op.
|
||||
ALTER TABLE "SignalSource" ALTER COLUMN config TYPE jsonb USING config::jsonb;
|
||||
-- Optimistic concurrency for page saves (review DATA-01): content saves bump
|
||||
-- this revision and a stale base version in PATCH is rejected with 409.
|
||||
ALTER TABLE "Page" ADD COLUMN IF NOT EXISTS version integer NOT NULL DEFAULT 1;
|
||||
-- Durable per-source mailbox ingestion checkpoints (review SYNC-02): polls
|
||||
-- process only UIDs newer than lastUid and advance only after durable inserts.
|
||||
CREATE TABLE IF NOT EXISTS "SignalCheckpoint" (
|
||||
"sourceId" text PRIMARY KEY REFERENCES "SignalSource"(id) ON DELETE CASCADE,
|
||||
"uidValidity" bigint NOT NULL,
|
||||
"lastUid" bigint NOT NULL,
|
||||
"updatedAt" timestamptz NOT NULL DEFAULT now()
|
||||
);
|
||||
-- One-shot administrator bootstrap state (review AUTH-06): once recorded,
|
||||
-- signup never auto-promotes again; recovery is operator-driven.
|
||||
CREATE TABLE IF NOT EXISTS "AppSetting" (
|
||||
key text PRIMARY KEY,
|
||||
value jsonb NOT NULL DEFAULT '{}',
|
||||
"updatedAt" timestamptz NOT NULL DEFAULT now()
|
||||
);
|
||||
-- Installations that already have a qualifying administrator completed
|
||||
-- bootstrap before this ledger existed; record it so demoting that
|
||||
-- administrator cannot re-open first-signup promotion.
|
||||
INSERT INTO "AppSetting"(key,value)
|
||||
SELECT 'adminBootstrap',jsonb_build_object('completed',true)
|
||||
WHERE EXISTS (SELECT 1 FROM "User" WHERE role='ADMIN' AND email NOT LIKE '%@example.com')
|
||||
ON CONFLICT(key) DO NOTHING;
|
||||
Reference in New Issue
Block a user