Fork of atp.tools as a universal profile for people on the ATmosphere
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;