← 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));