← back to Hollywood Import

innov-recon.mjs

46 lines

// Innovations trade login + price recon. Uses the shared DW trade login (same as Momentum,
// from .env). reCAPTCHA v3 → real Chrome mints token. Checks if prices unlock + dumps structure.
import { chromium } from 'playwright';
import fs from 'node:fs';
const E = Object.fromEntries(fs.readFileSync(new URL('.env', import.meta.url), 'utf8')
  .split('\n').filter(l => l && !l.startsWith('#') && l.includes('=')).map(l => { const i = l.indexOf('='); return [l.slice(0, i).trim(), l.slice(i + 1).trim()]; }));
const USER = E.INNOVATIONS_USER, PASS = E.INNOVATIONS_PASS;  // Innovations trade login (Steve-confirmed)
const BASE = E.INNOVATIONS_BASE || 'https://www.innovationsusa.com';
const out = { loggedIn: false, notes: [], productPrice: null };

const browser = await chromium.launch({ channel: 'chrome', headless: false });
const ctx = await browser.newContext({ viewport: { width: 1400, height: 1000 } });
const page = await ctx.newPage();

await page.goto(BASE + '/login', { waitUntil: 'domcontentloaded', timeout: 60000 });
await page.waitForTimeout(2500);
const fv = async (sel, val) => { const l = page.locator(sel); const n = await l.count();
  for (let i = 0; i < n; i++) { const el = l.nth(i); if (await el.isVisible().catch(() => 0)) { await el.fill(val, { timeout: 8000 }); return 1; } } return 0; };
const okE = await fv('#email', USER) || await fv('input[type="email"]', USER) || await fv('input[name="email"]', USER);
const okP = await fv('#password', PASS) || await fv('input[type="password"]', PASS) || await fv('input[name="password"]', PASS);
out.notes.push('filled email=' + okE + ' pass=' + okP);
await page.locator('button[type="submit"]:visible, input[type="submit"]:visible').first().click({ timeout: 8000 }).catch(() => page.keyboard.press('Enter'));
await page.waitForTimeout(7000);
out.notes.push('after login url=' + page.url());
// authed? look for logout / account
out.loggedIn = await page.evaluate(() => /log\s?out|sign\s?out|my account/i.test(document.body.innerText));
await ctx.storageState({ path: new URL('innov-auth.json', import.meta.url).pathname });

// try a known product (Anjuna ARP-001) + Damier (known live) to see price
for (const u of ['/item/anjuna/arp-001', '/item/damier/dmr-01']) {
  await page.goto(BASE + u, { waitUntil: 'domcontentloaded', timeout: 40000 }).catch(() => {});
  await page.waitForTimeout(3500);
  const r = await page.evaluate(() => {
    const t = document.body.innerText;
    return { url: location.href, status404: /not found|404|no longer/i.test(document.title + t.slice(0, 200)),
      prices: [...t.matchAll(/\$\s?\d[\d,]*\.?\d{0,2}\s*(\/\s*(yard|yd|roll|sq|panel|each))?/gi)].map(m => m[0]).slice(0, 8),
      hasPrice: /\$\d/.test(t) };
  });
  out.productPrice = out.productPrice || r;
  out.notes.push(u + ' → ' + JSON.stringify(r).slice(0, 200));
}
fs.writeFileSync(new URL('innov-recon-out.json', import.meta.url), JSON.stringify(out, null, 2));
console.log('loggedIn:', out.loggedIn);
out.notes.forEach(n => console.log(' -', n));
await browser.close();