1
mirror of https://github.com/jakejarvis/hoot.git synced 2025-10-18 22:34:25 -04:00
Files
hoot/server/services/rdap-parser.ts
Copilot ced8367006 Complete clean refactor of provider detection system with focused rule-based approach (#3)
* Initial plan

* Implement extensible provider detection system with rule engine

Co-authored-by: jakejarvis <1703673+jakejarvis@users.noreply.github.com>

* Add comprehensive documentation and examples for new provider detection system

Co-authored-by: jakejarvis <1703673+jakejarvis@users.noreply.github.com>

* Refactor to clean rule-based provider detection system following @jakejarvis feedback

Co-authored-by: jakejarvis <1703673+jakejarvis@users.noreply.github.com>

* Complete transition to clean rule-based system, remove all legacy code

Co-authored-by: jakejarvis <1703673+jakejarvis@users.noreply.github.com>

* Remove legacy provider detection files and update imports to new detection module

* Refactor provider interface to replace `iconDomain` with `domain` for consistency across components

* Refactor Favicon component usage for improved readability in domain sections

* Remove aliases from provider definitions and update detection functions to return structured provider information

* Add registrar providers and enhance detection functions for improved domain resolution

* Add Vercel and RegistrarSafe to provider lists for enhanced domain management

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: jakejarvis <1703673+jakejarvis@users.noreply.github.com>
Co-authored-by: Jake Jarvis <jake@jarv.is>
2025-09-21 17:12:40 -04:00

117 lines
3.1 KiB
TypeScript

import { resolveRegistrarDomain } from "@/lib/providers/detection";
export type Whois = {
source?: "rdap" | "whois";
registrar: { name: string; domain: string | null };
creationDate: string;
expirationDate: string;
registrant: { organization: string; country: string; state?: string };
status?: string[];
registered: boolean;
};
type RdapEvent = { eventAction?: string; eventDate?: string };
type RdapEntity = { roles?: string[]; vcardArray?: [string, unknown[]] };
type RdapJson = {
registrar?: { name?: string };
entities?: RdapEntity[];
events?: RdapEvent[];
status?: string[];
};
export function parseRdapResponse(json: RdapJson): Whois {
const registrarInfo = extractRegistrarInfo(json);
const registrantInfo = extractRegistrantInfo(json);
const eventDates = extractEventDates(json);
const status = json.status ?? [];
const registered = !status.some((s) => s.toLowerCase() === "available");
return {
source: "rdap",
registrar: registrarInfo,
creationDate: eventDates.creation,
expirationDate: eventDates.expiration,
registrant: registrantInfo,
status,
registered,
};
}
function extractRegistrarInfo(json: RdapJson): {
name: string;
domain: string | null;
} {
const registrarName =
json.registrar?.name ??
findVcardValue(findEntity(json.entities, "registrar"), "fn") ??
"Unknown";
return {
name: registrarName,
domain: resolveRegistrarDomain(registrarName) || null,
};
}
function extractRegistrantInfo(json: RdapJson): {
organization: string;
country: string;
state?: string;
} {
const registrantEnt = findEntity(json.entities, "registrant");
const organization = findVcardValue(registrantEnt, "org") ?? "";
const adr = findVcardEntry(registrantEnt, "adr");
const country =
Array.isArray(adr?.[3]) && typeof adr?.[3]?.[6] === "string"
? (adr?.[3]?.[6] as string)
: "";
const state =
Array.isArray(adr?.[3]) && typeof adr?.[3]?.[4] === "string"
? (adr?.[3]?.[4] as string)
: undefined;
return { organization, country, state };
}
function extractEventDates(json: RdapJson): {
creation: string;
expiration: string;
} {
const creationDate =
json.events?.find((e) => e.eventAction === "registration")?.eventDate ?? "";
const expirationDate =
json.events?.find((e) => e.eventAction === "expiration")?.eventDate ?? "";
return { creation: creationDate, expiration: expirationDate };
}
function findEntity(
entities: RdapEntity[] | undefined,
role: string,
): RdapEntity | undefined {
return entities?.find((e) => e.roles?.includes(role));
}
function findVcardEntry(
entity: RdapEntity | undefined,
key: string,
): unknown[] | undefined {
const arr = entity?.vcardArray?.[1];
if (!Array.isArray(arr)) return undefined;
return arr.find((v) => Array.isArray(v) && v[0] === key) as
| unknown[]
| undefined;
}
function findVcardValue(
entity: RdapEntity | undefined,
key: string,
): string | undefined {
const entry = findVcardEntry(entity, key);
const value = entry?.[3];
return typeof value === "string" ? value : undefined;
}