/* eslint-disable no-restricted-syntax, no-await-in-loop */ import type { Payload } from 'payload' import type { Migration } from 'payload/database' import { generateDrizzleJson } from 'drizzle-kit/utils' import { readMigrationFiles } from 'payload/database' import { DatabaseError } from 'pg' import prompts from 'prompts' import type { PostgresAdapter } from './types' import { createMigrationTable } from './utilities/createMigrationTable' import { migrationTableExists } from './utilities/migrationTableExists' export async function migrate(this: PostgresAdapter): Promise { const { payload } = this const migrationFiles = await readMigrationFiles({ payload }) let latestBatch = 0 let migrationsInDB = [] const hasMigrationTable = await migrationTableExists(this.drizzle) if (hasMigrationTable) { ;({ docs: migrationsInDB } = await payload.find({ collection: 'payload-migrations', limit: 0, sort: '-name', })) if (Number(migrationsInDB?.[0]?.batch) > 0) { latestBatch = Number(migrationsInDB[0]?.batch) } } else { await createMigrationTable(this.drizzle) } if (migrationsInDB.find((m) => m.batch === -1)) { const { confirm: runMigrations } = await prompts( { name: 'confirm', initial: false, message: "It looks like you've run Payload in dev mode, meaning you've dynamically pushed changes to your database. " + "If you'd like to run migrations, data loss will occur. Would you like to proceed?", type: 'confirm', }, { onCancel: () => { process.exit(0) }, }, ) if (!runMigrations) { process.exit(0) } } const newBatch = latestBatch + 1 // Execute 'up' function for each migration sequentially for (const migration of migrationFiles) { const alreadyRan = migrationsInDB.find((existing) => existing.name === migration.name) // If already ran, skip if (alreadyRan) { continue // eslint-disable-line no-continue } await runMigrationFile(payload, migration, newBatch) } } async function runMigrationFile(payload: Payload, migration: Migration, batch: number) { const start = Date.now() payload.logger.info({ msg: `Migrating: ${migration.name}` }) const pgAdapter = payload.db const drizzleJSON = generateDrizzleJson(pgAdapter.schema) try { await migration.up({ payload }) payload.logger.info({ msg: `Migrated: ${migration.name} (${Date.now() - start}ms)` }) await payload.create({ collection: 'payload-migrations', data: { name: migration.name, batch, schema: drizzleJSON, }, }) } catch (err: unknown) { let msg = `Error running migration ${migration.name}` if (err instanceof DatabaseError) { msg += `: ${err.message}` if (err.hint) msg += `. ${err.hint}` } payload.logger.error({ err, msg }) throw err } }