69 lines
2.3 KiB
JavaScript
69 lines
2.3 KiB
JavaScript
// 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:<pw>@<cluster>...:25060/<app-db>
|
||
|
|
//
|
||
|
|
// 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)
|
||
|
|
}
|