From a8a3f5d2e3737e8c7d760b83a308eb385598e439 Mon Sep 17 00:00:00 2001 From: default Date: Sun, 7 Jun 2026 07:16:30 +0000 Subject: [PATCH] fix(seed): inline scrypt hash so seed.ts runs outside Next.js server context MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The seed script previously imported hashPassword from src/lib/passwords.ts, which has 'import "server-only"' at the top. That guard throws when the file is loaded outside a Next.js server runtime (which is what 'tsx db/seed.ts' is — plain Node). Inline the same scrypt format (matches verifyPassword) so the seed runs from a CLI. --- db/seed.ts | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/db/seed.ts b/db/seed.ts index 4fa7977..cc3fe88 100644 --- a/db/seed.ts +++ b/db/seed.ts @@ -16,7 +16,21 @@ */ import "dotenv/config"; import { Pool } from "pg"; -import { hashPassword } from "../src/lib/passwords"; +import { randomBytes, scryptSync } from "node:crypto"; + +// `src/lib/passwords.ts` has `import "server-only"` which throws when this +// script runs outside a Next.js server context. Inline the same scrypt +// format here (must match `verifyPassword` in passwords.ts) so the seed +// can run from a plain Node process. +const SALT_LEN = 16; +const KEY_LEN = 64; +const DEFAULT_N = 16384; +const ALGO = "scrypt"; +function hashPassword(plain: string): string { + const salt = randomBytes(SALT_LEN).toString("hex"); + const hash = scryptSync(plain, salt, KEY_LEN, { N: DEFAULT_N }).toString("hex"); + return `${ALGO}$${DEFAULT_N}$${salt}$${hash}`; +} // Seed needs elevated privileges (inserts into plans, add_ons, tenants — // tables that are intentionally not RLS-scoped but the runtime app user