UNPKG

client-ip-geolocation

Version:

Fetch client IP and geolocation from an HTTP request

85 lines (84 loc) 3.75 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.getClientIpAndLocation = getClientIpAndLocation; exports.getClientIp = getClientIp; const axios_1 = __importDefault(require("axios")); function getClientIpAndLocation(req) { return __awaiter(this, void 0, void 0, function* () { try { // Extract IP address const clientIp = getClientIp(req); // Call geolocation API const response = yield axios_1.default.get(`http://ip-api.com/json/${clientIp}`); if (response.status === 200 && response.data.status === "success") { const { query, country, countryCode, regionName, region, city, lat, lon, isp, timezone, zip } = response.data; return { ip: query, country, countryCode, regionCode: region, regionName, city, geolocation: [ lat, lon, ], isp, timezone, zip }; } return null; // Return null if geolocation fails } catch (error) { console.error("Error fetching geolocation:", error); return null; } }); } function getClientIp(req) { const headers = req.headers; // Common headers used for forwarding client IP const forwardedHeaders = [ headers["x-forwarded-for"], // Standard header for forwarded IPs headers["x-real-ip"], // Often used by NGINX or other proxies headers["cf-connecting-ip"], // Cloudflare's header for client IP headers["fastly-client-ip"], // Fastly's header for client IP headers["true-client-ip"], // Akamai and other CDNs headers["x-client-ip"], // Less common, fallback headers["x-cluster-client-ip"], // Common with some load balancers headers["forwarded"], // General header, might include "for=" ]; // Extract and parse the first valid IP for (const headerValue of forwardedHeaders) { if (typeof headerValue === "string" && headerValue.trim() !== "") { // x-forwarded-for can contain a list of IPs, split and take the first const ip = headerValue.split(",")[0].trim(); if (isValidIp(ip)) return ip; } } // Fallback to remoteAddress if no headers provided an IP const remoteAddress = req.socket.remoteAddress; return isValidIp(remoteAddress) ? remoteAddress : null; } // Helper function to validate IP format function isValidIp(ip) { if (!ip) return false; const ipv4Regex = /^(?:\d{1,3}\.){3}\d{1,3}$/; // Match IPv4 const ipv6Regex = /^[a-fA-F0-9:]+$/; // Match IPv6 return ipv4Regex.test(ip) || ipv6Regex.test(ip); }