ip-public
Version:
Get your public IPV4 address, also works well in mainland China.
138 lines (122 loc) • 4.24 kB
text/typescript
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];
})
)
);
}