mirror of
https://github.com/Xevion/rdap.git
synced 2025-12-07 11:16:06 -06:00
Add await registry-informed getTypes method with smart entity tag validation
fuck type coloring madness
This commit is contained in:
@@ -46,7 +46,7 @@ type LookupInputProps = {
|
||||
onChange?: (target: {
|
||||
target: string;
|
||||
targetType: TargetType | null;
|
||||
}) => void;
|
||||
}) => Promise<void>;
|
||||
detectedType: Maybe<TargetType>;
|
||||
};
|
||||
|
||||
@@ -155,7 +155,7 @@ const LookupInput: FunctionComponent<LookupInputProps> = ({
|
||||
required: true,
|
||||
onChange: () => {
|
||||
if (onChange != undefined)
|
||||
onChange({
|
||||
void onChange({
|
||||
target: getValues("target"),
|
||||
// dropdown target will be pulled from state anyways, so no need to provide it here
|
||||
targetType: retrieveTargetType(null),
|
||||
@@ -172,7 +172,7 @@ const LookupInput: FunctionComponent<LookupInputProps> = ({
|
||||
setSelected(value);
|
||||
|
||||
if (onChange != undefined)
|
||||
onChange({
|
||||
void onChange({
|
||||
target: getValues("target"),
|
||||
// we provide the value as the state will not have updated yet for this context
|
||||
targetType: retrieveTargetType(value),
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useEffect, useMemo, useRef, useState } from "react";
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import { domainMatchPredicate, getBestURL, getType } from "@/rdap";
|
||||
import type {
|
||||
AutonomousNumber,
|
||||
@@ -43,19 +43,19 @@ const useLookup = (warningHandler?: WarningHandler) => {
|
||||
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [target, setTarget] = useState<string>("");
|
||||
const [uriType, setUriType] = useState<Maybe<TargetType>>(Maybe.nothing());
|
||||
|
||||
// Used by a callback on LookupInput to forcibly set the type of the lookup.
|
||||
const [currentType, setTargetType] = useState<TargetType | null>(null);
|
||||
|
||||
// Used to allow repeatable lookups when weird errors happen.
|
||||
const repeatableRef = useRef<string>("");
|
||||
|
||||
const uriType = useMemo<Maybe<TargetType>>(
|
||||
function () {
|
||||
if (currentType != null) return Maybe.just(currentType);
|
||||
return getType(target).mapOr(Maybe.nothing(), (type) => Maybe.just(type));
|
||||
},
|
||||
[target, currentType]
|
||||
);
|
||||
useCallback(async () => {
|
||||
if (currentType != null) return Maybe.just(currentType);
|
||||
const uri: Maybe<TargetType> = (await getTypeEasy(target)).mapOr(Maybe.nothing(), (type) => Maybe.just(type));
|
||||
setUriType(uri);
|
||||
}, [target, currentType])
|
||||
|
||||
// Fetch & load a specific registry's data into memory.
|
||||
async function loadBootstrap(type: RootRegistryType, force = false) {
|
||||
@@ -81,6 +81,20 @@ const useLookup = (warningHandler?: WarningHandler) => {
|
||||
};
|
||||
}
|
||||
|
||||
async function getRegistry(type: RootRegistryType): Promise<Register> {
|
||||
if (registryDataRef.current[type] == null) await loadBootstrap(type);
|
||||
if (registryDataRef.current[type] == null)
|
||||
throw new Error(
|
||||
`Could not load bootstrap data for ${type} registry.`
|
||||
);
|
||||
return registryDataRef.current[type];
|
||||
}
|
||||
|
||||
async function getTypeEasy(target: string): Promise<Result<TargetType, Error>> {
|
||||
return getType(target, getRegistry);
|
||||
}
|
||||
|
||||
|
||||
function getRegistryURL(
|
||||
type: RootRegistryType,
|
||||
lookupTarget: string
|
||||
@@ -232,7 +246,7 @@ const useLookup = (warningHandler?: WarningHandler) => {
|
||||
new Error("A target must be given in order to execute a lookup.")
|
||||
);
|
||||
|
||||
const targetType = getType(target);
|
||||
const targetType = await getTypeEasy(target);
|
||||
|
||||
if (targetType.isErr) {
|
||||
return Result.err(
|
||||
@@ -365,7 +379,7 @@ const useLookup = (warningHandler?: WarningHandler) => {
|
||||
}
|
||||
}
|
||||
|
||||
return { error, setTarget, setTargetType, submit, currentType: uriType };
|
||||
return { error, setTarget, setTargetType, submit, currentType: uriType, getType: getTypeEasy };
|
||||
};
|
||||
|
||||
export default useLookup;
|
||||
|
||||
@@ -9,10 +9,9 @@ import LookupInput from "@/components/form/LookupInput";
|
||||
import ErrorCard from "@/components/common/ErrorCard";
|
||||
import { Maybe } from "true-myth";
|
||||
import type { TargetType } from "@/types";
|
||||
import { getType } from "@/rdap";
|
||||
|
||||
const Index: NextPage = () => {
|
||||
const { error, setTarget, setTargetType, submit } = useLookup();
|
||||
const { error, setTarget, setTargetType, submit, getType } = useLookup();
|
||||
const [detectedType, setDetectedType] = useState<Maybe<TargetType>>(
|
||||
Maybe.nothing()
|
||||
);
|
||||
@@ -46,11 +45,11 @@ const Index: NextPage = () => {
|
||||
<LookupInput
|
||||
isLoading={isLoading}
|
||||
detectedType={detectedType}
|
||||
onChange={({ target, targetType }) => {
|
||||
onChange={async ({ target, targetType }) => {
|
||||
setTarget(target);
|
||||
setTargetType(targetType);
|
||||
|
||||
const detectResult = getType(target);
|
||||
const detectResult = await getType(target);
|
||||
if (detectResult.isOk) {
|
||||
setDetectedType(Maybe.just(detectResult.value));
|
||||
} else {
|
||||
|
||||
70
src/rdap.ts
70
src/rdap.ts
@@ -1,4 +1,4 @@
|
||||
import type { TargetType } from "@/types";
|
||||
import type { Register, RootRegistryType, TargetType } from "@/types";
|
||||
import { Result } from "true-myth";
|
||||
|
||||
// const cardTitles = {
|
||||
@@ -757,19 +757,54 @@ export function createRDAPLink(url, title) {
|
||||
}
|
||||
*/
|
||||
|
||||
const TypeValidators: Record<TargetType, (value: string) => boolean> = {
|
||||
autnum: (value) => /^AS\d+$/.test(value),
|
||||
ip4: (value) => /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\/?\d*$/.test(value),
|
||||
ip6: (value) => /^[0-9a-f:]{2,}\/?\d*$/.test(value),
|
||||
url: (value) => /^https?:/.test(value),
|
||||
json: (value) => /^{/.test(value),
|
||||
tld: (value) => /^\.\w+$/.test(value),
|
||||
domain: (value) =>
|
||||
/[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?/.test(
|
||||
value
|
||||
),
|
||||
entity: (value) => false,
|
||||
registrar: (value) => false,
|
||||
type ValidatorArgs = {
|
||||
value: string;
|
||||
getRegistry: (type: RootRegistryType) => Promise<Register>;
|
||||
};
|
||||
|
||||
const TypeValidators: Record<
|
||||
TargetType,
|
||||
(args: ValidatorArgs) => Promise<boolean>
|
||||
> = {
|
||||
autnum: ({ value }) => Promise.resolve(/^AS\d+$/.test(value)),
|
||||
ip4: ({ value }) =>
|
||||
Promise.resolve(/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\/?\d*$/.test(value)),
|
||||
ip6: ({ value }) => Promise.resolve(/^[0-9a-f:]{2,}\/?\d*$/.test(value)),
|
||||
url: ({ value }) => Promise.resolve(/^https?:/.test(value)),
|
||||
json: ({ value }) => Promise.resolve(/^{/.test(value)),
|
||||
tld: ({ value }) => Promise.resolve(/^\.\w+$/.test(value)),
|
||||
domain: ({ value }) => {
|
||||
return Promise.resolve(
|
||||
/[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?/.test(
|
||||
value
|
||||
)
|
||||
);
|
||||
},
|
||||
entity: async ({ value, getRegistry }) => {
|
||||
// Ensure the entity handle is in the correct format
|
||||
const result = value.match(/^\w+-(\w+)$/);
|
||||
if (result === null || result.length <= 1 || result[1] == undefined) return false;
|
||||
|
||||
// Check if the entity object tag is real
|
||||
try {
|
||||
const registry = await getRegistry("entity");
|
||||
|
||||
// Check each service to see if tag starts with inputted value
|
||||
for (const service of registry.services) {
|
||||
const tags = service[1];
|
||||
console.log({tags, result});
|
||||
if (tags.some((tag) => tag.startsWith(result[1] as string))) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
} catch (e) {
|
||||
console.error(new Error("Failed to fetch entity registry", {cause: e}));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
registrar: ({ }) => Promise.resolve(false),
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -779,9 +814,12 @@ const TypeValidators: Record<TargetType, (value: string) => boolean> = {
|
||||
* @returns A `Result` object containing the determined `TargetType` if a match is found,
|
||||
* otherwise an `Error` object.
|
||||
*/
|
||||
export function getType(value: string): Result<TargetType, Error> {
|
||||
export async function getType(
|
||||
value: string,
|
||||
getRegistry: (type: RootRegistryType) => Promise<Register>
|
||||
): Promise<Result<TargetType, Error>> {
|
||||
for (const [type, validator] of Object.entries(TypeValidators)) {
|
||||
if (validator(value)) return Result.ok(type);
|
||||
if (await validator({ value, getRegistry })) return Result.ok(type);
|
||||
}
|
||||
return Result.err(new Error("No patterns matched the input"));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user