#!/usr/bin/env node /** * import-dev-seed.mjs * * Imports packages/db/prisma/dev-seed.sql into the local dev database. * Wipes the public schema, re-applies the current Prisma schema, loads the * seed data, then sets every user's password to "Dev123456!" via argon2id. * * Usage: * node scripts/import-dev-seed.mjs * * Requirements: * - The capakraken-postgres-1 Docker container must be running * - DATABASE_URL must point to a local capakraken database * - dev-seed.sql must exist (run export-dev-seed.mjs first) */ import { execSync, spawnSync } from "node:child_process"; import { existsSync, readFileSync } from "node:fs"; import { resolve } from "node:path"; import { loadWorkspaceEnv, resolveRealWorkspaceRoot } from "./load-env.mjs"; loadWorkspaceEnv(); const workspaceRoot = resolveRealWorkspaceRoot(); // ── Safety check ───────────────────────────────────────────────────────────── const rawUrl = process.env["DATABASE_URL"]; if (!rawUrl) { console.error("❌ DATABASE_URL is not set."); process.exit(1); } let parsedUrl; try { parsedUrl = new URL(rawUrl); } catch { console.error("❌ DATABASE_URL is not a valid URL."); process.exit(1); } const host = parsedUrl.hostname; if (!["localhost", "127.0.0.1", "::1"].includes(host)) { console.error(`❌ Refusing to import into non-local host: ${host}`); console.error(" import-dev-seed is only for local development databases."); process.exit(1); } const DB_USER = decodeURIComponent(parsedUrl.username) || "capakraken"; const DB_NAME = parsedUrl.pathname.replace(/^\/+/, "") || "capakraken"; const DB_PORT = parsedUrl.port || "5432"; // ── Docker container check ──────────────────────────────────────────────────── const CONTAINER = "capakraken-postgres-1"; const containerCheck = spawnSync("docker", ["inspect", "--format={{.State.Running}}", CONTAINER], { encoding: "utf8", }); if (containerCheck.stdout.trim() !== "true") { console.error(`❌ Container ${CONTAINER} is not running.`); console.error(" Start it with: docker compose up -d postgres"); process.exit(1); } // ── Check seed file exists ──────────────────────────────────────────────────── const seedPath = resolve(workspaceRoot, "packages/db/prisma/dev-seed.sql"); if (!existsSync(seedPath)) { console.error("❌ packages/db/prisma/dev-seed.sql not found."); console.error(" Generate it first with: node scripts/export-dev-seed.mjs"); process.exit(1); } console.log(`🗑 Wiping public schema in ${DB_USER}@${host}:${DB_PORT}/${DB_NAME} …`); // ── Drop and recreate the public schema ────────────────────────────────────── function psql(sql) { const result = spawnSync( "docker", ["exec", "-i", CONTAINER, "psql", "-U", DB_USER, "-d", DB_NAME, "-c", sql], { encoding: "utf8" }, ); if (result.status !== 0) { console.error("❌ psql command failed:"); console.error(result.stderr); process.exit(1); } return result.stdout; } psql("DROP SCHEMA public CASCADE; CREATE SCHEMA public;"); // ── Push current Prisma schema ──────────────────────────────────────────────── console.log("🔧 Applying current Prisma schema (db push) …"); try { execSync("pnpm db:push", { cwd: workspaceRoot, stdio: "inherit", env: { ...process.env }, }); } catch { console.error("❌ pnpm db:push failed. See output above."); process.exit(1); } // ── Import the seed SQL ─────────────────────────────────────────────────────── console.log("📥 Importing dev-seed.sql …"); const importResult = spawnSync( "docker", ["exec", "-i", CONTAINER, "psql", "-U", DB_USER, "-d", DB_NAME], { encoding: "utf8", input: readFileSync(seedPath, "utf8"), maxBuffer: 256 * 1024 * 1024, }, ); if (importResult.status !== 0) { console.error("❌ psql import failed:"); console.error(importResult.stderr); process.exit(1); } // ── Hash dev password and update all users ──────────────────────────────────── console.log("🔐 Setting dev passwords (Dev123456!) …"); const { hash } = await import("@node-rs/argon2"); const devHash = await hash("Dev123456!", { memoryCost: 19456, timeCost: 2, outputLen: 32, parallelism: 1, }); const updateResult = spawnSync( "docker", [ "exec", "-i", CONTAINER, "psql", "-U", DB_USER, "-d", DB_NAME, "-c", `UPDATE users SET "passwordHash" = '${devHash}';`, ], { encoding: "utf8" }, ); if (updateResult.status !== 0) { console.error("❌ Password update failed:"); console.error(updateResult.stderr); process.exit(1); } // ── Summary ─────────────────────────────────────────────────────────────────── const userCount = psql(`SELECT COUNT(*) FROM users;`) .trim() .split("\n") .find((line) => /^\s*\d+\s*$/.test(line)) ?.trim() ?? "?"; console.log(); console.log("✅ Dev seed imported successfully."); console.log(` Users: ${userCount}`); console.log(" Password for all accounts: Dev123456!"); console.log(" Sign in at: http://localhost:3100/auth/signin"); console.log(); console.log("Note: TOTP is disabled for all users. Re-enable via Settings if needed.");