/** * Rescue Mark Adams (mark@adamslumber.com) - paid Jun 1 for an MCS-150 update via * card, but the card/webhook path never created his portal `customers` row (the * login bug), so he could not log in or complete intake -> his MCS-150 is stuck * "NEEDS MANUAL FILING". Now that the login bug is fixed: * 1. create his customers row, * 2. send a password-set link + the intake link (CC justin), so he can log in * and complete intake to unblock the filing. * * Run: docker exec performancewest-api-1 node /app/scripts/rescue-mark.mjs */ import pg from "pg"; import crypto from "crypto"; import nodemailer from "nodemailer"; const EMAIL = "mark@adamslumber.com"; const CC = "justin@performancewest.net"; 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 "; const { rows: orders } = await pool.query( `SELECT order_number, service_name, service_slug, customer_name, intake_data FROM compliance_orders WHERE lower(customer_email)=lower($1) ORDER BY created_at`, [EMAIL], ); if (!orders.length) { console.log("no orders for", EMAIL); process.exit(1); } const name = orders[0].customer_name || "there"; const firstName = name.split(" ")[0]; let company = null; try { const i = typeof orders[0].intake_data === "string" ? JSON.parse(orders[0].intake_data) : orders[0].intake_data; company = i?.company || i?.legal_name || i?.entity_name || null; } catch {} // 1) customers row const c = 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, (password_hash IS NOT NULL) AS has_pw`, [EMAIL, name, company]); console.log(`[rescue] customers row id=${c.rows[0].id} email=${EMAIL} has_password=${c.rows[0].has_pw}`); // 2) password-set link 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.rows[0].id, token, new Date(Date.now() + 7 * 24 * 60 * 60 * 1000)]); const resetLink = `${SITE}/account/reset-password?token=${token}`; const items = orders.map(o => { const url = `${SITE}/order/${o.service_slug}?order=${o.order_number}`; const note = o.service_slug === "mcs150-update" ? " (we will prepare the update from your intake, then send it to you to sign before we submit to FMCSA)" : ""; return `
  • ${o.service_name} (${o.order_number})${note}
  • `; }).join(""); await mailer.sendMail({ from: FROM, to: EMAIL, cc: CC, subject: "Your Performance West order - log in and complete your intake", html: `

    You're all set, ${firstName}

    Thanks for your order. We had a delivery issue that kept our earlier emails from reaching you - that's fixed now, so here is everything you need to get your filing moving.

    1. Set your password to log in (valid for 7 days):

    Set my password →

    Or paste this link: ${resetLink}

    2. Complete the short intake form so we have what we need to prepare your filing (about 2-5 minutes):

    Once your intake is in, we prepare the MCS-150 update and send it to you to review and sign - we never submit to FMCSA without your signature. Questions? Reply here or call 1-888-411-0383.

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

    `, text: `Hi ${firstName}, set your password to log in: ${resetLink} (valid 7 days). Then complete your intake: ${orders.map(o => o.service_name + " (" + o.order_number + "): " + SITE + "/order/" + o.service_slug + "?order=" + o.order_number).join("; ")}. The MCS-150 will be sent to you to sign before we submit to FMCSA. Questions? 1-888-411-0383.`, }); console.log(`[rescue] login + intake email sent to ${EMAIL} (cc ${CC}) for ${orders.length} order(s)`); await pool.end();