From 30ce4042ade8a1e3a6de45738f2e4ba21e040064 Mon Sep 17 00:00:00 2001 From: Felix Wotschofsky Date: Fri, 29 Dec 2023 23:22:02 +0100 Subject: [PATCH] =?UTF-8?q?Move=20ip=20details=20lookup=20to=20edge=20?= =?UTF-8?q?=F0=9F=8C=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/api/lookupIp/route.ts | 76 ++++++++++++++++++++++++++++++++------- 1 file changed, 64 insertions(+), 12 deletions(-) diff --git a/app/api/lookupIp/route.ts b/app/api/lookupIp/route.ts index c8f2320..95de320 100644 --- a/app/api/lookupIp/route.ts +++ b/app/api/lookupIp/route.ts @@ -1,7 +1,7 @@ -import dns from 'dns'; -import { promisify } from 'util'; import isIP from 'validator/lib/isIP'; +export const runtime = 'edge'; + export type IpLookupResponse = { city: string; country: string; @@ -16,6 +16,64 @@ export type IpLookupResponse = { export type IpLookupErrorResponse = { error: true; message: string }; +const getIpDetails = async (ip: string) => { + const response = await fetch(`http://ip-api.com/json/${ip}`); + + if (!response.ok) + throw new Error(`Error fetching IP details: ${response.statusText}`); + + const data = (await response.json()) as Record; + delete data.status; + + return data; +}; + +const ipv4ToDnsName = (ipv4: string) => + ipv4.split('.').reverse().join('.') + '.in-addr.arpa'; + +const ipv6ToDnsName = (ipv6: string) => { + const segments = ipv6.split(':'); + const missingSegments = 8 - segments.length + (ipv6.includes('::') ? 1 : 0); + const expandedSegments = segments.map((segment) => segment.padStart(4, '0')); + for (let i = 0; i < missingSegments; i++) { + expandedSegments.splice(segments.indexOf(''), 0, '0000'); + } + const fullAddress = expandedSegments.join(''); + + return ( + fullAddress + .split('') + .reverse() + .join('.') + .replace(/:/g, '') + .split('.') + .filter((x) => x) + .join('.') + '.ip6.arpa' + ); +}; + +const lookupReverse = async (ip: string): Promise => { + const reverseDnsName = ip.includes(':') + ? ipv6ToDnsName(ip) + : ipv4ToDnsName(ip); + + const response = await fetch( + `https://cloudflare-dns.com/dns-query?name=${reverseDnsName}&type=PTR`, + { + headers: { Accept: 'application/dns-json' }, + } + ); + + if (!response.ok) + throw new Error(`Error fetching DNS records: ${response.statusText}`); + + const data = await response.json(); + + return data.Answer + ? data.Answer.map((record: { data: string }) => record.data) + : []; +}; + export async function GET(request: Request) { const { searchParams } = new URL(request.url); const ip = searchParams.get('ip'); @@ -35,16 +93,10 @@ export async function GET(request: Request) { ); } - const url = `http://ip-api.com/json/${ip}`; - const response = await fetch(url); - const data = (await response.json()) as Record; - - let reverse: string[] = []; - try { - reverse = await promisify(dns.reverse)(ip); - } catch (error) { - console.error(error); - } + const [data, reverse] = await Promise.all([ + getIpDetails(ip), + lookupReverse(ip), + ]); return new Response( JSON.stringify({