at main 3.1 kB view raw
1const ESCAPE_LOOKUP: Record<string, string> = { 2 "&": "&amp;", 3 '"': "&quot;", 4 "'": "&apos;", 5 "<": "&lt;", 6 ">": "&gt;", 7}; 8const PDS = "https://katproto.girlonthemoon.xyz"; 9 10async function getKatprotoUsers() { 11 const users = await fetch(PDS + "/xrpc/com.atproto.sync.listRepos") 12 // type cast because no point validating for smthn like this 13 // real type has more info; not needed here 14 .then((res) => res.json() as Promise<{ repos: { did: string }[] }>) 15 .then((res) => 16 // get display name, handle, and did for each user 17 res.repos.map((repo) => ({ 18 display: fetch( 19 `${PDS}/xrpc/com.atproto.repo.getRecord?repo=${repo.did}&collection=app.bsky.actor.profile&rkey=self` 20 ) 21 .then((res) => res.json()) 22 .then((profile) => profile.value.displayName), 23 // dont validate handles because I'm Lazy + trust myself 24 handle: fetch( 25 repo.did.startsWith("did:plc") 26 ? "https://plc.directory/" + repo.did 27 : `https://${repo.did.replace("did:web:", "")}/.well-known/did.json` 28 ) 29 .then((res) => res.json()) 30 .then((doc) => doc.alsoKnownAs[0].replace("at://", "")), 31 did: repo.did, 32 })) 33 ) 34 .then( 35 async (users) => 36 await Promise.all( 37 users.map( 38 async (x) => 39 `<li><a href="https://bsky.app/profile/${x.did}">${await x.display}</a></li>` 40 ) 41 ) 42 ); 43 44 return users.join(""); 45} 46 47async function checkStatus() { 48 const statusElement = document.getElementById("current-status"); 49 if (!statusElement) return; 50 51 // try get `/xrpc/_health` 52 const result = await fetch(PDS + "/xrpc/_health") 53 .then(async (res) => ({ 54 status: res.status, 55 statusText: res.statusText, 56 res: await res.text(), 57 })) 58 .catch((err) => { 59 // we only return undefined if we get a type error 60 // this means we were blocked by permissions (cors) (upstream is offline) 61 // or the device couldnt connect (main server and index is offline) 62 if (err instanceof TypeError) return undefined; 63 console.warn("Ignoring:", err); 64 // if we didnt expect this error we want to bubble it up as normal 65 throw err; 66 }); 67 68 // make sure html is escaped for status text 69 // this could (in theory) be anything so we should make sure its escaped properly 70 // also an & could break things 71 if (result) { 72 result.statusText = result.statusText.replaceAll( 73 /[&"'<>]/g, 74 (c) => ESCAPE_LOOKUP[c] 75 ); 76 } 77 78 if (!result) { 79 statusElement.innerHTML = "🔴 Offline"; 80 statusElement.title = "The server could not be reached."; 81 return; 82 } 83 84 if (result.status < 200 || result.status > 299) { 85 statusElement.innerHTML = `🟡 Some Issues: ${result.status} ${result.statusText}`; 86 statusElement.title = result.res; 87 } 88 89 statusElement.innerHTML = `🟢 Online`; 90 statusElement.title = result.res; 91} 92 93// silence errors 94getKatprotoUsers().catch((err) => console.warn(err)); 95addEventListener("load", () => checkStatus().catch((err) => console.warn(err)));