11 Commits

Author SHA1 Message Date
aef5000539 fix(bikegear): serialize browser fetches and slow 429 retries 2026-08-01 18:52:58 +05:30
2b8cf2c6fc fix(bikegear): support Webshare proxy config and cache-only mode 2026-07-31 01:06:37 +05:30
19fd7f16ea fix(bikegear): add proxy support to Playwright browser context
BIKEGEAR_PROXY_URL now applies to the browser-mode fetcher too.
The VPS IP is permanently blocked at Cloudflare network level — a residential
proxy is the only remaining solution. Set BIKEGEAR_PROXY_URL=http://user:pass@host:port.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-29 07:34:32 +05:30
76bd69a92f fix(bikegear): switch to persistent browser profile + stealth script to beat Cloudflare detection
Previous approach (newContext per run) was still detected via navigator.plugins=0
and window.chrome=undefined. Two fixes:
1. launchPersistentContext writes a real browser profile to disk — cookies,
   cache and fingerprint survive restarts so Cloudflare trusts the same "user"
2. addInitScript patches plugins, chrome runtime, webdriver and languages on
   every page, hiding remaining headless Chromium signals

recycleBrowserContext now just closes stale pages (profile stays on disk).
closeBrowser just closes the browser process — profile already persisted.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-29 07:25:35 +05:30
e4b3292533 fix(bikegear): always delay between brands and tighten anti-ban settings
- Between-brand delay now fires for skipped brands too (was only firing for
  brands with products), fixing rapid-fire requests that caused 403 on detail pages
- Default request delay increased 1500ms -> 2500ms
- Browser context recycle interval tightened 20 -> 10 brands

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-24 17:02:57 +05:30
46d018c56e fix(bikegear): recycle browser context every 20 brands to prevent memory OOM restart
Running Playwright Chromium for 2+ hours with one shared context accumulates
memory until PM2/OOM kills the process with no error. Fix: save cookies and
close/reopen the context every 20 brands so Chromium memory is freed mid-run.
Cookie persistence means the new context resumes with the cf_clearance session.
Override interval with BIKEGEAR_RECYCLE_EVERY env var.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-24 16:27:00 +05:30
a8adf00732 fix(bikegear): reduce browser concurrency to 1 and add 1.5s delay between requests
Cloudflare 429s when running 2+ concurrent browser pages against bikegear.in.
Browser mode now runs 1 page at a time with a 1.5s polite delay after each load.
HTTP mode keeps its original concurrency (3 listing / 2 detail).
Override with BIKEGEAR_LISTING_CONCURRENCY / BIKEGEAR_DETAIL_CONCURRENCY / BIKEGEAR_REQUEST_DELAY.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-22 18:13:29 +05:30
0fbe75dd3d fix(bikegear): use Playwright browser mode to bypass Cloudflare on VPS IPs
HTTP requests from datacenter IPs get 403 from bikegear.in Cloudflare WAF.
Adds BIKEGEAR_FETCH_MODE=browser (default) which runs a headless Chromium via
Playwright — already installed — and navigates pages like a real user, bypassing
the JS challenge without needing a paid proxy.

Set BIKEGEAR_FETCH_MODE=http to keep the old axios path (for local/residential IPs).
Browser instance is shared across the full scrape run and closed cleanly on finish.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-22 18:05:24 +05:30
846e467b85 fix(bikegear): route scraper through residential proxy to bypass Cloudflare 403 on server IPs
Datacenter IPs are blocked by bikegear.in Cloudflare WAF.
Switches fetchHtml from native fetch to axios with BIKEGEAR_PROXY_URL support.
Set BIKEGEAR_PROXY_URL=http://user:pass@host:port in server .env to enable.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-14 01:17:29 +05:30
bf207e48a1 fix(bikegear): fix 2 scraper issues found during test run
1. parseProductJsonLd: remove sku requirement — out-of-stock products have
   Product JSON-LD but no sku field, causing false "No product JSON-LD found" errors.
   Now picks block with offers.price or description instead.

2. extractProductImages: add fallback for non-standard image paths — products
   with images at /catalog/KTM/... or other custom paths were falling through
   to JSON-LD fallback (1 image). Now also scans the product-img anchor HTML.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-13 18:18:35 +05:30
c2e037310d feat: add bikegear.in import source (Alpinestars as first brand)
Adds a new OpenCart-based source (bikegear.in) to the import pipeline.
2-phase scraper: listing pages collect product URLs, detail pages extract
JSON-LD (name, price in INR, brand, SKU) + description table HTML + gallery images.

- sources/bikegear/brands.js  — brand list (Alpinestars active, 40+ commented)
- sources/bikegear/scraper.js — HTML scraper with full Sec-Fetch browser headers
- sources/bikegear/converter.js — Shopify-ready converter (record.scraped || record pattern)
- sources/bikegear/index.js   — 24h cache, zero-success guard, brands[] export
- sources/index.js             — register bikegear source

Set BIKEGEAR_CACHE_HOURS=0 to force re-scrape.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-13 17:27:06 +05:30