hogwarts.html
edited
1<!DOCTYPE html>
2<html lang="en">
3<head>
4 <meta charset="UTF-8">
5 <meta name="viewport" content="width=device-width, initial-scale=1.0">
6 <title>Hogwarts PDS</title>
7 <style>
8 body {
9 display: flex;
10 flex-direction: column;
11 align-items: center;
12 padding: 2rem;
13 }
14 ul {
15 list-style: none;
16 padding: 0;
17 }
18 li {
19 text-align: center;
20 margin: 0.5rem 0;
21 }
22 </style>
23</head>
24<body>
25 <h1>The Hogwarts PDS</h1>
26 <ul id="users"></ul>
27
28 <script>
29 const PDS_URL = "https://pds.hogwarts.dev";
30
31 async function fetchAllRepos() {
32 const dids = [];
33 let cursor;
34
35 do {
36 const url = new URL(`${PDS_URL}/xrpc/com.atproto.sync.listRepos`);
37 if (cursor) url.searchParams.set("cursor", cursor);
38
39 const res = await fetch(url);
40 const data = await res.json();
41
42 dids.push(...data.repos.map(r => r.did));
43 cursor = data.cursor;
44 } while (cursor);
45
46 return dids;
47 }
48
49 async function fetchProfile(did) {
50 try {
51 const url = `${PDS_URL}/xrpc/com.atproto.repo.describeRepo?repo=${did}`;
52 const res = await fetch(url);
53 const data = await res.json();
54 return { did, handle: data.handle || did };
55 } catch {
56 return { did, handle: did };
57 }
58 }
59
60 async function loadUsers() {
61 const usersEl = document.getElementById('users');
62
63 try {
64 const dids = await fetchAllRepos();
65
66 if (dids.length === 0) {
67 usersEl.innerHTML = '<li>No users found</li>';
68 } else {
69 const profiles = await Promise.all(dids.map(fetchProfile));
70
71 profiles.forEach(profile => {
72 const li = document.createElement('li');
73 li.innerHTML = `<a href="https://bsky.app/profile/${profile.handle}" target="_blank">@${profile.handle}</a>`;
74 usersEl.appendChild(li);
75 });
76 }
77 } catch (error) {
78 usersEl.innerHTML = '<li>Error loading users</li>';
79 console.error(error);
80 }
81 }
82
83 loadUsers();
84 </script>
85</body>
86</html>