UNPKG

ggez-banking-sdk

Version:

A Node.js package to handle GGEZ Banking API endpoints, Simplify the process of managing CRUD operations with this efficient and easy-to-use package.

161 lines (160 loc) 5.63 kB
import axios from "axios"; import { Endpoints } from "../constant/constant"; class GeoHelper { // #region Constants & State static CACHE_KEY_PREFIX = "geo_coordinates_cache"; static DEFAULT_TTL_MS = 24 * 60 * 60 * 1000; static cache = new Map(); static pending = new Map(); baseUrl; axiosInstance; // #endregion // #region Constructor constructor(baseUrl) { this.baseUrl = baseUrl; this.axiosInstance = axios.create({ baseURL: baseUrl }); } // #endregion // #region Cache static clearCache() { GeoHelper.cache.clear(); GeoHelper.pending.clear(); try { const keysToRemove = []; for (let i = 0; i < localStorage.length; i++) { const key = localStorage.key(i); if (key?.startsWith(GeoHelper.CACHE_KEY_PREFIX)) { keysToRemove.push(key); } } keysToRemove.forEach((key) => localStorage.removeItem(key)); } catch { // Ignore errors when clearing cache } } static storageKey(baseUrl) { return `${GeoHelper.CACHE_KEY_PREFIX}:${baseUrl}`; } static readCache(baseUrl, ttlMs) { const memEntry = GeoHelper.cache.get(baseUrl); if (memEntry) { if (Date.now() - memEntry.timestamp < ttlMs) return memEntry; GeoHelper.cache.delete(baseUrl); } try { const cachedDataStr = localStorage.getItem(GeoHelper.storageKey(baseUrl)); if (!cachedDataStr) return null; const cachedData = JSON.parse(cachedDataStr); if (Date.now() - cachedData.timestamp < ttlMs) { GeoHelper.cache.set(baseUrl, cachedData); return cachedData; } localStorage.removeItem(GeoHelper.storageKey(baseUrl)); } catch (error) { console.warn("Failed to read geo coordinates from cache:", error); } return null; } static writeCache(baseUrl, data) { GeoHelper.cache.set(baseUrl, data); try { localStorage.setItem(GeoHelper.storageKey(baseUrl), JSON.stringify(data)); } catch (error) { console.warn("Failed to store geo coordinates in cache:", error); } } // #endregion // #region Helpers async fetchIPAddressAndLocation() { const response = await this.axiosInstance.get(Endpoints.IPAddress); const ipAddressAndLocation = response.data.value; if (!ipAddressAndLocation) return null; try { return JSON.parse(ipAddressAndLocation); } catch (error) { console.warn("Failed to parse IP address and location:", error); return null; } } // Dedupes concurrent fetches per baseURL so N callers on cold cache // result in 1 network call instead of N. fetchSharedLocation() { const existing = GeoHelper.pending.get(this.baseUrl); if (existing) return existing; const inflight = this.fetchIPAddressAndLocation(); GeoHelper.pending.set(this.baseUrl, inflight); const cleanup = () => { if (GeoHelper.pending.get(this.baseUrl) === inflight) { GeoHelper.pending.delete(this.baseUrl); } }; inflight.then(cleanup, cleanup); return inflight; } async loadGeoData(ttlMs, forceRefresh) { if (!forceRefresh && ttlMs > 0) { const cached = GeoHelper.readCache(this.baseUrl, ttlMs); if (cached) return cached; } const location = await this.fetchSharedLocation(); // Failed fetch returns fallback but does NOT cache it — a transient // network blip should not poison the cache for 24h. if (!location) { return { geo_coordinates: GeoHelper.fallbackGeoCoordinates(), ip_address: "", timestamp: Date.now(), }; } const data = { geo_coordinates: GeoHelper.toGeoCoordinates(location), ip_address: location.ip_address || "", timestamp: Date.now(), }; if (ttlMs > 0) GeoHelper.writeCache(this.baseUrl, data); return data; } static toGeoCoordinates(location) { const { latitude, longitude, city, country } = location; return { latitude: latitude ?? 0, longitude: longitude ?? 0, position_description: `${city || "N/A"}, ${country || "N/A"}`, }; } static fallbackGeoCoordinates() { return { latitude: 0, longitude: 0, position_description: "N/A, N/A", }; } // #endregion // #region Public API async getGeoCoordinates(ttlMs = GeoHelper.DEFAULT_TTL_MS, forceRefresh = false) { const data = await this.loadGeoData(ttlMs, forceRefresh); return data.geo_coordinates; } async getIPAddress(ttlMs = GeoHelper.DEFAULT_TTL_MS, forceRefresh = false) { const data = await this.loadGeoData(ttlMs, forceRefresh); return data.ip_address ?? ""; } async getGeoCoordinatesAndIPAddress(ttlMs = GeoHelper.DEFAULT_TTL_MS, forceRefresh = false) { const data = await this.loadGeoData(ttlMs, forceRefresh); return { geo_coordinates: data.geo_coordinates, ip_address: data.ip_address ?? "", }; } } export { GeoHelper };