IP属地查询

liuxixi 发布于 1 天前 41 次阅读


AI 摘要

IP地址不只是数字,它是数字世界的"身份证"。这段代码教你如何读取IP背后的地理位置信息,解锁网络数据的地理维度。
const searcher_v4 = flow.get("searcher_v4", "memory")
const searcher_v6 = flow.get("searcher_v6", "memory")
const readerPromise = flow.get("geo_reader_promise", "memory");

// 路由区分:访问首页返回HTML页面;/ipquery 执行接口查询
if (msg.req.url === "/ip" || msg.req.url === "/ip/") {
msg.payload = `
`;
msg.headers = { "Content-Type": "text/html;charset=utf-8" };
return msg;
}

// ========== 下方全部是你原始未改动的IP查询逻辑 ==========
// 内网IP正则 IPv4 + IPv6内网段
const privateIpRules = [
/^127\./,
/^192\.168\./,
/^10\./,
/^172\.(1[6-9]|2\d|3[01])\./,
/^::1$/,
/^fd[0-9a-f]{2}:/,
/^fe80:/
];

try {
// 获取IP参数,无参数自动取客户端真实IP
let targetIp;
if (msg.req.query && msg.req.query.ip) {
targetIp = msg.req.query.ip.trim();
} else {
// 读取代理真实IP
const xf = msg.req.headers['x-forwarded-for'];
if (xf) {
targetIp = xf.split(',')[0].trim();
} else {
targetIp = msg.req.ip || msg.req.connection.remoteAddress;
}
}

// 清理IPv6映射前缀 ::ffff:
targetIp = targetIp.replace("::ffff:", "");

// 判空兜底
if (!targetIp) {
msg.payload = {
code: 400,
msg: "无法获取IP地址",
data: null
};
msg.statusCode = 400;
return msg;
}

// IP格式正则
const ipv4Reg = /^(?:\d{1,3}\.){3}\d{1,3}$/;
const ipv6Reg = /^[0-9a-fA-F:]+$/;

// 拦截内网地址
for (const rule of privateIpRules) {
if (rule.test(targetIp)) {
msg.payload = {
code: 400,
msg: "内网IP无法查询属地",
data: null
};
msg.statusCode = 400;
return msg;
}
}

let searcher;
if (ipv4Reg.test(targetIp)) {
searcher = searcher_v4;
} else if (ipv6Reg.test(targetIp)) {
searcher = searcher_v6;
} else {
msg.payload = {
code: 400,
msg: "IP地址格式非法",
data: null
};
msg.statusCode = 400;
return msg;
}

// 查询
const resultStr = await searcher.search(targetIp);
// 兜底防止undefined.split崩溃
const str = resultStr || "";
const [country, area, province, city, isp] = str.split("|");
msg.resultStr = resultStr
// ============ GeoLite2 查询经纬度 ============
let latitude = "";
let longitude = "";
let timeZone = ""
if (readerPromise) {
try {
// 关键:await promise,不管是否打开完成,自动等待
const geoReader = await readerPromise;
const geoInfo = await geoReader.city(targetIp);
if (geoInfo.location) {
latitude = geoInfo.location.latitude;
longitude = geoInfo.location.longitude;
timeZone = geoInfo.location.timeZone
}
msg.geoInfo = geoInfo
} catch (geoErr) {
node.debug("地理位置查询异常:" + geoErr.message);
}
}
msg.payload = {
code: 200,
msg: "success",
data: {
ip: targetIp,
country,
area,
province,
city,
isp,
latitude,
longitude,
timeZone
}
};
msg.statusCode = 200;

} catch (err) {
node.warn(err);
msg.payload = {
code: 500,
msg: "查询失败",
error: err.message
};
msg.statusCode = 500;
}
return msg;

  • reward_image1
  • reward_image2
奔赴月色山海,自愈且自由
最后更新于 2026-08-01