The only PDS hosted on a jailbroken chromebook
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>Create Account - pds.madebydanny.uk</title>
7 <style>
8 * {
9 box-sizing: border-box;
10 margin: 0;
11 padding: 0;
12 }
13
14 body {
15 background-color: #0d0d0d;
16 color: #f0f0f0;
17 font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
18 padding: 2rem;
19 min-height: 100vh;
20 display: flex;
21 align-items: center;
22 justify-content: center;
23 }
24
25 .container {
26 max-width: 500px;
27 width: 100%;
28 background: #1a1a1a;
29 border-radius: 12px;
30 padding: 2rem;
31 box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3);
32 }
33
34 h1 {
35 color: #4eaaff;
36 margin-bottom: 0.5rem;
37 font-size: 1.75rem;
38 }
39
40 .subtitle {
41 color: #999;
42 margin-bottom: 2rem;
43 font-size: 0.9rem;
44 }
45
46 .form-group {
47 margin-bottom: 1.5rem;
48 }
49
50 label {
51 display: block;
52 margin-bottom: 0.5rem;
53 color: #ccc;
54 font-size: 0.9rem;
55 font-weight: 500;
56 }
57
58 input[type="text"],
59 input[type="email"],
60 input[type="password"],
61 select {
62 width: 100%;
63 padding: 0.75rem;
64 background: #0d0d0d;
65 border: 1px solid #333;
66 border-radius: 6px;
67 color: #f0f0f0;
68 font-size: 1rem;
69 transition: border-color 0.2s;
70 }
71
72 input[type="text"]:focus,
73 input[type="email"]:focus,
74 input[type="password"]:focus,
75 select:focus {
76 outline: none;
77 border-color: #4eaaff;
78 }
79
80 .handle-input-wrapper {
81 display: flex;
82 flex-direction: column;
83 gap: 0.5rem;
84 }
85
86 .helper-text {
87 font-size: 0.8rem;
88 color: #666;
89 margin-top: 0.25rem;
90 }
91
92 button {
93 width: 100%;
94 padding: 0.875rem;
95 background: #4eaaff;
96 color: #fff;
97 border: none;
98 border-radius: 6px;
99 font-size: 1rem;
100 font-weight: 600;
101 cursor: pointer;
102 transition: background 0.2s;
103 }
104
105 button:hover {
106 background: #3d99ee;
107 }
108
109 button:disabled {
110 background: #333;
111 cursor: not-allowed;
112 }
113
114 .error {
115 background: #ff4444;
116 color: white;
117 padding: 0.75rem;
118 border-radius: 6px;
119 margin-bottom: 1rem;
120 display: none;
121 }
122
123 .success {
124 background: #44ff44;
125 color: #000;
126 padding: 0.75rem;
127 border-radius: 6px;
128 margin-bottom: 1rem;
129 display: none;
130 }
131
132 .back-link {
133 text-align: center;
134 margin-top: 1.5rem;
135 }
136
137 .back-link a {
138 color: #4eaaff;
139 text-decoration: none;
140 font-size: 0.9rem;
141 }
142
143 .back-link a:hover {
144 text-decoration: underline;
145 }
146
147 .loading {
148 display: none;
149 text-align: center;
150 color: #4eaaff;
151 margin-top: 1rem;
152 }
153 </style>
154</head>
155<body>
156 <div class="container">
157 <h1>Create Account</h1>
158 <p class="subtitle">Join pds.madebydanny.uk on the AT Protocol network</p>
159
160 <div class="error" id="error"></div>
161 <div class="success" id="success"></div>
162
163 <form id="createAccountForm">
164 <div class="form-group">
165 <label for="handle">Handle *</label>
166 <div class="handle-input-wrapper">
167 <input
168 type="text"
169 id="handle"
170 name="handle"
171 required
172 placeholder="yourname"
173 pattern="[a-zA-Z0-9-]+"
174 autocomplete="off"
175 >
176 <select id="domain" name="domain" required>
177 <option value=".pds.madebydanny.uk">.pds.madebydanny.uk</option>
178 <option value=".pds.danielmorrisey.com">.pds.danielmorrisey.com</option>
179 <option value=".good-example.com">.good-example.com</option>
180 <option value=".mbdio.uk">.mbdio.uk</option>
181 <option value=".certifiedshitposter.com">.certifiedshitposter.com</option>
182 </select>
183 </div>
184 <div class="helper-text">Letters, numbers, and hyphens only</div>
185 </div>
186
187 <div class="form-group">
188 <label for="email">Email *</label>
189 <input
190 type="email"
191 id="email"
192 name="email"
193 required
194 placeholder="you@example.com"
195 autocomplete="email"
196 >
197 </div>
198
199 <div class="form-group">
200 <label for="password">Password *</label>
201 <input
202 type="password"
203 id="password"
204 name="password"
205 required
206 minlength="8"
207 placeholder="At least 8 characters"
208 autocomplete="new-password"
209 >
210 </div>
211
212 <button type="submit" id="submitBtn">Create Account</button>
213 <div class="loading" id="loading">Creating your account...</div>
214 </form>
215
216 <div class="back-link">
217 <a href="/">← Back to home</a>
218 </div>
219 </div>
220
221 <script>
222 const form = document.getElementById('createAccountForm');
223 const errorDiv = document.getElementById('error');
224 const successDiv = document.getElementById('success');
225 const submitBtn = document.getElementById('submitBtn');
226 const loadingDiv = document.getElementById('loading');
227
228 function showError(message) {
229 errorDiv.textContent = message;
230 errorDiv.style.display = 'block';
231 successDiv.style.display = 'none';
232 }
233
234 function showSuccess(message) {
235 successDiv.textContent = message;
236 successDiv.style.display = 'block';
237 errorDiv.style.display = 'none';
238 }
239
240 function hideMessages() {
241 errorDiv.style.display = 'none';
242 successDiv.style.display = 'none';
243 }
244
245 form.addEventListener('submit', async (e) => {
246 e.preventDefault();
247 hideMessages();
248
249 const handle = document.getElementById('handle').value.trim();
250 const domain = document.getElementById('domain').value;
251 const email = document.getElementById('email').value.trim();
252 const password = document.getElementById('password').value;
253
254 // Validate handle
255 if (!/^[a-zA-Z0-9-]+$/.test(handle)) {
256 showError('Handle can only contain letters, numbers, and hyphens');
257 return;
258 }
259
260 // Build full handle
261 const fullHandle = `${handle}${domain}`;
262
263 // Disable form
264 submitBtn.disabled = true;
265 loadingDiv.style.display = 'block';
266
267 try {
268 // Create account via AT Protocol API
269 const response = await fetch('/xrpc/com.atproto.server.createAccount', {
270 method: 'POST',
271 headers: {
272 'Content-Type': 'application/json',
273 },
274 body: JSON.stringify({
275 email: email,
276 handle: fullHandle,
277 password: password,
278 })
279 });
280
281 const data = await response.json();
282
283 if (!response.ok) {
284 throw new Error(data.message || 'Failed to create account');
285 }
286
287 // Account created successfully
288 showSuccess('Account created successfully! You can now log in to Bluesky with your credentials.');
289
290 // Clear form
291 form.reset();
292
293 } catch (error) {
294 console.error('Error:', error);
295 showError(error.message || 'An error occurred. Please try again.');
296 } finally {
297 submitBtn.disabled = false;
298 loadingDiv.style.display = 'none';
299 }
300 });
301 </script>
302</body>
303</html>