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