Spaces:
Running
Running
deploy(hf): sync szl-holdings/nexus@00edebc993282efd5b43b0144b3d3d27ab43e264 derived COPY set
31b3c47 verified Download scripts/migrate.mjs from SZLHOLDINGS/nexus: direct link, hf CLI and curl.
- Browser
- Download file 3.18 kB
-
https://huggingface.co/spaces/SZLHOLDINGS/nexus/resolve/main/scripts/migrate.mjs
- Command line
-
hf download hf://spaces/SZLHOLDINGS/nexus/scripts/migrate.mjs
-
curl -L -o migrate.mjs https://huggingface.co/spaces/SZLHOLDINGS/nexus/resolve/main/scripts/migrate.mjs
3.18 kB
| /** | |
| * Deploy-time database migrator (node-postgres, `pg`). | |
| * | |
| * Runs during `npm run build` β on every Vercel deploy β applying pending files | |
| * in ../migrations to DATABASE_URL. Each file is applied in one transaction and | |
| * recorded in a `_migrations` table, so it runs once and is safe to re-run. | |
| * | |
| * The read is non-recursive, so the opt-in auth schema under migrations/auth/ | |
| * is not applied to an app that never asked for sign-in. | |
| * | |
| * No DATABASE_URL (local / preview builds) -> skip; the PGLite fallback applies | |
| * the same files at startup instead (see src/lib/db.ts). | |
| */ | |
| import { readdir, readFile } from "node:fs/promises"; | |
| import { fileURLToPath } from "node:url"; | |
| import { dirname, join } from "node:path"; | |
| import pg from "pg"; | |
| import { pendingMigrations } from "./migration-plan.mjs"; | |
| const databaseUrl = process.env.DATABASE_URL; | |
| if (!databaseUrl) { | |
| console.log( | |
| "[migrate] DATABASE_URL not set β skipping (the PGLite fallback migrates itself).", | |
| ); | |
| process.exit(0); | |
| } | |
| const migrationsDir = join(dirname(fileURLToPath(import.meta.url)), "..", "migrations"); | |
| async function main() { | |
| let entries; | |
| try { | |
| entries = await readdir(migrationsDir); | |
| } catch { | |
| console.log("[migrate] no migrations/ directory β nothing to do."); | |
| return; | |
| } | |
| // An app with no schema of its own must not pay for a database connection. | |
| if (pendingMigrations(entries, []).length === 0) { | |
| console.log("[migrate] no migrations β nothing to do."); | |
| return; | |
| } | |
| const pool = new pg.Pool({ connectionString: databaseUrl, max: 1 }); | |
| const client = await pool.connect(); | |
| try { | |
| await client.query( | |
| "CREATE TABLE IF NOT EXISTS _migrations (name TEXT PRIMARY KEY, applied_at TIMESTAMPTZ NOT NULL DEFAULT now())", | |
| ); | |
| const applied = (await client.query("SELECT name FROM _migrations")).rows.map( | |
| (r) => r.name, | |
| ); | |
| let count = 0; | |
| for (const { name } of pendingMigrations(entries, applied)) { | |
| const text = await readFile(join(migrationsDir, name), "utf8"); | |
| try { | |
| await client.query("BEGIN"); | |
| // pg's simple-query protocol runs a whole multi-statement file at once. | |
| await client.query(text); | |
| await client.query("INSERT INTO _migrations (name) VALUES ($1)", [name]); | |
| await client.query("COMMIT"); | |
| } catch (err) { | |
| console.error(`[migrate] error applying ${name}`); | |
| try { | |
| await client.query("ROLLBACK"); | |
| } catch { | |
| // ROLLBACK fails when the connection died β keep the original error. | |
| } | |
| throw err; | |
| } | |
| console.log(`[migrate] applied ${name}`); | |
| count += 1; | |
| } | |
| console.log(count ? `[migrate] done β ${count} migration(s) applied.` : "[migrate] up to date."); | |
| } finally { | |
| client.release(); | |
| await pool.end(); | |
| } | |
| } | |
| main().catch((err) => { | |
| console.error("[migrate] failed:", err?.message || err); | |
| // pg errors carry the context needed to debug a bad SQL file. | |
| for (const key of ["code", "detail", "hint", "position", "where"]) { | |
| if (err?.[key] != null) console.error(`[migrate] ${key}: ${err[key]}`); | |
| } | |
| process.exit(1); | |
| }); | |