diff --git a/backend/src/services/puppeteer-preflight.ts b/backend/src/services/puppeteer-preflight.ts index 506dabeb..b9c0aea2 100644 --- a/backend/src/services/puppeteer-preflight.ts +++ b/backend/src/services/puppeteer-preflight.ts @@ -26,11 +26,34 @@ const TEST_PLATFORM_ID = '6405ef617056e8014d79101b'; const FINGERPRINT_DEMO_URL = 'https://demo.fingerprint.com/'; const AMIUNIQUE_URL = 'https://amiunique.org/fingerprint'; -// IP geolocation API - returns IP, timezone, lat/lng, city, region in one call -const IP_API_URL = 'http://ip-api.com/json'; +// State fallback coordinates (used when IP lookup fails) +const STATE_GEO: Record = { + 'AK': { lat: 61.2181, lng: -149.9003, tz: 'America/Anchorage', city: 'Anchorage' }, + 'AZ': { lat: 33.4484, lng: -112.0740, tz: 'America/Phoenix', city: 'Phoenix' }, + 'CA': { lat: 34.0522, lng: -118.2437, tz: 'America/Los_Angeles', city: 'Los Angeles' }, + 'CO': { lat: 39.7392, lng: -104.9903, tz: 'America/Denver', city: 'Denver' }, + 'CT': { lat: 41.7658, lng: -72.6734, tz: 'America/New_York', city: 'Hartford' }, + 'FL': { lat: 25.7617, lng: -80.1918, tz: 'America/New_York', city: 'Miami' }, + 'IL': { lat: 41.8781, lng: -87.6298, tz: 'America/Chicago', city: 'Chicago' }, + 'MA': { lat: 42.3601, lng: -71.0589, tz: 'America/New_York', city: 'Boston' }, + 'MI': { lat: 42.3314, lng: -83.0458, tz: 'America/Detroit', city: 'Detroit' }, + 'NV': { lat: 36.1699, lng: -115.1398, tz: 'America/Los_Angeles', city: 'Las Vegas' }, + 'NJ': { lat: 40.7357, lng: -74.1724, tz: 'America/New_York', city: 'Newark' }, + 'NY': { lat: 40.7128, lng: -74.0060, tz: 'America/New_York', city: 'New York' }, + 'OH': { lat: 39.9612, lng: -82.9988, tz: 'America/New_York', city: 'Columbus' }, + 'OK': { lat: 35.4676, lng: -97.5164, tz: 'America/Chicago', city: 'Oklahoma City' }, + 'OR': { lat: 45.5152, lng: -122.6784, tz: 'America/Los_Angeles', city: 'Portland' }, + 'PA': { lat: 39.9526, lng: -75.1652, tz: 'America/New_York', city: 'Philadelphia' }, + 'WA': { lat: 47.6062, lng: -122.3321, tz: 'America/Los_Angeles', city: 'Seattle' }, + 'DEFAULT': { lat: 39.8283, lng: -98.5795, tz: 'America/Chicago', city: 'US Center' }, +}; + +function getStateGeo(stateCode: string): { lat: number; lng: number; tz: string; city: string } { + return STATE_GEO[stateCode?.toUpperCase()] || STATE_GEO['DEFAULT']; +} interface GeoData { - ip: string; + ip: string | null; timezone: string; city: string; region: string; @@ -45,9 +68,33 @@ interface GeoData { async function getProxyGeoData(page: any): Promise { try { // Use browser to fetch - this goes through the proxy + // Try ipify first (simpler, more reliable) const geoData = await page.evaluate(async () => { - const response = await fetch('http://ip-api.com/json?fields=status,query,timezone,city,regionName,lat,lon'); - return response.json(); + try { + const response = await fetch('http://ip-api.com/json?fields=status,query,timezone,city,regionName,lat,lon', { + signal: AbortSignal.timeout(10000) + }); + return response.json(); + } catch { + // Fallback to ipapi.co (https) + try { + const response = await fetch('https://ipapi.co/json/', { + signal: AbortSignal.timeout(10000) + }); + const data = await response.json(); + return { + status: 'success', + query: data.ip, + timezone: data.timezone, + city: data.city, + regionName: data.region, + lat: data.latitude, + lon: data.longitude, + }; + } catch { + return null; + } + } }); if (geoData?.status === 'success') { @@ -261,23 +308,44 @@ export async function runPuppeteerPreflight( }; // ========================================================================= - // STEP 1: Detect proxy IP and get actual geolocation + // STEP 1: Detect proxy IP and get actual geolocation (with state fallback) // ========================================================================= console.log(`[PuppeteerPreflight] Detecting proxy IP and location...`); - const geoData = await getProxyGeoData(page); + let geoData = await getProxyGeoData(page); + + // Fallback to state coords if IP lookup fails + if (!geoData && targetState) { + const stateGeo = getStateGeo(targetState); + geoData = { + ip: null, + timezone: stateGeo.tz, + city: stateGeo.city, + region: targetState, + lat: stateGeo.lat, + lng: stateGeo.lng, + }; + console.log(`[PuppeteerPreflight] IP lookup failed, using state fallback: ${stateGeo.city}, ${targetState}`); + } if (!geoData) { - result.error = 'Failed to detect proxy IP/location'; - console.log(`[PuppeteerPreflight] FAILED - ${result.error}`); - result.responseTimeMs = Date.now() - startTime; - return result; + // No IP data and no target state - use default + const defaultGeo = getStateGeo('DEFAULT'); + geoData = { + ip: null, + timezone: defaultGeo.tz, + city: defaultGeo.city, + region: 'US', + lat: defaultGeo.lat, + lng: defaultGeo.lng, + }; + console.log(`[PuppeteerPreflight] Using default US coords`); } result.proxyIp = geoData.ip; result.proxyConnected = true; result.detectedTimezone = geoData.timezone; result.detectedLocation = { city: geoData.city, region: geoData.region }; - console.log(`[PuppeteerPreflight] Proxy IP: ${geoData.ip} - ${geoData.city}, ${geoData.region} (${geoData.timezone})`); + console.log(`[PuppeteerPreflight] Using: ${geoData.city}, ${geoData.region} (${geoData.timezone})${geoData.ip ? ` IP: ${geoData.ip}` : ' [fallback]'}`); // ========================================================================= // STEP 2: Configure antidetect to match actual proxy location