- Add WorkerFingerprint interface with timezone, city, state, ip, locale - Store fingerprint in TaskWorker after preflight passes - Pass fingerprint through TaskContext to handlers - Apply timezone via CDP and locale via Accept-Language header - Ensures browser fingerprint matches proxy IP location This fixes anti-detect detection where timezone/locale mismatch with proxy IP was getting blocked by Cloudflare. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
80 lines
2.8 KiB
TypeScript
80 lines
2.8 KiB
TypeScript
/**
|
|
* Find ALL differing fields between MED and REC product payloads
|
|
*/
|
|
import puppeteer from 'puppeteer-extra';
|
|
import StealthPlugin from 'puppeteer-extra-plugin-stealth';
|
|
puppeteer.use(StealthPlugin());
|
|
|
|
async function main() {
|
|
const browser = await puppeteer.launch({ headless: 'new', args: ['--no-sandbox'] });
|
|
const page = await browser.newPage();
|
|
|
|
await page.goto('https://www.iheartjane.com/stores', { waitUntil: 'domcontentloaded' });
|
|
await new Promise(r => setTimeout(r, 2000));
|
|
|
|
// Get full product payload from REC store
|
|
const recProduct = await page.evaluate(async () => {
|
|
const res = await fetch('https://search.iheartjane.com/1/indexes/menu-products-production/query', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ query: '', hitsPerPage: 1, filters: 'store_id=3379' }),
|
|
});
|
|
const data = await res.json();
|
|
return data.hits?.[0];
|
|
});
|
|
|
|
const productId = recProduct?.product_id;
|
|
|
|
// Get same product from MED store
|
|
const medProduct = await page.evaluate(async (pid: number) => {
|
|
const res = await fetch('https://search.iheartjane.com/1/indexes/menu-products-production/query', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ query: '', hitsPerPage: 100, filters: 'store_id=4540' }),
|
|
});
|
|
const data = await res.json();
|
|
return data.hits?.find((h: any) => h.product_id === pid);
|
|
}, productId);
|
|
|
|
console.log('Product:', recProduct?.name, '(ID:', productId, ')\n');
|
|
|
|
// Get all keys
|
|
const allKeys = new Set([...Object.keys(recProduct || {}), ...Object.keys(medProduct || {})]);
|
|
const sortedKeys = [...allKeys].sort();
|
|
|
|
console.log('=== ALL KEYS IN PAYLOAD ===');
|
|
console.log(sortedKeys.join(', '));
|
|
|
|
console.log('\n=== FIELDS THAT DIFFER ===');
|
|
let diffCount = 0;
|
|
for (const key of sortedKeys) {
|
|
const recVal = JSON.stringify(recProduct?.[key]);
|
|
const medVal = JSON.stringify(medProduct?.[key]);
|
|
if (recVal !== medVal) {
|
|
diffCount++;
|
|
console.log(`${key}:`);
|
|
console.log(` REC: ${recVal?.substring(0, 100)}`);
|
|
console.log(` MED: ${medVal?.substring(0, 100)}`);
|
|
}
|
|
}
|
|
|
|
if (diffCount === 0) {
|
|
console.log('(none - payloads are identical)');
|
|
}
|
|
|
|
// Check for limit/allowance related fields
|
|
console.log('\n=== LIMIT-RELATED FIELDS ===');
|
|
const limitFields = sortedKeys.filter(k =>
|
|
k.includes('limit') || k.includes('max') || k.includes('allow') ||
|
|
k.includes('quantity') || k.includes('cart') || k.includes('medical') ||
|
|
k.includes('rec') || k.includes('weight')
|
|
);
|
|
for (const key of limitFields) {
|
|
console.log(`${key}: REC=${JSON.stringify(recProduct?.[key])} | MED=${JSON.stringify(medProduct?.[key])}`);
|
|
}
|
|
|
|
await browser.close();
|
|
}
|
|
|
|
main().catch(console.error);
|