···
1
-
async function getKatprotoUsers() {
2
-
const users = fetch("https://katproto.girlonthemoon.xyz/xrpc/com.atproto.sync.listRepos")
3
-
// type cast because no point validating for smthn like this
4
-
// real type has more info; not needed here
5
-
.then((res) => res.json() as Promise<{ repos: { did: string }[] }>)
7
-
// get display name, handle, and did for each user
8
-
res.repos.map((repo) => ({
10
-
`https://katproto.girlonthemoon.xyz/xrpc/com.atproto.repo.getRecord?repo=${repo.did}&collection=app.bsky.actor.profile&rkey=self`
12
-
.then((res) => res.json())
13
-
.then((profile) => profile.value.displayName),
14
-
// dont validate handles because I'm Lazy + trust myself
16
-
repo.did.startsWith("did:plc")
17
-
? "https://plc.directory/" + repo.did
18
-
: `https://${repo.did.replace("did:web:", "")}/.well-known/did.json`
20
-
.then((res) => res.json())
21
-
.then((doc) => doc.alsoKnownAs[0].replace("at://", "")),
25
-
.then(async (users) =>
30
-
`<span class="user-count"><a href="https://bsky.app/profile/${x.did}">${await x.display}</a></span>`
35
-
const fin = Array.from(await users).join("<br>");
1
+
const ESCAPE_LOOKUP: Record<string, string> = {
8
+
const PDS = "https://katproto.girlonthemoon.xyz";
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 }[] }>)
16
+
// get display name, handle, and did for each user
17
+
res.repos.map((repo) => ({
19
+
`${PDS}/xrpc/com.atproto.repo.getRecord?repo=${repo.did}&collection=app.bsky.actor.profile&rkey=self`
21
+
.then((res) => res.json())
22
+
.then((profile) => profile.value.displayName),
23
+
// dont validate handles because I'm Lazy + trust myself
25
+
repo.did.startsWith("did:plc")
26
+
? "https://plc.directory/" + repo.did
27
+
: `https://${repo.did.replace("did:web:", "")}/.well-known/did.json`
29
+
.then((res) => res.json())
30
+
.then((doc) => doc.alsoKnownAs[0].replace("at://", "")),
39
+
`<span class="user-count"><a href="https://bsky.app/profile/${x.did}">${await x.display}</a></span>`
44
+
const fin = Array.from(users).join("<br>");
48
+
async function checkStatus() {
49
+
const statusElement = document.getElementById("current-status");
50
+
if (!statusElement) return;
52
+
// try get `/xrpc/_health`
53
+
const result = await fetch(PDS + "/xrpc/_health")
54
+
.then(async (res) => ({
56
+
statusText: res.statusText,
57
+
res: await res.text(),
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
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
73
+
result.statusText = result.statusText.replaceAll(
75
+
(c) => ESCAPE_LOOKUP[c]
80
+
statusElement.innerHTML = "🔴 Offline";
81
+
statusElement.title = "The server could not be reached.";
85
+
if (result.status < 200 || result.status > 299) {
86
+
statusElement.innerHTML = `🟡 Some Issues: ${result.status} ${result.statusText}`;
87
+
statusElement.title = result.res;
90
+
statusElement.innerHTML = `🟢 Online`;
91
+
statusElement.title = result.res;
95
+
getKatprotoUsers().catch((err) => console.warn(err));
96
+
addEventListener("load", () => checkStatus().catch((err) => console.warn(err)));