286 lines
14 KiB
TypeScript
286 lines
14 KiB
TypeScript
import { MigrateUpArgs, MigrateDownArgs, sql } from '@payloadcms/db-postgres';
|
|
|
|
/**
|
|
* Migration: native_localization
|
|
*
|
|
* Transforms the existing schema (manual `locale` select column on each row)
|
|
* into Payload's native localization join-table structure.
|
|
*
|
|
* Each statement is a separate db.execute call to avoid Drizzle multi-statement issues.
|
|
*/
|
|
export async function up({ db }: MigrateUpArgs): Promise<void> {
|
|
// ── 1. Global locale enum ────────────────────────────────────────────────────
|
|
await db.execute(sql`
|
|
DO $$ BEGIN
|
|
CREATE TYPE "public"."enum__locales" AS ENUM('de', 'en');
|
|
EXCEPTION WHEN duplicate_object THEN null; END $$
|
|
`);
|
|
await db.execute(sql`
|
|
DO $$ BEGIN
|
|
CREATE TYPE "public"."enum__posts_v_published_locale" AS ENUM('de', 'en');
|
|
EXCEPTION WHEN duplicate_object THEN null; END $$
|
|
`);
|
|
await db.execute(sql`
|
|
DO $$ BEGIN
|
|
CREATE TYPE "public"."enum__products_v_published_locale" AS ENUM('de', 'en');
|
|
EXCEPTION WHEN duplicate_object THEN null; END $$
|
|
`);
|
|
await db.execute(sql`
|
|
DO $$ BEGIN
|
|
CREATE TYPE "public"."enum__pages_v_published_locale" AS ENUM('de', 'en');
|
|
EXCEPTION WHEN duplicate_object THEN null; END $$
|
|
`);
|
|
await db.execute(sql`
|
|
DO $$ BEGIN
|
|
CREATE TYPE "public"."enum_pages_layout" AS ENUM('default', 'fullBleed');
|
|
EXCEPTION WHEN duplicate_object THEN null; END $$
|
|
`);
|
|
await db.execute(sql`
|
|
DO $$ BEGIN
|
|
CREATE TYPE "public"."enum__pages_v_version_layout" AS ENUM('default', 'fullBleed');
|
|
EXCEPTION WHEN duplicate_object THEN null; END $$
|
|
`);
|
|
await db.execute(sql`
|
|
DO $$ BEGIN
|
|
CREATE TYPE "public"."enum__pages_v_version_status" AS ENUM('draft', 'published');
|
|
EXCEPTION WHEN duplicate_object THEN null; END $$
|
|
`);
|
|
await db.execute(sql`
|
|
DO $$ BEGIN
|
|
CREATE TYPE "public"."enum__posts_v_version_status" AS ENUM('draft', 'published');
|
|
EXCEPTION WHEN duplicate_object THEN null; END $$
|
|
`);
|
|
await db.execute(sql`
|
|
DO $$ BEGIN
|
|
CREATE TYPE "public"."enum__products_v_version_status" AS ENUM('draft', 'published');
|
|
EXCEPTION WHEN duplicate_object THEN null; END $$
|
|
`);
|
|
|
|
// ── 2. Alter pages table ─────────────────────────────────────────────────────
|
|
await db.execute(sql`ALTER TABLE "pages" ADD COLUMN IF NOT EXISTS "layout" "enum_pages_layout" DEFAULT 'default'`);
|
|
await db.execute(sql`ALTER TABLE "pages" ADD COLUMN IF NOT EXISTS "_status" "enum_pages_status" DEFAULT 'draft'`);
|
|
|
|
// ── 3. Create pages_locales join table ───────────────────────────────────────
|
|
await db.execute(sql`
|
|
CREATE TABLE IF NOT EXISTS "pages_locales" (
|
|
"title" varchar,
|
|
"slug" varchar,
|
|
"excerpt" varchar,
|
|
"content" jsonb,
|
|
"id" serial PRIMARY KEY NOT NULL,
|
|
"_locale" "enum__locales" NOT NULL,
|
|
"_parent_id" integer NOT NULL
|
|
)
|
|
`);
|
|
await db.execute(sql`
|
|
DO $$ BEGIN
|
|
ALTER TABLE "pages_locales" ADD CONSTRAINT "pages_locales_locale_parent_id_unique" UNIQUE("_locale", "_parent_id");
|
|
EXCEPTION WHEN duplicate_object THEN null; END $$
|
|
`);
|
|
await db.execute(sql`
|
|
DO $$ BEGIN
|
|
ALTER TABLE "pages_locales" ADD CONSTRAINT "pages_locales_parent_id_fk"
|
|
FOREIGN KEY ("_parent_id") REFERENCES "pages"("id") ON DELETE cascade;
|
|
EXCEPTION WHEN duplicate_object THEN null; END $$
|
|
`);
|
|
|
|
// ── 4. Backfill pages_locales from old pages rows ────────────────────────────
|
|
await db.execute(sql`
|
|
INSERT INTO "pages_locales" ("title", "slug", "excerpt", "content", "_locale", "_parent_id")
|
|
SELECT
|
|
p.title, p.slug, p.excerpt, p.content,
|
|
CASE WHEN p.locale::text = 'de' THEN 'de'::"enum__locales" ELSE 'en'::"enum__locales" END,
|
|
p.id
|
|
FROM "pages" p
|
|
WHERE p.locale IS NOT NULL
|
|
ON CONFLICT ("_locale", "_parent_id") DO UPDATE
|
|
SET "title" = EXCLUDED."title",
|
|
"slug" = EXCLUDED."slug",
|
|
"excerpt" = EXCLUDED."excerpt",
|
|
"content" = EXCLUDED."content"
|
|
`);
|
|
|
|
// ── 5. Drop old columns from pages ───────────────────────────────────────────
|
|
await db.execute(sql`ALTER TABLE "pages" DROP COLUMN IF EXISTS "title"`);
|
|
await db.execute(sql`ALTER TABLE "pages" DROP COLUMN IF EXISTS "slug"`);
|
|
await db.execute(sql`ALTER TABLE "pages" DROP COLUMN IF EXISTS "excerpt"`);
|
|
await db.execute(sql`ALTER TABLE "pages" DROP COLUMN IF EXISTS "content"`);
|
|
await db.execute(sql`ALTER TABLE "pages" DROP COLUMN IF EXISTS "locale"`);
|
|
|
|
// ── 6. Create posts_locales join table ───────────────────────────────────────
|
|
await db.execute(sql`
|
|
CREATE TABLE IF NOT EXISTS "posts_locales" (
|
|
"title" varchar,
|
|
"slug" varchar,
|
|
"excerpt" varchar,
|
|
"category" varchar,
|
|
"content" jsonb,
|
|
"id" serial PRIMARY KEY NOT NULL,
|
|
"_locale" "enum__locales" NOT NULL,
|
|
"_parent_id" integer NOT NULL
|
|
)
|
|
`);
|
|
await db.execute(sql`
|
|
DO $$ BEGIN
|
|
ALTER TABLE "posts_locales" ADD CONSTRAINT "posts_locales_locale_parent_id_unique" UNIQUE("_locale", "_parent_id");
|
|
EXCEPTION WHEN duplicate_object THEN null; END $$
|
|
`);
|
|
await db.execute(sql`
|
|
DO $$ BEGIN
|
|
ALTER TABLE "posts_locales" ADD CONSTRAINT "posts_locales_parent_id_fk"
|
|
FOREIGN KEY ("_parent_id") REFERENCES "posts"("id") ON DELETE cascade;
|
|
EXCEPTION WHEN duplicate_object THEN null; END $$
|
|
`);
|
|
|
|
// ── 7. Backfill posts_locales ────────────────────────────────────────────────
|
|
await db.execute(sql`
|
|
INSERT INTO "posts_locales" ("title", "slug", "excerpt", "category", "content", "_locale", "_parent_id")
|
|
SELECT
|
|
p.title, p.slug, p.excerpt, p.category, p.content,
|
|
CASE WHEN p.locale::text = 'de' THEN 'de'::"enum__locales" ELSE 'en'::"enum__locales" END,
|
|
p.id
|
|
FROM "posts" p
|
|
WHERE p.locale IS NOT NULL
|
|
ON CONFLICT ("_locale", "_parent_id") DO UPDATE
|
|
SET "title" = EXCLUDED."title",
|
|
"slug" = EXCLUDED."slug",
|
|
"excerpt" = EXCLUDED."excerpt",
|
|
"category" = EXCLUDED."category",
|
|
"content" = EXCLUDED."content"
|
|
`);
|
|
|
|
// ── 8. Drop old columns from posts ───────────────────────────────────────────
|
|
await db.execute(sql`ALTER TABLE "posts" DROP COLUMN IF EXISTS "title"`);
|
|
await db.execute(sql`ALTER TABLE "posts" DROP COLUMN IF EXISTS "slug"`);
|
|
await db.execute(sql`ALTER TABLE "posts" DROP COLUMN IF EXISTS "excerpt"`);
|
|
await db.execute(sql`ALTER TABLE "posts" DROP COLUMN IF EXISTS "category"`);
|
|
await db.execute(sql`ALTER TABLE "posts" DROP COLUMN IF EXISTS "content"`);
|
|
await db.execute(sql`ALTER TABLE "posts" DROP COLUMN IF EXISTS "locale"`);
|
|
|
|
// ── 9. Create products_locales join table ────────────────────────────────────
|
|
await db.execute(sql`
|
|
CREATE TABLE IF NOT EXISTS "products_locales" (
|
|
"title" varchar,
|
|
"description" varchar,
|
|
"application" jsonb,
|
|
"content" jsonb,
|
|
"id" serial PRIMARY KEY NOT NULL,
|
|
"_locale" "enum__locales" NOT NULL,
|
|
"_parent_id" integer NOT NULL
|
|
)
|
|
`);
|
|
await db.execute(sql`
|
|
DO $$ BEGIN
|
|
ALTER TABLE "products_locales" ADD CONSTRAINT "products_locales_locale_parent_id_unique" UNIQUE("_locale", "_parent_id");
|
|
EXCEPTION WHEN duplicate_object THEN null; END $$
|
|
`);
|
|
await db.execute(sql`
|
|
DO $$ BEGIN
|
|
ALTER TABLE "products_locales" ADD CONSTRAINT "products_locales_parent_id_fk"
|
|
FOREIGN KEY ("_parent_id") REFERENCES "products"("id") ON DELETE cascade;
|
|
EXCEPTION WHEN duplicate_object THEN null; END $$
|
|
`);
|
|
|
|
// ── 10. Backfill products_locales ────────────────────────────────────────────
|
|
// Products were separate DE/EN rows — each becomes a locale entry on its own id
|
|
await db.execute(sql`
|
|
INSERT INTO "products_locales" ("title", "description", "application", "content", "_locale", "_parent_id")
|
|
SELECT
|
|
p.title, p.description, p.application, p.content,
|
|
CASE WHEN p.locale::text = 'de' THEN 'de'::"enum__locales" ELSE 'en'::"enum__locales" END,
|
|
p.id
|
|
FROM "products" p
|
|
WHERE p.locale IS NOT NULL
|
|
ON CONFLICT ("_locale", "_parent_id") DO NOTHING
|
|
`);
|
|
|
|
// ── 11. Drop old columns from products ───────────────────────────────────────
|
|
await db.execute(sql`ALTER TABLE "products" DROP COLUMN IF EXISTS "title"`);
|
|
await db.execute(sql`ALTER TABLE "products" DROP COLUMN IF EXISTS "description"`);
|
|
await db.execute(sql`ALTER TABLE "products" DROP COLUMN IF EXISTS "application"`);
|
|
await db.execute(sql`ALTER TABLE "products" DROP COLUMN IF EXISTS "content"`);
|
|
await db.execute(sql`ALTER TABLE "products" DROP COLUMN IF EXISTS "locale"`);
|
|
|
|
// ── 12. Version tables (_posts_v, _products_v, _pages_v) locale columns ──────
|
|
await db.execute(sql`ALTER TABLE "_posts_v" ADD COLUMN IF NOT EXISTS "published_locale" "enum__posts_v_published_locale"`);
|
|
await db.execute(sql`ALTER TABLE "_products_v" ADD COLUMN IF NOT EXISTS "published_locale" "enum__products_v_published_locale"`);
|
|
await db.execute(sql`ALTER TABLE "_pages_v" ADD COLUMN IF NOT EXISTS "published_locale" "enum__pages_v_published_locale"`);
|
|
|
|
// ── 13. Create _posts_v_locales ──────────────────────────────────────────────
|
|
await db.execute(sql`
|
|
CREATE TABLE IF NOT EXISTS "_posts_v_locales" (
|
|
"version_title" varchar, "version_slug" varchar, "version_excerpt" varchar,
|
|
"version_category" varchar, "version_content" jsonb,
|
|
"id" serial PRIMARY KEY NOT NULL,
|
|
"_locale" "enum__locales" NOT NULL, "_parent_id" integer NOT NULL
|
|
)
|
|
`);
|
|
await db.execute(sql`
|
|
DO $$ BEGIN
|
|
ALTER TABLE "_posts_v_locales" ADD CONSTRAINT "_posts_v_locales_locale_parent_id_unique" UNIQUE("_locale", "_parent_id");
|
|
EXCEPTION WHEN duplicate_object THEN null; END $$
|
|
`);
|
|
await db.execute(sql`
|
|
DO $$ BEGIN
|
|
ALTER TABLE "_posts_v_locales" ADD CONSTRAINT "_posts_v_locales_parent_id_fk"
|
|
FOREIGN KEY ("_parent_id") REFERENCES "_posts_v"("id") ON DELETE cascade;
|
|
EXCEPTION WHEN duplicate_object THEN null; END $$
|
|
`);
|
|
|
|
// ── 14. Create _products_v_locales ───────────────────────────────────────────
|
|
await db.execute(sql`
|
|
CREATE TABLE IF NOT EXISTS "_products_v_locales" (
|
|
"version_title" varchar, "version_description" varchar,
|
|
"version_application" jsonb, "version_content" jsonb,
|
|
"id" serial PRIMARY KEY NOT NULL,
|
|
"_locale" "enum__locales" NOT NULL, "_parent_id" integer NOT NULL
|
|
)
|
|
`);
|
|
await db.execute(sql`
|
|
DO $$ BEGIN
|
|
ALTER TABLE "_products_v_locales" ADD CONSTRAINT "_products_v_locales_locale_parent_id_unique" UNIQUE("_locale", "_parent_id");
|
|
EXCEPTION WHEN duplicate_object THEN null; END $$
|
|
`);
|
|
await db.execute(sql`
|
|
DO $$ BEGIN
|
|
ALTER TABLE "_products_v_locales" ADD CONSTRAINT "_products_v_locales_parent_id_fk"
|
|
FOREIGN KEY ("_parent_id") REFERENCES "_products_v"("id") ON DELETE cascade;
|
|
EXCEPTION WHEN duplicate_object THEN null; END $$
|
|
`);
|
|
|
|
// ── 15. Create _pages_v_locales ──────────────────────────────────────────────
|
|
await db.execute(sql`
|
|
CREATE TABLE IF NOT EXISTS "_pages_v_locales" (
|
|
"version_title" varchar, "version_slug" varchar,
|
|
"version_excerpt" varchar, "version_content" jsonb,
|
|
"id" serial PRIMARY KEY NOT NULL,
|
|
"_locale" "enum__locales" NOT NULL, "_parent_id" integer NOT NULL
|
|
)
|
|
`);
|
|
await db.execute(sql`
|
|
DO $$ BEGIN
|
|
ALTER TABLE "_pages_v_locales" ADD CONSTRAINT "_pages_v_locales_locale_parent_id_unique" UNIQUE("_locale", "_parent_id");
|
|
EXCEPTION WHEN duplicate_object THEN null; END $$
|
|
`);
|
|
await db.execute(sql`
|
|
DO $$ BEGIN
|
|
ALTER TABLE "_pages_v_locales" ADD CONSTRAINT "_pages_v_locales_parent_id_fk"
|
|
FOREIGN KEY ("_parent_id") REFERENCES "_pages_v"("id") ON DELETE cascade;
|
|
EXCEPTION WHEN duplicate_object THEN null; END $$
|
|
`);
|
|
|
|
// ── 16. Drop the now-redundant old locale enum ───────────────────────────────
|
|
await db.execute(sql`DROP TYPE IF EXISTS "public"."enum_pages_locale"`);
|
|
await db.execute(sql`DROP TYPE IF EXISTS "public"."enum_posts_locale"`);
|
|
await db.execute(sql`DROP TYPE IF EXISTS "public"."enum_products_locale"`);
|
|
}
|
|
|
|
export async function down({ db }: MigrateDownArgs): Promise<void> {
|
|
await db.execute(sql`DROP TABLE IF EXISTS "pages_locales" CASCADE`);
|
|
await db.execute(sql`DROP TABLE IF EXISTS "_pages_v_locales" CASCADE`);
|
|
await db.execute(sql`DROP TABLE IF EXISTS "posts_locales" CASCADE`);
|
|
await db.execute(sql`DROP TABLE IF EXISTS "_posts_v_locales" CASCADE`);
|
|
await db.execute(sql`DROP TABLE IF EXISTS "products_locales" CASCADE`);
|
|
await db.execute(sql`DROP TABLE IF EXISTS "_products_v_locales" CASCADE`);
|
|
}
|