Feat: Suggest IP or Hostname (#1739)

* get ip and hostnames

* use heroicons

* add host field

* get private info

* unstyled prototype

* styled select

* add to setup

* fix types
This commit is contained in:
Bernd Storath
2025-03-14 10:33:02 +01:00
committed by GitHub
parent 86bdbe4c3d
commit 198b240755
39 changed files with 450 additions and 302 deletions
+140 -1
View File
@@ -1,5 +1,7 @@
import type { parseCidr } from 'cidr-tools';
import { Resolver } from 'node:dns/promises';
import { networkInterfaces } from 'node:os';
import { stringifyIp } from 'ip-bigint';
import type { parseCidr } from 'cidr-tools';
import type { ClientNextIpType } from '#db/repositories/client/types';
@@ -31,3 +33,140 @@ export function nextIP(
return address;
}
// use opendns to get public ip
const dnsServers = {
ip4: ['208.67.222.222'],
ip6: ['2620:119:35::35'],
ip: 'myip.opendns.com',
};
async function getPublicInformation() {
const ipv4 = await getPublicIpv4();
const ipv6 = await getPublicIpv6();
const ptr4 = ipv4 ? await getReverseDns(ipv4) : [];
const ptr6 = ipv6 ? await getReverseDns(ipv6) : [];
const hostnames = [...new Set([...ptr4, ...ptr6])];
return { ipv4, ipv6, hostnames };
}
async function getPublicIpv4() {
try {
const resolver = new Resolver();
resolver.setServers(dnsServers.ip4);
const ipv4 = await resolver.resolve4(dnsServers.ip);
return ipv4[0];
} catch {
return null;
}
}
async function getPublicIpv6() {
try {
const resolver = new Resolver();
resolver.setServers(dnsServers.ip6);
const ipv6 = await resolver.resolve6(dnsServers.ip);
return ipv6[0];
} catch {
return null;
}
}
async function getReverseDns(ip: string) {
try {
const resolver = new Resolver();
resolver.setServers([...dnsServers.ip4, ...dnsServers.ip6]);
const ptr = await resolver.reverse(ip);
return ptr;
} catch {
return [];
}
}
function getPrivateInformation() {
const interfaces = networkInterfaces();
const interfaceNames = Object.keys(interfaces);
const obj: Record<string, { ipv4: string[]; ipv6: string[] }> = {};
for (const name of interfaceNames) {
if (name === 'wg0') {
continue;
}
const iface = interfaces[name];
if (!iface) continue;
for (const { family, internal, address } of iface) {
if (internal) {
continue;
}
if (!obj[name]) {
obj[name] = {
ipv4: [],
ipv6: [],
};
}
if (family === 'IPv4') {
obj[name].ipv4.push(address);
} else if (family === 'IPv6') {
obj[name].ipv6.push(address);
}
}
}
return obj;
}
async function getIpInformation() {
const results = [];
const publicInfo = await getPublicInformation();
if (publicInfo.ipv4) {
results.push({
value: publicInfo.ipv4,
label: 'IPv4 - Public',
});
}
if (publicInfo.ipv6) {
results.push({
value: `[${publicInfo.ipv6}]`,
label: 'IPv6 - Public',
});
}
for (const hostname of publicInfo.hostnames) {
results.push({
value: hostname,
label: 'Hostname - Public',
});
}
const privateInfo = getPrivateInformation();
for (const [name, { ipv4, ipv6 }] of Object.entries(privateInfo)) {
for (const ip of ipv4) {
results.push({
value: ip,
label: `IPv4 - ${name}`,
});
}
for (const ip of ipv6) {
results.push({
value: `[${ip}]`,
label: `IPv6 - ${name}`,
});
}
}
return results;
}
/**
* Fetch IP Information
* @cache Response is cached for 15 min
*/
export const cachedGetIpInformation = cacheFunction(getIpInformation, {
expiry: 15 * 60 * 1000,
});