From 20a57caf4f05d593d53c2f538d3d612ef1c5df3a Mon Sep 17 00:00:00 2001 From: afterlifepro Date: Wed, 5 Nov 2025 17:54:33 +0000 Subject: [PATCH] add current status also base urls on absolute (to make more reusable for other people) and apply formatting changes from prettier --- index.html | 2 + katproto_users.ts | 131 +++++++++++++++++++++++++++++++++------------- 2 files changed, 96 insertions(+), 37 deletions(-) diff --git a/index.html b/index.html index e7e4e13..f19922a 100644 --- a/index.html +++ b/index.html @@ -162,6 +162,8 @@

status

+

Current status: Loading...

+

off-site status page in case the PDS goes down; in other words,
this page will remain up even when the PDS is down.

diff --git a/katproto_users.ts b/katproto_users.ts index 2e1f9ca..e3ffeec 100644 --- a/katproto_users.ts +++ b/katproto_users.ts @@ -1,39 +1,96 @@ -async function getKatprotoUsers() { - const users = fetch("https://katproto.girlonthemoon.xyz/xrpc/com.atproto.sync.listRepos") - // type cast because no point validating for smthn like this - // real type has more info; not needed here - .then((res) => res.json() as Promise<{ repos: { did: string }[] }>) - .then((res) => - // get display name, handle, and did for each user - res.repos.map((repo) => ({ - display: fetch( - `https://katproto.girlonthemoon.xyz/xrpc/com.atproto.repo.getRecord?repo=${repo.did}&collection=app.bsky.actor.profile&rkey=self` - ) - .then((res) => res.json()) - .then((profile) => profile.value.displayName), - // dont validate handles because I'm Lazy + trust myself - handle: fetch( - repo.did.startsWith("did:plc") - ? "https://plc.directory/" + repo.did - : `https://${repo.did.replace("did:web:", "")}/.well-known/did.json` - ) - .then((res) => res.json()) - .then((doc) => doc.alsoKnownAs[0].replace("at://", "")), - did: repo.did - })) - ) - .then(async (users) => - ( - await Promise.all( - users.map( - async (x) => - `${await x.display}` - ) - ) - ) - ) - const fin = Array.from(await users).join("
"); - return (await fin); +const ESCAPE_LOOKUP: Record = { + "&": "&", + '"': """, + "'": "'", + "<": "<", + ">": ">", }; +const PDS = "https://katproto.girlonthemoon.xyz"; + +async function getKatprotoUsers() { + const users = await fetch(PDS + "/xrpc/com.atproto.sync.listRepos") + // type cast because no point validating for smthn like this + // real type has more info; not needed here + .then((res) => res.json() as Promise<{ repos: { did: string }[] }>) + .then((res) => + // get display name, handle, and did for each user + res.repos.map((repo) => ({ + display: fetch( + `${PDS}/xrpc/com.atproto.repo.getRecord?repo=${repo.did}&collection=app.bsky.actor.profile&rkey=self` + ) + .then((res) => res.json()) + .then((profile) => profile.value.displayName), + // dont validate handles because I'm Lazy + trust myself + handle: fetch( + repo.did.startsWith("did:plc") + ? "https://plc.directory/" + repo.did + : `https://${repo.did.replace("did:web:", "")}/.well-known/did.json` + ) + .then((res) => res.json()) + .then((doc) => doc.alsoKnownAs[0].replace("at://", "")), + did: repo.did, + })) + ) + .then( + async (users) => + await Promise.all( + users.map( + async (x) => + `${await x.display}` + ) + ) + ); + + const fin = Array.from(users).join("
"); + return await fin; +} + +async function checkStatus() { + const statusElement = document.getElementById("current-status"); + if (!statusElement) return; + + // try get `/xrpc/_health` + const result = await fetch(PDS + "/xrpc/_health") + .then(async (res) => ({ + status: res.status, + statusText: res.statusText, + res: await res.text(), + })) + .catch((err) => { + // we only return undefined if we get a type error + // this means we were blocked by permissions (cors) (upstream is offline) + // or the device couldnt connect (main server and index is offline) + if (err instanceof TypeError) return undefined; + console.warn("Ignoring:", err); + // if we didnt expect this error we want to bubble it up as normal + throw err; + }); + + // make sure html is escaped for status text + // this could (in theory) be anything so we should make sure its escaped properly + // also an & could break things + if (result) { + result.statusText = result.statusText.replaceAll( + /[&"'<>]/g, + (c) => ESCAPE_LOOKUP[c] + ); + } + + if (!result) { + statusElement.innerHTML = "🔴 Offline"; + statusElement.title = "The server could not be reached."; + return; + } + + if (result.status < 200 || result.status > 299) { + statusElement.innerHTML = `🟡 Some Issues: ${result.status} ${result.statusText}`; + statusElement.title = result.res; + } + + statusElement.innerHTML = `🟢 Online`; + statusElement.title = result.res; +} -getKatprotoUsers(); +// silence errors +getKatprotoUsers().catch((err) => console.warn(err)); +addEventListener("load", () => checkStatus().catch((err) => console.warn(err))); -- 2.51.1