client-ip-geolocation
Version:
Fetch client IP and geolocation from an HTTP request
85 lines (84 loc) • 3.75 kB
JavaScript
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);
}
;