← back to Dw Cowtan Recrawl

probe2.js

39 lines

#!/usr/bin/env node
// Probe the cowtan /Search AJAX API raw product object + hunt for a design-detail JSON endpoint.
const https = require('https');
function httpPost(url, body) {
  return new Promise((resolve, reject) => {
    const p = new URL(url);
    const req = https.request({ hostname: p.hostname, port: 443, path: p.pathname, method: 'POST', timeout: 30000,
      headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'Content-Length': Buffer.byteLength(body), 'User-Agent': 'Mozilla/5.0', 'Accept': 'application/json', 'X-Requested-With': 'XMLHttpRequest' } },
      res => { if (res.statusCode == 301 || res.statusCode == 302) { return httpPost(res.headers.location, body).then(resolve).catch(reject); } let d = ''; res.on('data', c => d += c); res.on('end', () => { try { resolve(JSON.parse(d)); } catch (e) { reject(new Error('JSON ' + e.message + ' len=' + d.length)); } }); });
    req.on('error', reject); req.on('timeout', () => { req.destroy(); reject(new Error('timeout')); });
    req.write(body); req.end();
  });
}
function httpGet(url, m = 4) {
  return new Promise((res, rej) => { https.get(url, { timeout: 25000, headers: { 'User-Agent': 'Mozilla/5.0', 'Accept': 'application/json, text/html' } }, r => {
    if ((r.statusCode == 301 || r.statusCode == 302) && r.headers.location && m > 0) { r.resume(); const loc = r.headers.location.startsWith('http') ? r.headers.location : 'https://designs.cowtan.com' + r.headers.location; return httpGet(loc, m - 1).then(res).catch(rej); }
    let d = ''; r.on('data', c => d += c); r.on('end', () => res({ status: r.statusCode, body: d, ct: r.headers['content-type'] })); }).on('error', rej).on('timeout', function () { this.destroy(); rej(new Error('timeout')); }); });
}
function buildBody(pi, tv) { const p = ['Keywords=', 'NewOnly=false']; ['F', 'W', 'T'].forEach((t, i) => { p.push('Types%5B' + i + '%5D.Value=' + t); p.push('Types%5B' + i + '%5D.Selected=' + (t === tv ? 'true' : 'false')); }); p.push('PageIndex=' + pi); return p.join('&'); }
(async () => {
  console.log('=== /Search raw product[0] keys ===');
  const f = await httpPost('https://designs.cowtan.com/Search', buildBody(0, 'W'));
  const prod = (f.Products && f.Products[0] && f.Products[0][0]) || null;
  if (prod) { console.log('keys:', Object.keys(prod).join(', ')); console.log(JSON.stringify(prod, null, 1).slice(0, 1500)); }
  else console.log('no product, top keys:', Object.keys(f).join(', '));
  // hunt detail endpoints
  const code = prod ? prod.StockCode : '1020-01';
  const cands = [
    '/Design/Detail/' + code, '/Design/Get/' + code, '/api/design/' + code,
    '/Product/' + code, '/Design/Product?stockCode=' + encodeURIComponent(code),
    '/Search/Product?stockCode=' + encodeURIComponent(code), '/Design/Data/' + code
  ];
  console.log('\n=== detail endpoint probes for', code, '===');
  for (const c of cands) {
    try { const r = await httpGet('https://designs.cowtan.com' + c); console.log(c, '->', r.status, (r.ct || '').split(';')[0], 'len=' + r.body.length, /flatshots|RoomScene|Images/.test(r.body) ? 'HAS-IMG-HINT' : ''); }
    catch (e) { console.log(c, '-> ERR', e.message); }
  }
})().catch(e => console.error('FATAL', e.message));