// One-off production migration runner (DigitalOcean Managed Postgres). // // Applies any pending Drizzle migrations in lib/db/migrations to the production // database, using the doadmin connection. Drizzle tracks what's already applied // in the __drizzle_migrations table, so this only runs the missing migrations // and is safe to re-run. // // The connection string is read from PROD_DATABASE_URL so the credential never // has to be typed on the command line. Set it in .env.local (git-ignored): // // PROD_DATABASE_URL=postgresql://doadmin:@...:25060/ // // Then run: node scripts/migrate-prod.mjs // // TLS: verified against ca-certificate.crt when present, otherwise encrypted // but unverified. Uses the direct port (25060), NOT the 25061 pooler — the // migrator needs a real session (transactions + advisory locks). import { readFileSync } from "node:fs" import { config } from "dotenv" import { drizzle } from "drizzle-orm/node-postgres" import { migrate } from "drizzle-orm/node-postgres/migrator" import pg from "pg" config({ path: ".env.local", quiet: true }) const url = process.env.PROD_DATABASE_URL if (!url) { console.error( "[migrate-prod] PROD_DATABASE_URL is not set. Add the doadmin connection " + "string (port 25060, app database) to .env.local and re-run." ) process.exit(1) } if (url.includes(":25061")) { console.error( "[migrate-prod] Refusing to run migrations through the 25061 connection " + "pooler. Use the direct port 25060." ) process.exit(1) } let ssl try { const ca = readFileSync("ca-certificate.crt", "utf8") ssl = { rejectUnauthorized: true, ca } console.log("[migrate-prod] TLS: verifying against ca-certificate.crt") } catch { ssl = { rejectUnauthorized: false } console.log("[migrate-prod] TLS: encrypted (certificate not verified)") } const { Pool } = pg const pool = new Pool({ connectionString: url, ssl }) const db = drizzle(pool) try { const host = new URL(url).host console.log(`[migrate-prod] Connecting to ${host} ...`) await migrate(db, { migrationsFolder: "./lib/db/migrations" }) console.log("[migrate-prod] Migrations applied successfully.") await pool.end() process.exit(0) } catch (err) { console.error(`[migrate-prod] Failed: ${err?.message ?? err}`) await pool.end().catch(() => {}) process.exit(1) }