← back to Norma Platform
fix(set-admin-password): read .env.local first, echo DB host, abort on multi-row (Cody gate)
7c027a1b4872e30962b86818eb6e29b4a721efd8 · 2026-08-28 00:45:05 -0700 · Steve Abrams
Files touched
M scripts/set-admin-password.mjs
Diff
commit 7c027a1b4872e30962b86818eb6e29b4a721efd8
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Fri Aug 28 00:45:05 2026 -0700
fix(set-admin-password): read .env.local first, echo DB host, abort on multi-row (Cody gate)
---
scripts/set-admin-password.mjs | 21 ++++++++++++++++++---
1 file changed, 18 insertions(+), 3 deletions(-)
diff --git a/scripts/set-admin-password.mjs b/scripts/set-admin-password.mjs
index e666085..2cf2206 100644
--- a/scripts/set-admin-password.mjs
+++ b/scripts/set-admin-password.mjs
@@ -11,13 +11,19 @@ import pg from 'pg';
import fs from 'node:fs';
import path from 'node:path';
-if (!process.env.DATABASE_URL && fs.existsSync('.env')) {
- for (const line of fs.readFileSync('.env', 'utf8').split('\n')) {
+// PM2 loads the fleet via --env-file=.env.local (see ecosystem.config.js), and
+// DATABASE_URL lives there — so check .env.local FIRST, then .env as a fallback.
+const envFile = ['.env.local', '.env'].find((f) => fs.existsSync(f));
+if (!process.env.DATABASE_URL && envFile) {
+ for (const line of fs.readFileSync(envFile, 'utf8').split('\n')) {
const m = line.match(/^\s*DATABASE_URL\s*=\s*(.*)$/);
if (m) { process.env.DATABASE_URL = m[1].trim().replace(/^["']|["']$/g, ''); break; }
}
}
-if (!process.env.DATABASE_URL) { console.error('FATAL: DATABASE_URL not set (env or .env)'); process.exit(2); }
+if (!process.env.DATABASE_URL) {
+ console.error('FATAL: DATABASE_URL not set (checked env, .env.local, .env)');
+ process.exit(2);
+}
const username = process.env.TARGET_USER || 'admin';
const newPw = process.env.NEW_PW || 'DW2024!';
@@ -25,6 +31,8 @@ const newPw = process.env.NEW_PW || 'DW2024!';
const { Client } = pg;
const c = new Client({ connectionString: process.env.DATABASE_URL });
await c.connect();
+// Audit trail: prove which server/db we hit (host + db only — never the password).
+try { const u = new URL(process.env.DATABASE_URL); console.log(`DB: ${u.hostname}:${u.port || 5432}${u.pathname}`); } catch { /* non-URL DSN */ }
// 1. Capture current hash(es) for rollback
const before = await c.query(
@@ -36,6 +44,13 @@ if (before.rows.length === 0) {
await c.end();
process.exit(3);
}
+if (before.rows.length > 1) {
+ // schema.sql UNIQUEs username, but guard in case a prod migration dropped it —
+ // a single-row rollback file would silently under-capture a multi-row overwrite.
+ console.error(`ABORT: ${before.rows.length} rows for username='${username}' — refusing to overwrite ambiguously.`);
+ await c.end();
+ process.exit(4);
+}
const stamp = new Date().toISOString().replace(/[:.]/g, '-');
const restoreDir = path.join('scripts', 'password-restore');
fs.mkdirSync(restoreDir, { recursive: true });
← c5e8f58 scripts: reversible set-admin-password helper (bcrypt, captu
·
back to Norma Platform
·
feat(set-admin-password): NEW_PW_B64 input to survive shell db0ecc2 →