diff --git a/src/utils/api.ts b/src/utils/api.ts index 4e7eec4..8f20319 100644 --- a/src/utils/api.ts +++ b/src/utils/api.ts @@ -215,6 +215,32 @@ const getRecordBacklinks = ( ): Promise => getConstellation("/links", target, collection, path, cursor, limit || 100); +export interface HandleResolveResult { + success: boolean; + did?: string; + error?: string; +} + +export const resolveHandleDetailed = async (handle: Handle) => { + const dnsResolver = new DohJsonHandleResolver({ dohUrl: "https://dns.google/resolve?" }); + const httpResolver = new WellKnownHandleResolver(); + + const tryResolve = async ( + resolver: DohJsonHandleResolver | WellKnownHandleResolver, + ): Promise => { + try { + const did = await resolver.resolve(handle); + return { success: true, did }; + } catch (err: any) { + return { success: false, error: err.message ?? String(err) }; + } + }; + + const [dns, http] = await Promise.all([tryResolve(dnsResolver), tryResolve(httpResolver)]); + + return { dns, http }; +}; + export { didDocCache, getAllBacklinks, diff --git a/src/views/repo.tsx b/src/views/repo.tsx index fa2628e..60ad7b1 100644 --- a/src/views/repo.tsx +++ b/src/views/repo.tsx @@ -23,17 +23,19 @@ import { NavMenu, } from "../components/dropdown.jsx"; import { Favicon } from "../components/favicon.jsx"; +import { Modal } from "../components/modal.jsx"; import { addNotification, removeNotification, updateNotification, } from "../components/notification.jsx"; -import Tooltip from "../components/tooltip.jsx"; import { isTouchDevice } from "../layout.jsx"; import { didDocCache, + type HandleResolveResult, labelerCache, resolveHandle, + resolveHandleDetailed, resolveLexiconAuthority, resolvePDS, validateHandle, @@ -54,6 +56,11 @@ export const RepoView = () => { const [filter, setFilter] = createSignal(); const [validHandles, setValidHandles] = createStore>({}); const [rotationKeys, setRotationKeys] = createSignal>([]); + const [handleModalAlias, setHandleModalAlias] = createSignal(null); + const [handleDetailedResult, setHandleDetailedResult] = createSignal<{ + dns: HandleResolveResult; + http: HandleResolveResult; + } | null>(null); let rpc: Client; let pds: string; const did = params.repo!; @@ -502,17 +509,20 @@ export const RepoView = () => {
{alias} - { + setHandleDetailedResult(null); + setHandleModalAlias(alias); + const handle = alias.replace("at://", "") as Handle; + const result = await resolveHandleDetailed(handle); + if (handleModalAlias() === alias) + setHandleDetailedResult(result); + }} > { validHandles[alias] === undefined, }} > - +
)} @@ -528,6 +538,108 @@ export const RepoView = () => { + {/* Handle Verification Modal */} + setHandleModalAlias(null)} + contentClass="dark:bg-dark-300 dark:shadow-dark-700 pointer-events-auto w-max max-w-[90vw] rounded-lg border-[0.5px] border-neutral-300 bg-white p-4 shadow-md sm:max-w-xl dark:border-neutral-700" + > +
+

+ {handleModalAlias()?.replace("at://", "")} +

+ +
+
+ + + Resolving handle... +
+ } + > + {(result) => { + const expectedDid = didDocument().id; + const dnsOk = () => + result().dns.success && result().dns.did === expectedDid; + const httpOk = () => + result().http.success && result().http.did === expectedDid; + const dnsMismatch = () => + result().dns.success && result().dns.did !== expectedDid; + const httpMismatch = () => + result().http.success && result().http.did !== expectedDid; + + return ( +
+ {/* DNS Result */} + + DNS (TXT record) + +
+ + {result().dns.error} +
+ } + > +
{result().dns.did}
+ +
+ Expected: {expectedDid} +
+
+ +
+ + {/* HTTP Result */} + + HTTP (.well-known) + +
+ + {result().http.error} +
+ } + > +
{result().http.did}
+ +
+ Expected: {expectedDid} +
+
+ + + + ); + }} + + +
+ {/* Services Section */}