Local dev and the test suite previously defaulted to the live production Neon database, and running the tests mutated prod data. Add guardrails plus a proper local Postgres + migrations workflow so production can't be hit by mistake: - docker-compose: local Postgres 17 (matches prod) + auto-created teqo_test - dev guard refuses a non-local DATABASE_URL; tests refuse non-_test databases - .env.test isolates the suite; vitest/playwright load it and fail closed - Payload migrations set up (push disabled); build runs `payload migrate`, so deploys apply pending migrations to prod automatically - baseline `initial` migration + idempotent consent.text varchar->jsonb fix - `pnpm db:pull` copies prod content locally, excluding supporter PII - docs (README, AGENTS.md) + agent skills for the local-db/migration workflow
43 lines
1.4 KiB
TypeScript
43 lines
1.4 KiB
TypeScript
import { MigrateDownArgs, MigrateUpArgs, sql } from '@payloadcms/db-postgres'
|
|
|
|
/**
|
|
* Reconciles production schema drift: `consent.text` is a `richText` field in the
|
|
* Payload config (which maps to `jsonb`), but the production database still has it
|
|
* as `varchar`. Its single row already contains valid Lexical JSON, so the cast is
|
|
* lossless. This migration is idempotent — it only runs the conversion when the
|
|
* column is still `varchar`, so it is a no-op on any database already at `jsonb`
|
|
* (e.g. a fresh local DB built from the initial migration).
|
|
*/
|
|
export async function up({ db }: MigrateUpArgs): Promise<void> {
|
|
await db.execute(sql`
|
|
DO $$
|
|
BEGIN
|
|
IF EXISTS (
|
|
SELECT 1 FROM information_schema.columns
|
|
WHERE table_schema = 'public'
|
|
AND table_name = 'consent'
|
|
AND column_name = 'text'
|
|
AND data_type = 'character varying'
|
|
) THEN
|
|
ALTER TABLE "consent" ALTER COLUMN "text" TYPE jsonb USING "text"::jsonb;
|
|
END IF;
|
|
END $$;
|
|
`)
|
|
}
|
|
|
|
export async function down({ db }: MigrateDownArgs): Promise<void> {
|
|
await db.execute(sql`
|
|
DO $$
|
|
BEGIN
|
|
IF EXISTS (
|
|
SELECT 1 FROM information_schema.columns
|
|
WHERE table_schema = 'public'
|
|
AND table_name = 'consent'
|
|
AND column_name = 'text'
|
|
AND data_type = 'jsonb'
|
|
) THEN
|
|
ALTER TABLE "consent" ALTER COLUMN "text" TYPE varchar USING "text"::text;
|
|
END IF;
|
|
END $$;
|
|
`)
|
|
}
|