UNPKG

@fleetbase/ember-core

Version:

Provides all the core services, decorators and utilities for building a Fleetbase extension for the Console.

245 lines (221 loc) 6.77 kB
/** * Lookup user's IP address and geolocation information using geoiplookup.io * * This method calls the geoiplookup.io API directly from the browser to get * accurate user location data, avoiding the issue of server-side IP lookup * returning the server's location instead of the user's location. * * @param {Object} options - Configuration options * @param {number} options.timeout - Request timeout in milliseconds (default: 5000) * @param {boolean} options.cache - Whether to cache the result (default: true) * @returns {Promise<Object>} User's IP and geolocation data * * @example * const whois = await lookupUserIp(); * console.log(whois.city); // "New York" * console.log(whois.country_code); // "US" */ export default async function lookupUserIp(options = {}) { const { timeout = 5000, cache = true } = options; // Check cache first if enabled if (cache) { const cached = getCachedWhois(); if (cached) { return cached; } } // Try multiple APIs with fallback const apis = [ { url: 'https://json.geoiplookup.io/', normalize: normalizeGeoIPLookup, }, { url: 'https://ipapi.co/json/', normalize: normalizeIPApi, }, ]; for (const api of apis) { try { const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), timeout); const response = await fetch(api.url, { signal: controller.signal, headers: { Accept: 'application/json', }, }); clearTimeout(timeoutId); if (!response.ok) { console.warn(`[lookupUserIp] ${api.url} returned ${response.status}`); continue; } const data = await response.json(); const normalized = api.normalize(data); const normalizedWithTimezone = ensureWhoisTimezone(normalized); // Cache the result if enabled if (cache) { cacheWhois(normalizedWithTimezone); } return normalizedWithTimezone; } catch (error) { console.warn(`[lookupUserIp] ${api.url} failed:`, error.message); // Continue to next API } } // All APIs failed, return fallback console.error('[lookupUserIp] All IP lookup APIs failed, using fallback data'); return getFallbackWhois(); } /** * Normalize geoiplookup.io response to match Fleetbase whois format */ function normalizeGeoIPLookup(data) { return { ip: data.ip, city: data.city, region: data.region, country_code: data.country_code, country_name: data.country_name, continent_code: data.continent_code, continent_name: data.continent_name, latitude: data.latitude, longitude: data.longitude, postal_code: data.postal_code, timezone: data.timezone_name || getBrowserTimezone(), currency: { code: data.currency_code, name: data.currency_name, }, languages: [ { code: data.language_code, name: data.language_name, }, ], isp: data.isp, org: data.org, asn: data.asn, connection_type: data.connection_type, // Metadata _source: 'geoiplookup.io', _timestamp: Date.now(), }; } /** * Normalize ipapi.co response to match Fleetbase whois format */ function normalizeIPApi(data) { return { ip: data.ip, city: data.city, region: data.region, country_code: data.country_code, country_name: data.country_name, continent_code: data.continent_code, continent_name: null, latitude: data.latitude, longitude: data.longitude, postal_code: data.postal, timezone: data.timezone || getBrowserTimezone(), currency: { code: data.currency, name: data.currency_name, }, languages: data.languages ? data.languages.split(',').map((lang) => ({ code: lang.trim(), name: lang.trim() })) : [], isp: data.org, org: data.org, asn: data.asn, connection_type: null, // Metadata _source: 'ipapi.co', _timestamp: Date.now(), }; } /** * Get cached whois data from localStorage */ function getCachedWhois() { try { const cached = localStorage.getItem('fleetbase:whois'); if (!cached) { return null; } const data = JSON.parse(cached); const age = Date.now() - data._timestamp; const maxAge = 60 * 60 * 1000; // 1 hour if (age > maxAge) { localStorage.removeItem('fleetbase:whois'); return null; } const normalized = ensureWhoisTimezone(data); if (normalized.timezone !== data.timezone) { cacheWhois(normalized); } return normalized; } catch (error) { console.error('[getCachedWhois] Error reading cache:', error); return null; } } /** * Cache whois data to localStorage */ function cacheWhois(data) { try { localStorage.setItem('fleetbase:whois', JSON.stringify(data)); } catch (error) { console.error('[cacheWhois] Error writing cache:', error); } } /** * Get fallback whois data when all APIs fail */ function getFallbackWhois() { // Try to get browser language and timezone as fallback const browserLang = navigator.language || 'en-US'; const timezone = getBrowserTimezone(); return { ip: null, city: null, region: null, country_code: null, country_name: null, continent_code: null, continent_name: null, latitude: null, longitude: null, postal_code: null, timezone: timezone, currency: { code: null, name: null, }, languages: [ { code: browserLang.split('-')[0], name: browserLang, }, ], isp: null, org: null, asn: null, connection_type: null, _source: 'fallback', _timestamp: Date.now(), }; } export function getBrowserTimezone() { try { return Intl.DateTimeFormat().resolvedOptions().timeZone || null; } catch (error) { return null; } } export function ensureWhoisTimezone(whois = {}) { whois = whois || {}; return { ...whois, timezone: whois.timezone || getBrowserTimezone(), }; }