Fork of atp.tools as a universal profile for people on the ATmosphere
at main 116 lines 3.0 kB view raw
1import { Check, X } from "lucide-react"; 2import { useState, useEffect } from "react"; 3 4const LexiconResolver = ({ 5 lexiconId, 6 did, 7}: { 8 lexiconId: string; 9 did: string; 10}) => { 11 const [resolved, setResolved] = useState(""); 12 const [isValid, setIsValid] = useState<boolean | null>(null); 13 const [isLoading, setIsLoading] = useState(false); 14 const [error, setError] = useState<string | null>(null); 15 16 const resolveLexicon = (id: string) => { 17 if (!id) return ""; 18 19 const parts = id.split("."); 20 const domainParts = []; 21 let i = 0; 22 while (i < parts.length) { 23 domainParts.push(parts[i]); 24 i++; 25 } 26 const reversedDomain = domainParts.reverse(); 27 const pathParts = parts.slice(i + 1); 28 29 return [ 30 "_lexicon", 31 ...reversedDomain.slice(1, Infinity), 32 ...pathParts, 33 ].join("."); 34 }; 35 36 const verifyDohRecord = async (dohString: string) => { 37 try { 38 setIsLoading(true); 39 setError(null); 40 41 // Using Google's DNS-over-HTTPS API 42 const response = await fetch( 43 `https://dns.google/resolve?name=${dohString}&type=TXT`, 44 ); 45 46 if (!response.ok) { 47 throw new Error("DNS lookup failed"); 48 } 49 50 const data = await response.json(); 51 52 // Check if we got any TXT records 53 if (data.Answer && data.Answer.length > 0) { 54 // TXT record exists 55 setIsValid(false); 56 data.Answer.forEach((record: { data: string }) => { 57 if (record.data === `did=${did}`) setIsValid(true); 58 }); 59 return true; 60 } else { 61 setIsValid(false); 62 return false; 63 } 64 } catch (err) { 65 if (err instanceof Error) { 66 setError(err.message); 67 } else { 68 setError("An unknown error occurred"); 69 } 70 setIsValid(false); 71 return false; 72 } finally { 73 setIsLoading(false); 74 } 75 }; 76 77 useEffect(() => { 78 if (lexiconId) { 79 const dohString = resolveLexicon(lexiconId); 80 setResolved(dohString); 81 verifyDohRecord(dohString); 82 } 83 }, [lexiconId]); 84 85 return ( 86 <div> 87 {isLoading && <p>Verifying...</p>} 88 {isValid !== null && !isLoading && ( 89 <p> 90 Lexicon status:{" "} 91 {isValid ? ( 92 <> 93 <Check className="inline text-green-500 h-6 pb-1" /> Verified 94 </> 95 ) : ( 96 <> 97 <X className="inline text-red-500 h-6 pb-1" /> Not verified 98 </> 99 )}{" "} 100 <span className="inline text-muted-foreground border-r pl-1 mr-2" />{" "} 101 <a 102 href={`https://dns.google/resolve?name=${resolved}`} 103 target="_blank" 104 className="text-blue-700 dark:text-blue-400 hover:underline" 105 > 106 {resolved} 107 </a> 108 </p> 109 )}{" "} 110 {isLoading && <p>Verifying lexicon</p>} 111 {error && <p style={{ color: "red" }}>Error: {error}</p>} 112 </div> 113 ); 114}; 115 116export default LexiconResolver;