From f7212b3969e368ab8e6037526a9822f1ec25e8f7 Mon Sep 17 00:00:00 2001 From: justin Date: Wed, 17 Jun 2026 10:19:53 -0500 Subject: [PATCH] scripts: one-off fresh password-set link for Paul Wilson (ERPNext auth) --- scripts/rescue-paul-set-password.mjs | 69 ++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 scripts/rescue-paul-set-password.mjs diff --git a/scripts/rescue-paul-set-password.mjs b/scripts/rescue-paul-set-password.mjs new file mode 100644 index 0000000..6b87920 --- /dev/null +++ b/scripts/rescue-paul-set-password.mjs @@ -0,0 +1,69 @@ +/** + * One-off: send Paul Wilson (Compound Technologies, Inc) a fresh password-set + * link so he can log in to the portal. + * + * Context: customer portal auth now uses ERPNext as the single source of truth + * for passwords (commit 9c87759). Paul's old Postgres password is no longer + * used for login, and his previous 7-day set-password link has expired. This + * mints a fresh 7-day reset token and emails ONLY the set-password link + * (NOT the earlier "next steps" email). When he clicks it, /reset-password + * writes his chosen password to ERPNext. CC justin@performancewest.net. + * + * Run in the api container (uses its DATABASE_URL + SMTP_* env), piped via stdin: + * docker exec -i performancewest-api-1 node --input-type=module < scripts/rescue-paul-set-password.mjs + */ +import pg from "pg"; +import crypto from "crypto"; +import nodemailer from "nodemailer"; + +const EMAIL = "synthetic@pipeline.com"; +const CC = "justin@performancewest.net"; +const NAME = "Paul Wilson"; +const SITE = process.env.DOMAIN ? `https://${process.env.DOMAIN}` : "https://performancewest.net"; +const firstName = NAME.split(" ")[0]; + +const pool = new pg.Pool({ connectionString: process.env.DATABASE_URL }); +const mailer = nodemailer.createTransport({ + host: process.env.SMTP_HOST || "co.carrierone.com", + port: parseInt(process.env.SMTP_PORT || "587", 10), + secure: false, + auth: { user: process.env.SMTP_USER, pass: process.env.SMTP_PASS }, +}); +const FROM = process.env.SMTP_FROM || "Performance West "; +const log = (m) => console.log("[rescue] " + m); + +// Look up his customers row (portal profile + reset-token owner). +const cust = await pool.query(`SELECT id, email FROM customers WHERE email = $1`, [EMAIL]); +if (cust.rows.length === 0) throw new Error(`no customers row for ${EMAIL}`); +const customer = cust.rows[0]; +log(`customers row id=${customer.id} email=${customer.email}`); + +// Mint a fresh 7-day reset token. +const token = crypto.randomBytes(32).toString("hex"); +const expires = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000); +await pool.query( + `INSERT INTO password_reset_tokens (customer_id, token, expires_at) VALUES ($1, $2, $3)`, + [customer.id, token, expires], +); +const resetLink = `${SITE}/account/reset-password?token=${token}`; +log(`reset token minted, expires ${expires.toISOString()}`); + +await mailer.sendMail({ + from: FROM, to: EMAIL, cc: CC, + subject: "Set your Performance West password to log in", + html: `
+

Set your password

+

Hi ${firstName},

+

To log in to the Performance West portal and track your filings, click below to + choose your password. This link is valid for 7 days.

+

Set my password →

+

Or paste this link into your browser:
${resetLink}

+

Once you're in, you can view your orders and complete any remaining intake forms. Questions? Reply to this email or call 1-888-411-0383.

+

Performance West Inc. · performancewest.net · 1-888-411-0383

+
`, + text: `Hi ${firstName}, set your Performance West password to log in: ${resetLink} (valid for 7 days). Questions? 1-888-411-0383.`, +}); +log(`password-set link sent to ${EMAIL} (cc ${CC})`); + +await pool.end(); +log("DONE");