From 5d6b72120fb0b30c1106b5f46fc6e3f9d14cd9b2 Mon Sep 17 00:00:00 2001 From: Felix Wotschofsky Date: Sat, 30 Dec 2023 00:16:27 +0100 Subject: [PATCH] =?UTF-8?q?Load=20WHOIS=20preview=20through=20API=20route?= =?UTF-8?q?=20=F0=9F=91=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/lookup/[domain]/certs/page.tsx | 4 + app/lookup/[domain]/layout.tsx | 11 +-- app/lookup/[domain]/whois-summary/route.ts | 87 ++++++++++++++++++++ components/WhoisQuickInfo.tsx | 95 ++++++---------------- 4 files changed, 118 insertions(+), 79 deletions(-) create mode 100644 app/lookup/[domain]/whois-summary/route.ts diff --git a/app/lookup/[domain]/certs/page.tsx b/app/lookup/[domain]/certs/page.tsx index 7082f8c..1cbf5ba 100644 --- a/app/lookup/[domain]/certs/page.tsx +++ b/app/lookup/[domain]/certs/page.tsx @@ -23,6 +23,10 @@ type CertsData = { serial_number: string; }[]; +export const runtime = 'edge'; +// crt.sh located in GB, always use LHR1 for lowest latency +export const preferredRegion = 'lhr1'; + const lookupCerts = async (domain: string): Promise => { const response = await fetch( 'https://crt.sh?' + diff --git a/app/lookup/[domain]/layout.tsx b/app/lookup/[domain]/layout.tsx index a883753..c6e466a 100644 --- a/app/lookup/[domain]/layout.tsx +++ b/app/lookup/[domain]/layout.tsx @@ -1,14 +1,12 @@ import { ExternalLinkIcon } from 'lucide-react'; import type { Metadata } from 'next'; import { notFound } from 'next/navigation'; -import { type FC, type ReactNode, Suspense } from 'react'; +import { type FC, type ReactNode } from 'react'; import RelatedDomains from '@/components/RelatedDomains'; import ResultsTabs from '@/components/ResultsTabs'; import SearchForm from '@/components/SearchForm'; -import WhoisQuickInfo, { - WhoisQuickInfoPlaceholder, -} from '@/components/WhoisQuickInfo'; +import WhoisQuickInfo from '@/components/WhoisQuickInfo'; import isValidDomain from '@/utils/isValidDomain'; type LookupLayoutProps = { @@ -67,10 +65,7 @@ const LookupLayout: FC = ({ - }> - {/* TODO Add error boundary */} - - + {children} diff --git a/app/lookup/[domain]/whois-summary/route.ts b/app/lookup/[domain]/whois-summary/route.ts new file mode 100644 index 0000000..7399f52 --- /dev/null +++ b/app/lookup/[domain]/whois-summary/route.ts @@ -0,0 +1,87 @@ +import whoiser, { WhoisSearchResult } from 'whoiser'; + +import isValidDomain from '@/utils/isValidDomain'; + +export type WhoisSummaryResponse = { + registrar: string | null; + createdAt: string | null; + dnssec: string | null; +}; + +export type WhoisSummaryErrorResponse = { error: true; message: string }; + +const getSummary = async (domain: string): Promise => { + // TODO Allow resolving for TLDs + if (!isValidDomain(domain)) { + return { + registrar: null, + createdAt: null, + dnssec: null, + }; + } + + try { + const results = await whoiser(domain, { + timeout: 5000, + }); + + const resultsKey = Object.keys(results).find( + // @ts-expect-error + (key) => !('error' in results[key]) + ); + if (!resultsKey) { + throw new Error('No valid results found for domain ' + domain); + } + const firstResult = results[resultsKey] as WhoisSearchResult; + + return { + registrar: firstResult['Registrar']?.toString(), + createdAt: + firstResult && 'Created Date' in firstResult + ? new Date(firstResult['Created Date'].toString()).toLocaleDateString( + 'en-US' + ) + : null, + dnssec: firstResult['DNSSEC']?.toString(), + }; + } catch (error) { + console.error(error); + return { + registrar: null, + createdAt: null, + dnssec: null, + }; + } +}; + +export async function GET( + _request: Request, + { params }: { params: { domain: string } } +) { + if (!params.domain) { + return Response.json( + { error: true, message: 'No domain provided' }, + { + status: 400, + headers: { + 'Content-Type': 'application/json', + }, + } + ); + } + + try { + const summary = await getSummary(params.domain); + return Response.json(summary); + } catch (error) { + return Response.json( + { error: true, message: 'Error fetching whois summary' }, + { + status: 500, + headers: { + 'Content-Type': 'application/json', + }, + } + ); + } +} diff --git a/components/WhoisQuickInfo.tsx b/components/WhoisQuickInfo.tsx index 29c3a1d..17f5117 100644 --- a/components/WhoisQuickInfo.tsx +++ b/components/WhoisQuickInfo.tsx @@ -1,7 +1,9 @@ -import { FC } from 'react'; -import whoiser, { type WhoisSearchResult } from 'whoiser'; +'use client'; -import isValidDomain from '@/utils/isValidDomain'; +import type { FC } from 'react'; +import useSWR from 'swr'; + +import { WhoisSummaryResponse } from '@/app/lookup/[domain]/whois-summary/route'; import { Skeleton } from './ui/skeleton'; @@ -9,88 +11,39 @@ type WhoisQuickInfoProps = { domain: string; }; -const getSummary = async ( - domain: string -): Promise<{ registrar: string; createdAt: string; dnssec: string }> => { - // TODO Allow resolving for TLDs - if (!isValidDomain(domain)) { - return { - registrar: 'Unavailable', - createdAt: 'Unavailable', - dnssec: 'Unavailable', - }; - } - - try { - const results = await whoiser(domain, { - timeout: 5000, - }); - - const resultsKey = Object.keys(results).find( - // @ts-expect-error - (key) => !('error' in results[key]) - ); - if (!resultsKey) { - throw new Error('No valid results found for domain ' + domain); - } - const firstResult = results[resultsKey] as WhoisSearchResult; - - return { - registrar: firstResult['Registrar']?.toString() || 'Unavailable', - createdAt: - firstResult && 'Created Date' in firstResult - ? new Date(firstResult['Created Date'].toString()).toLocaleDateString( - 'en-US' - ) - : 'Unavailable', - dnssec: firstResult['DNSSEC']?.toString() || 'Unavailable', - }; - } catch (error) { - console.error(error); - return { - registrar: 'Unavailable', - createdAt: 'Unavailable', - dnssec: 'Unavailable', - }; - } -}; - -const WhoisQuickInfo: FC = async ({ domain }) => { - const results = await getSummary(domain); +const WhoisQuickInfo: FC = ({ domain }) => { + const { data, isLoading } = useSWR( + `/lookup/${domain}/whois-summary` + ); return (

Registrar

-

{results.registrar}

+ {isLoading ? ( + + ) : ( +

{data?.registrar || 'Unavailable'}

+ )}

Creation Date

-

{results.createdAt}

+ {isLoading ? ( + + ) : ( +

{data?.createdAt || 'Unavailable'}

+ )}

DNSSEC

-

{results.dnssec}

+ {isLoading ? ( + + ) : ( +

{data?.dnssec || 'Unavailable'}

+ )}
); }; export default WhoisQuickInfo; - -export const WhoisQuickInfoPlaceholder: FC = () => ( -
-
-

Registrar

- -
-
-

Creation Date

- -
-
-

DNSSEC

- -
-
-);