diff --git a/src/components/Domain.tsx b/src/components/DomainCard.tsx similarity index 59% rename from src/components/Domain.tsx rename to src/components/DomainCard.tsx index 8fc177e..0180e5b 100644 --- a/src/components/Domain.tsx +++ b/src/components/DomainCard.tsx @@ -1,13 +1,14 @@ import type {FunctionComponent, ReactNode} from "react"; import React, {Fragment} from "react"; -import type {DomainType} from "@/components/DomainType"; import {rdapStatusInfo} from "@/constants"; +import type {Domain} from "@/responses"; +import Events from "@/components/Events" export type DomainProps = { - data: DomainType; + data: Domain; }; -const Domain: FunctionComponent = ({data}: DomainProps) => { +const DomainCard: FunctionComponent = ({data}: DomainProps) => { const properties: [string | ReactNode, string | ReactNode][] = []; if (data.unicodeName) { @@ -17,18 +18,20 @@ const Domain: FunctionComponent = ({data}: DomainProps) => { properties.push(["Name", data.ldhName]) } - if (data.handle) properties.push(["Handle", data.handle]); - // if (data.events) properties.push - if (data.status) properties.push([ - "Status", - data.status.map((statusKey, index) => - - {statusKey} - ) - ]) + properties.push(["Handle", data.handle]); + properties.push(["Events", ]) + properties.push([ + "Status", +
    + {data.status.map((statusKey, index) => +
  • + {statusKey} +
  • )} +
+ ]) return
-
{data.name} ({data.handle})
+
{data.ldhName ?? data.unicodeName} ({data.handle})
{ @@ -44,4 +47,4 @@ const Domain: FunctionComponent = ({data}: DomainProps) => {
} -export default Domain; \ No newline at end of file +export default DomainCard; \ No newline at end of file diff --git a/src/components/DomainType.tsx b/src/components/DomainType.tsx deleted file mode 100644 index c6e61e5..0000000 --- a/src/components/DomainType.tsx +++ /dev/null @@ -1,61 +0,0 @@ -import type {FunctionComponent} from "react"; -import Domain from "@/components/Domain"; - -export type Link = { - value: string; - rel: string; - href: string; - type: string -} -export type ObjectTypes = 'domain' | 'nameserver' | 'entity' | 'autnum' | 'ip network'; - -export type DomainType = { - objectClassName: 'domain'; - handle: string; - unicodeName: string; - ldhName: string; - links: Link[]; - nameservers: NameserverType[]; - entities: EntityType[]; - status: string[] -} - -export type NameserverType = { - objectClassName: 'nameserver'; -}; -export type EntityType = { - objectClassName: 'entity'; -}; -export type AutnumType = { - objectClassName: 'autnum'; -}; -export type IpNetworkType = { - objectClassName: 'ip network'; -}; - -export type ObjectProps = { - data: DomainType | NameserverType | EntityType | AutnumType | IpNetworkType; -}; - -const GenericObject: FunctionComponent = ({data}: ObjectProps) => { - switch (data.objectClassName) { - case "domain": - return - case "autnum": - case "entity": - case "ip network": - case "nameserver": - default: - return
-
Not implemented
-
- } - - // const title: string = (data.unicodeName ?? data.ldhName ?? data.handle)?.toUpperCase() ?? "Response"; - // return
- //
{title}
- // {objectFragment} - //
-} - -export default GenericObject; \ No newline at end of file diff --git a/src/components/Events.tsx b/src/components/Events.tsx index 983e9ed..aac1e22 100644 --- a/src/components/Events.tsx +++ b/src/components/Events.tsx @@ -1,9 +1,26 @@ import type {FunctionComponent} from "react"; +import type {Event} from "@/responses"; +import {Fragment} from "react"; -export type Event = { - eventAction: string; - eventDate: string; +export type EventsProps = { + data: Event[]; +} +const Events: FunctionComponent = ({data}) => { + return
+ {data.map(({eventAction, eventDate, eventActor}, index) => { + return +
+ {eventAction}: +
+
+ {eventDate.toString()} + + {eventActor != null ? `(by ${eventActor})` : null} +
+
+ })} +
} -const Events: FunctionComponent = () => { -} \ No newline at end of file +export default Events; \ No newline at end of file diff --git a/src/components/Generic.tsx b/src/components/Generic.tsx new file mode 100644 index 0000000..4994acc --- /dev/null +++ b/src/components/Generic.tsx @@ -0,0 +1,31 @@ +import type {FunctionComponent} from "react"; +import DomainCard from "@/components/DomainCard"; +import type {Domain, AutonomousNumber, Entity, Nameserver, IpNetwork} from "@/responses"; + +export type ParsedGeneric = Domain | Nameserver | Entity | AutonomousNumber | IpNetwork; +export type ObjectProps = { + data: ParsedGeneric; +}; + +const Generic: FunctionComponent = ({data}: ObjectProps) => { + switch (data.objectClassName) { + case "domain": + return + case "autnum": + case "entity": + case "ip network": + case "nameserver": + default: + return
+
Not implemented
+
+ } + + // const title: string = (data.unicodeName ?? data.ldhName ?? data.handle)?.toUpperCase() ?? "Response"; + // return
+ //
{title}
+ // {objectFragment} + //
+} + +export default Generic; \ No newline at end of file diff --git a/src/pages/index.tsx b/src/pages/index.tsx index 9a16fc9..9f658a0 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -1,44 +1,51 @@ import {type NextPage} from "next"; import Head from "next/head"; -import type {ObjectType} from "../types"; -import {placeholders} from "../constants"; -import {asnMatch, domainMatch, entityMatch, getBestURL, getRDAPURL, getType, ipMatch, showSpinner} from "../rdap"; +import type {ObjectType} from "@/types"; +import {placeholders, registryURLs} from "@/constants"; +import {domainMatch, getBestURL, getType} from "@/rdap"; +import type {FormEvent} from "react"; import {useEffect, useState} from "react"; -import {truthy} from "../helpers"; -import {registryURLs} from "../constants"; -import axios, {AxiosResponse} from "axios"; -import update from "immutability-helper"; -import GenericObject, {Link} from "../components/DomainType"; +import {truthy} from "@/helpers"; +import axios from "axios"; +import type {ParsedGeneric} from "@/components/Generic"; +import Generic from "@/components/Generic"; +import type {ZodSchema} from "zod"; +import {DomainSchema} from "@/responses"; const Index: NextPage = () => { const [uriType, setUriType] = useState('domain'); - - const [requestJSContact, setRequestJSContact] = useState(false); const [followReferral, setFollowReferral] = useState(false); const [object, setObject] = useState(""); const [loading, setLoading] = useState(false); - const [response, setResponse] = useState(null); + const [response, setResponse] = useState(null); const [error, setError] = useState(null); - const [registryData, setRegistryData] = useState>({}); + const [registryData, setRegistryData] = useState>({}); // Change the selected type automatically useEffect(function () { - const new_type = getType(object); - if (new_type != null && new_type != uriType) - setUriType(new_type) + const newType = getType(object); + if (newType != null && newType != uriType) + setUriType(newType) }, [object]); async function loadRegistryData() { setLoading(true); + + console.log('Retrieving registry ..') + let registersLoaded = 0; + const totalRegisters = Object.keys(registryURLs).length; const responses = await Promise.all(Object.entries(registryURLs).map(async ([url, registryType]) => { const response = await axios.get(url); + registersLoaded++; + console.log(`Registered loaded ${registersLoaded}/${totalRegisters}`) return { registryType, response: response.data }; })) + console.log('Registry data set.') setRegistryData(() => { return Object.fromEntries( responses.map(({registryType, response}) => [registryType, response.services]) @@ -93,40 +100,44 @@ const Index: NextPage = () => { } - function submit(e) { + async function submit(e?: FormEvent) { e?.preventDefault(); + console.log(`Submit invoked. ${uriType}/${JSON.stringify(object)}`) const queryParams = requestJSContact ? '?jscard=1' : ''; - - const url = (function () { + const [url, schema]: [string, ZodSchema] | [null, null] = (function () { switch (uriType) { - case 'url': - return object; - case 'tld': - return `https://root.rdap.org/domain/${object}${queryParams}`; - case 'registrar': - return `https://registrars.rdap.org/entity/${object}-IANA${queryParams}`; - case 'json': - return `json://${object}` + // case 'url': + // return [object]; + // case 'tld': + // return `https://root.rdap.org/domain/${object}${queryParams}`; + // case 'registrar': + // return `https://registrars.rdap.org/entity/${object}-IANA${queryParams}`; + // case 'json': + // return `json://${object}` case 'domain': const temp = getRDAPURL(object); - if (temp) return `${temp}${queryParams}` - return null; + if (temp) return [`${temp}${queryParams}`, DomainSchema] + return [null, null]; default: setError(`No RDAP URL available for ${uriType} ${object}.`); - return null; + return [null, null]; } })() - if (url) sendQuery(url, followReferral); + console.log(`URL: ${url ?? "null"}`) + if (url != null) + await sendQuery(url, schema, followReferral); } - async function sendQuery(url: string, followReferral = false) { + async function sendQuery(url: string, schema: ZodSchema, followReferral = false) { setLoading(true); + let data: ParsedGeneric | null = null; if (url.startsWith('json://')) { + console.log('Mock JSON query detected.') // run the callback with a mock XHR - await handleResponse(JSON.parse(url.substring(7))) + data = schema.parse(JSON.parse(url.substring(7))) } else { try { const response = await axios.get(url, {responseType: "json"}) @@ -134,19 +145,18 @@ const Index: NextPage = () => { setError('This object does not exist.'); else if (response.status != 200) setError(`Error ${response.status}: ${response.statusText}`) - await handleResponse(response, followReferral) + data = schema.parse(response.data); } catch (e) { + console.log(e); setLoading(false); - setError(e.toString()) + if (e instanceof Error) + setError(e.toString()) + return; } } - } - // callback executed when a response is received - async function handleResponse(data: { links: Link[] }, followReferral = false) { - setLoading(false); - - if (followReferral && data.links != null) { + if (followReferral && data?.links != null) { + console.log('Using followReferral.') for (const link of data.links) { if ('related' == link.rel && 'application/rdap+json' == link.type && link.href.match(/^(https?:|)\/\//i)) { await sendQuery(link.href, false) @@ -155,10 +165,11 @@ const Index: NextPage = () => { } } + setLoading(false); + console.log(data); try { - // div.appendChild(processObject(xhr.response, true)); setResponse(data); - const url = `${window.location.href}?type=${encodeURIComponent(uriType)}&object=${object}&request-jscontact=${requestJSContact ? 1 : 0}&follow-referral=${followReferral ? 1 : 0}` + const url = `${window.location.origin}?type=${encodeURIComponent(uriType)}&object=${object}&request-jscontact=${requestJSContact ? 1 : 0}&follow-referral=${followReferral ? 1 : 0}` window.history.pushState(null, document.title, url); } catch (e) { @@ -185,15 +196,9 @@ const Index: NextPage = () => { submit(null); } - loadRegistryData().catch(console.error); }, []) - useEffect(() => { - if (!loading && registryData.domain != undefined) - console.log(registryData); - }, [loading]) - return ( <> @@ -241,7 +246,7 @@ const Index: NextPage = () => {
-
+
@@ -296,7 +301,7 @@ const Index: NextPage = () => {
- {response != null ? : null} + {response != null ? : null}

This page implements a completely private lookup tool for domain names, IP addresses and