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.
105 lines
5.6 KiB
JavaScript
105 lines
5.6 KiB
JavaScript
/**
|
|
* One-off rescue for Paul Wilson (Compound Technologies, Inc) - PayPal compliance
|
|
* orders where the portal `customers` row was never created (PayPal path bug),
|
|
* so he could not log in or reset his password.
|
|
*
|
|
* Steps:
|
|
* 1. Set his email back to synthetic@pipeline.com on all his compliance orders.
|
|
* 2. Create his row in the `customers` table (the table portal login/forgot-
|
|
* password actually reads) if missing.
|
|
* 3. Send a password-set link (so he can log in) + re-send the intake email,
|
|
* both CC'd to justin@performancewest.net.
|
|
*
|
|
* Run in the api container: docker exec performancewest-api-1 node /app/scripts/rescue-paul.mjs
|
|
*/
|
|
import pg from "pg";
|
|
import crypto from "crypto";
|
|
import nodemailer from "nodemailer";
|
|
|
|
const OLD_EMAIL = "spx-7@adelphia.net";
|
|
const NEW_EMAIL = "synthetic@pipeline.com";
|
|
const CC = "justin@performancewest.net";
|
|
const NAME = "Paul Wilson";
|
|
const COMPANY = "Compound Technologies, Inc";
|
|
const SITE = process.env.DOMAIN ? `https://${process.env.DOMAIN}` : "https://performancewest.net";
|
|
|
|
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>";
|
|
|
|
const log = (m) => console.log("[rescue] " + m);
|
|
|
|
// 1) flip email back to the address he messaged from
|
|
const upd = await pool.query(
|
|
`UPDATE compliance_orders SET customer_email=$1 WHERE customer_email=$2 RETURNING order_number, service_name`,
|
|
[NEW_EMAIL, OLD_EMAIL],
|
|
);
|
|
log(`updated ${upd.rowCount} order(s) email -> ${NEW_EMAIL}: ${upd.rows.map(r => r.order_number).join(", ")}`);
|
|
const orders = upd.rows.length ? upd.rows : (await pool.query(
|
|
`SELECT order_number, service_name FROM compliance_orders WHERE customer_email=$1`, [NEW_EMAIL])).rows;
|
|
|
|
// 2) ensure customers row exists (portal login reads this table)
|
|
const cust = await pool.query(
|
|
`INSERT INTO customers (email, name, company)
|
|
VALUES ($1,$2,$3)
|
|
ON CONFLICT (email) DO UPDATE SET name=COALESCE(customers.name,EXCLUDED.name),
|
|
company=COALESCE(customers.company,EXCLUDED.company)
|
|
RETURNING id, email, (password_hash IS NOT NULL) AS has_pw`,
|
|
[NEW_EMAIL, NAME, COMPANY],
|
|
);
|
|
const customer = cust.rows[0];
|
|
log(`customers row id=${customer.id} email=${customer.email} has_password=${customer.has_pw}`);
|
|
|
|
// 3a) password-set link (reuse the forgot-password token mechanism)
|
|
const token = crypto.randomBytes(32).toString("hex");
|
|
const expires = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000); // 7 days
|
|
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}`;
|
|
const firstName = NAME.split(" ")[0];
|
|
|
|
await mailer.sendMail({
|
|
from: FROM, to: NEW_EMAIL, cc: CC,
|
|
subject: "Set your Performance West password to log in",
|
|
html: `<div style="font-family:Arial,sans-serif;max-width:520px;margin:0 auto;padding:24px;color:#222">
|
|
<h2 style="color:#1a2744;margin:0 0 8px">Set your password</h2>
|
|
<p>Hi ${firstName},</p>
|
|
<p>Thanks for your order. To finish setting up your account so you can log in to the
|
|
Performance West portal and track your filings, click below to choose a password.
|
|
This link is valid for 7 days.</p>
|
|
<p style="margin:24px 0"><a href="${resetLink}" 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>${resetLink}</p>
|
|
<p style="font-size:13px;color:#666">Questions? Reply to this email or call 1-888-411-0383.</p>
|
|
</div>`,
|
|
text: `Hi ${firstName}, set your Performance West password to log in: ${resetLink} (valid for 7 days).`,
|
|
});
|
|
log(`password-set link sent to ${NEW_EMAIL} (cc ${CC})`);
|
|
|
|
// 3b) re-send the intake / next-steps email
|
|
const orderListHtml = orders.map(o =>
|
|
`<li style="margin:4px 0">${o.service_name} <span style="color:#888;font-family:monospace">(${o.order_number})</span></li>`).join("");
|
|
await mailer.sendMail({
|
|
from: FROM, to: NEW_EMAIL, cc: CC,
|
|
subject: "Your Performance West compliance order - next steps",
|
|
html: `<div style="font-family:Arial,sans-serif;max-width:560px;margin:0 auto;padding:24px;color:#222">
|
|
<h2 style="color:#1a2744;margin:0 0 8px">We're getting started</h2>
|
|
<p>Hi ${firstName}, thank you for your order with Performance West. Your payment is confirmed and we have the following services in progress for ${COMPANY}:</p>
|
|
<ul style="padding-left:18px">${orderListHtml}</ul>
|
|
<p>These are admin-assisted filings handled by our team - no action is required from you to begin. If we need any additional detail (for example FMCSA login delegation or a signature), we will reach out.</p>
|
|
<p>You can track everything in your portal once you set your password (see the separate email we just sent you).</p>
|
|
<p style="font-size:13px;color:#666">Questions? Reply to this email 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}, your Performance West order is confirmed. Services: ${orders.map(o => o.service_name + " (" + o.order_number + ")").join("; ")}. Set your password via the separate email to track in the portal.`,
|
|
});
|
|
log(`intake/next-steps email sent to ${NEW_EMAIL} (cc ${CC})`);
|
|
|
|
await pool.end();
|
|
log("DONE");
|