const axios = require('axios'); const db = require('./db'); /** * Geo Location Service * Resolves IP addresses to geographical coordinates and country info. * Caches results in the database to minimize API calls. */ const ipInfoToken = process.env.IPINFO_TOKEN; async function getLocation(ip) { // Normalize IP (strip port if present) const cleanIp = ip.split(':')[0]; // Skip local/reserved IPs if (isLocalIp(cleanIp)) { return null; } // Check database first try { const [rows] = await db.query('SELECT * FROM server_locations WHERE ip = ?', [cleanIp]); if (rows.length > 0) { // Check if data is stale (e.g., older than 30 days) const data = rows[0]; const age = Date.now() - new Date(data.last_updated).getTime(); if (age < 30 * 24 * 60 * 60 * 1000) { return data; } } // Resolve via ipinfo.io console.log(`[Geo Service] Resolving location for IP: ${cleanIp}`); const url = `https://ipinfo.io/${cleanIp}/json${ipInfoToken ? `?token=${ipInfoToken}` : ''}`; const response = await axios.get(url, { timeout: 5000 }); const geo = response.data; if (geo && geo.loc) { const [lat, lon] = geo.loc.split(',').map(Number); const locationData = { ip: cleanIp, country: geo.country, country_name: geo.country_name || geo.country, // ipinfo might not have country_name in basic response region: geo.region, city: geo.city, latitude: lat, longitude: lon }; // Save to DB await db.query(` INSERT INTO server_locations (ip, country, country_name, region, city, latitude, longitude) VALUES (?, ?, ?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE country = VALUES(country), country_name = VALUES(country_name), region = VALUES(region), city = VALUES(city), latitude = VALUES(latitude), longitude = VALUES(longitude) `, [ locationData.ip, locationData.country, locationData.country_name, locationData.region, locationData.city, locationData.latitude, locationData.longitude ]); return locationData; } } catch (err) { console.error(`[Geo Service] Error resolving IP ${cleanIp}:`, err.message); } return null; } function isLocalIp(ip) { if (ip === 'localhost' || ip === '127.0.0.1' || ip === '::1') return true; // RFC1918 private addresses const p1 = /^10\./; const p2 = /^172\.(1[6-9]|2[0-9]|3[0-1])\./; const p3 = /^192\.168\./; return p1.test(ip) || p2.test(ip) || p3.test(ip); } module.exports = { getLocation };