UNPKG

ip-public

Version:

Get your public IPV4 address, also works well in mainland China.

138 lines (122 loc) 4.24 kB
import {AxiosInstance} from "axios"; import axios from "axios"; import * as os from "os"; const cache: { [baseUrl: string]: AxiosInstance } = {}; function getClient(baseURL: string = "", timeout: number = 17000) { let client: AxiosInstance = cache[baseURL]; if (!client) { client = axios.create({ baseURL, timeout, responseType: "json", headers: { "Content-Type": "application/json", }, }); client.interceptors.request.use(c => c, (error) => { return Promise.reject(error); }); cache[baseURL] = client; } return client; } export function isPrivate(address: string) { return /^(::f{4}:)?10\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})$/i .test(address) || /^(::f{4}:)?192\.168\.([0-9]{1,3})\.([0-9]{1,3})$/i.test(address) || /^(::f{4}:)?172\.(1[6-9]|2\d|30|31)\.([0-9]{1,3})\.([0-9]{1,3})$/i .test(address) || /^(::f{4}:)?127\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})$/i.test(address) || /^(::f{4}:)?169\.254\.([0-9]{1,3})\.([0-9]{1,3})$/i.test(address) || /^f[cd][0-9a-f]{2}:/i.test(address) || /^fe80:/i.test(address) || /^::1$/.test(address) || /^::$/.test(address); } export function isPublic(address: string) { return !isPrivate(address); } export function isLoopBack(address: string) { return /^(::f{4}:)?127\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})/.test(address) || /^fe80::1$/.test(address) || /^::1$/.test(address) || /^::$/.test(address); }; function loopBack(standard: 4 | 6 = 4) { if (standard !== 4 && standard !== 6) { throw new Error(`loopBack error, expect standard to be 4 or 6, got ${standard}`); } return standard === 4 ? '127.0.0.1' : 'fe80::1'; } function forMs(ms: number) { return new Promise((resolve) => { setTimeout(resolve, ms); }); } export function getInternalIP(standard: 4 | 6 = 4): string | null { if (standard !== 4 && standard !== 6) { throw new Error(`getInternalAddr error, expect standard to be 4 or 6, got ${standard}`); } const interfaces = os.networkInterfaces(); const ips = Object.keys(interfaces).map(name => { const addresses = interfaces[name].filter(info => { const familyGot = info.family.toLowerCase(); return familyGot === `ipv${standard}` && !isLoopBack(info.address); }); return (addresses[0] || {} as any).address; }).filter(Boolean); return ips[0] || loopBack(standard); } async function race<T>(ps: Promise<T>[]) { let retIp: T | null = null; let count = 0; for (let i in ps) { ps[i].then(ip => { if (!retIp) { retIp = ip; } count += 1; }).catch(() => { count += 1; }); } while (count < ps.length) { if(retIp) { return retIp; } await forMs(50); } return retIp; } /** * get the public IPV4 address * @param {string[]} hosts - custom server list * @param {number} timeoutMs - timeout in ms, default: 15000ms * @return {Promise<string>} ip address */ export async function getExternalIP(hosts?: string[], timeoutMs: number = 15000): Promise<string> { return await race([ "https://ipinfo.io/ip", "https://ip.cn", "http://icanhazip.com/", "http://ident.me/", "http://icanhazip.com/", "http://tnx.nl/ip", "http://ipecho.net/plain", "http://diagnostic.opendns.com/myip", ...(hosts || []) ].map((urlIp: string) => getClient(urlIp, timeoutMs).get("") .then(rsp => { if (rsp.status !== 200) { throw new Error(`rsp status error, expect 200, got ${rsp.status}`); } const rspData = rsp.data || (rsp as any).text; if (!rspData) { throw new Error(`rsp data error, got empty`); } return rspData.match(/\d+\.\d+\.\d+\.\d+/)[0]; }) ) ); }