From bd25fb1efa5eb67395278bf363bd993dcb6f0e33 Mon Sep 17 00:00:00 2001 From: tiennm99 Date: Sun, 14 Jun 2026 14:10:46 +0700 Subject: [PATCH] chore: add sql migration runner script Node runner using the project's postgres dependency, for applying SQL migrations where psql is unavailable. Reads POSTGRES_URL via --env-file; runs the file in a single transaction. --- scripts/run-migration.mjs | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 scripts/run-migration.mjs diff --git a/scripts/run-migration.mjs b/scripts/run-migration.mjs new file mode 100644 index 0000000..a9afa49 --- /dev/null +++ b/scripts/run-migration.mjs @@ -0,0 +1,36 @@ +// Minimal SQL migration runner. Reads POSTGRES_URL from the environment and +// executes the SQL file given as the first CLI arg. Used because the deploy +// box has no psql; reuses the project's `postgres` dependency. +// +// Usage: +// node --env-file=.env.local scripts/run-migration.mjs +// +// The connecting role must own/bypass RLS on the llmapikey schema. Statements +// run in a single transaction so a partial failure rolls back cleanly. +import { readFileSync } from "node:fs"; +import postgres from "postgres"; + +const file = process.argv[2]; +if (!file) { + console.error("Usage: node --env-file=.env.local scripts/run-migration.mjs "); + process.exit(1); +} +const url = process.env.POSTGRES_URL; +if (!url) { + console.error("Missing POSTGRES_URL (put it in .env.local)."); + process.exit(1); +} + +const sqlText = readFileSync(file, "utf8"); +// prepare:false — Supabase transaction pooler (port 6543) disallows prepared statements. +const sql = postgres(url, { prepare: false }); + +try { + await sql.begin((tx) => [tx.unsafe(sqlText)]); + console.log(`Applied: ${file}`); +} catch (err) { + console.error(`Migration failed: ${err.message}`); + process.exitCode = 1; +} finally { + await sql.end(); +}