The rescue onboarding emails hardcoded a 60-minute expiry -- way too short for a paid customer who hasn't engaged yet (they may not check email for hours/days), so Paul's and Mitchell's links expired before they used them. Onboarding links now last 7 days (ONBOARDING_TTL_MINUTES); the standard security password-RESET window bumped 30min -> 2h. Re-issued fresh 7-day links to all 3 affected customers (none had set a password yet) via reissue-onboarding-links.mjs, cc'd.
59 lines
3.3 KiB
JavaScript
59 lines
3.3 KiB
JavaScript
/**
|
|
* Re-issue LONG-LIVED (7-day) password-set links to the 3 rescued customers
|
|
* whose earlier onboarding links were wrongly set to 60 minutes (and have since
|
|
* expired). None of them set a password yet, so this gets them a working link.
|
|
* CC justin. Onboarding links should be days, not minutes -- these are paid
|
|
* customers who just need to get in, not a security reset.
|
|
*
|
|
* Run: docker exec performancewest-api-1 node /app/scripts/reissue-onboarding-links.mjs
|
|
*/
|
|
import pg from "pg";
|
|
import crypto from "crypto";
|
|
import nodemailer from "nodemailer";
|
|
|
|
const CC = "justin@performancewest.net";
|
|
const SITE = process.env.DOMAIN ? `https://${process.env.DOMAIN}` : "https://performancewest.net";
|
|
const TTL_DAYS = 7;
|
|
const EMAILS = ["mark@adamslumber.com", "synthetic@pipeline.com", "mitchell@allenscrapmetal.com"];
|
|
|
|
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 <noreply@performancewest.net>";
|
|
|
|
for (const email of EMAILS) {
|
|
const cust = await pool.query(`SELECT id, name, (password_hash IS NOT NULL) AS has_pw FROM customers WHERE lower(email)=lower($1)`, [email]);
|
|
if (!cust.rows.length) { console.log(`[reissue] no customers row for ${email} - skip`); continue; }
|
|
const c = cust.rows[0];
|
|
if (c.has_pw) { console.log(`[reissue] ${email} already set a password - skip`); continue; }
|
|
const firstName = (c.name || "there").split(" ")[0];
|
|
|
|
const token = crypto.randomBytes(32).toString("hex");
|
|
await pool.query(
|
|
`INSERT INTO password_reset_tokens (customer_id, token, expires_at) VALUES ($1,$2,$3)`,
|
|
[c.id, token, new Date(Date.now() + TTL_DAYS * 24 * 60 * 60 * 1000)],
|
|
);
|
|
const link = `${SITE}/account/reset-password?token=${token}`;
|
|
|
|
await mailer.sendMail({
|
|
from: FROM, to: email, cc: CC,
|
|
subject: "Your Performance West login link (valid 7 days)",
|
|
html: `<div style="font-family:Arial,sans-serif;max-width:540px;margin:0 auto;padding:24px;color:#222">
|
|
<h2 style="color:#1a2744;margin:0 0 8px">Set your password and log in, ${firstName}</h2>
|
|
<p>Apologies - the link we sent earlier expired too quickly. Here is a fresh one that stays valid for <strong>7 days</strong>.</p>
|
|
<p style="margin:20px 0"><a href="${link}" style="background:#2d4e78;color:#fff;padding:12px 28px;border-radius:8px;text-decoration:none;font-weight:600">Set my password →</a></p>
|
|
<p style="font-size:13px;color:#666">Or paste this link into your browser:<br>${link}</p>
|
|
<p style="font-size:13px;color:#666">Once you're in, you can track your filings and complete any remaining intake. Questions? Reply here or call 1-888-411-0383.</p>
|
|
<p style="font-size:12px;color:#9ca3af">Performance West Inc. · performancewest.net · 1-888-411-0383</p>
|
|
</div>`,
|
|
text: `Hi ${firstName}, apologies the earlier link expired too fast. Set your password to log in (valid 7 days): ${link}. Questions? 1-888-411-0383.`,
|
|
});
|
|
console.log(`[reissue] 7-day login link sent to ${email} (cc ${CC})`);
|
|
}
|
|
|
|
await pool.end();
|
|
console.log("[reissue] DONE");
|