-2
public/info.html
-2
public/info.html
···
8
8
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"/>
9
9
<title>PDS MOOver Info</title>
10
10
<link rel="stylesheet" href="/style.css">
11
-
<script src="https://unpkg.com/alpinejs" defer></script>
12
-
13
11
</head>
14
12
<body>
15
13
<div class="container">
+106
public/turnoff.html
+106
public/turnoff.html
···
1
+
<!doctype html>
2
+
<html lang="en">
3
+
<head>
4
+
<meta charset="UTF-8"/>
5
+
<link rel="icon" type="image/webp" href="/moo.webp"/>
6
+
<meta property="og:description" content="ATProto account migration tool"/>
7
+
<meta property="og:image" content="/moo.webp">
8
+
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"/>
9
+
<title>PDS MOOver - Turn OFF</title>
10
+
<link rel="stylesheet" href="/style.css">
11
+
<script src="https://unpkg.com/alpinejs" defer></script>
12
+
13
+
<script type="module">
14
+
import {Migrator} from './src/main.js';
15
+
16
+
window.Migrator = new Migrator();
17
+
</script>
18
+
19
+
<script>
20
+
document.addEventListener('alpine:init', () => {
21
+
22
+
Alpine.data('moover', () => ({
23
+
oldHandle: '',
24
+
oldPassword: '',
25
+
twoFactorCode: '',
26
+
showTwoFactorCodeInput: false,
27
+
error: null,
28
+
showStatusMessage: false,
29
+
updateStatusHandler(status) {
30
+
console.log("Status update:", status);
31
+
document.getElementById("status-message").innerText = status;
32
+
},
33
+
async handleSubmit() {
34
+
this.error = null;
35
+
this.showStatusMessage = false;
36
+
37
+
try {
38
+
39
+
if (this.showTwoFactorCodeInput) {
40
+
if (this.twoFactorCode === null) {
41
+
this.error = 'Please enter the 2FA that was sent to your email.'
42
+
}
43
+
}
44
+
45
+
this.showStatusMessage = true;
46
+
await window.Migrator.deactivateOldAccount(
47
+
this.oldHandle,
48
+
this.oldPassword,
49
+
this.updateStatusHandler,
50
+
this.twoFactorCode);
51
+
} catch (error) {
52
+
console.error(error.error, error.message);
53
+
if (error.error === 'AuthFactorTokenRequired') {
54
+
this.showTwoFactorCodeInput = true;
55
+
}
56
+
this.error = error.message;
57
+
}
58
+
},
59
+
}))
60
+
})
61
+
</script>
62
+
</head>
63
+
<body>
64
+
<div class="container" x-data="moover">
65
+
<h1>PDS MOOver</h1>
66
+
67
+
<div class="cow-image">
68
+
<img src="/moo.webp" alt="Cartoon milk cow" style="max-width: 100%; max-height: 100%; object-fit: contain;">
69
+
</div>
70
+
<div class="made-by-blur">Made by <a href="https://bsky.app/profile/baileytownsend.dev">@baileytownsend.dev</a>
71
+
</div>
72
+
<p>Use this page to make sure your old account is deactivated</p>
73
+
<form id="moover-form" @submit.prevent="await handleSubmit()">
74
+
<!-- First section: Login credentials -->
75
+
<div class="section">
76
+
<h2>Login for your old PDS</h2>
77
+
<div class="form-group">
78
+
<label for="handle">Old Handle:</label>
79
+
<input type="text" id="handle" name="handle" placeholder="alice.bsky.social" x-model="oldHandle"
80
+
required>
81
+
</div>
82
+
83
+
<div class="form-group">
84
+
<label for="password">Old Password:</label>
85
+
<input type="password" id="password" name="password" x-model="oldPassword" required>
86
+
</div>
87
+
88
+
<div x-show="showTwoFactorCodeInput" class="form-group">
89
+
<label for="two-factor-code">2FA from the email sent</label>
90
+
<input type="text" id="two-factor-code" name="two-factor-code" x-model="twoFactorCode">
91
+
<div class="error-message">Enter your 2fa code here</div>
92
+
93
+
</div>
94
+
</div>
95
+
96
+
<div x-show="error" x-text="error" class="error-message"></div>
97
+
<div x-show="showStatusMessage" id="status-message" class="status-message"></div>
98
+
<div>
99
+
<button type="submit">Turn it off</button>
100
+
</div>
101
+
</form>
102
+
</div>
103
+
104
+
105
+
</body>
106
+
</html>
+73
-1
src/pdsmoover.js
+73
-1
src/pdsmoover.js
···
237
237
}
238
238
}
239
239
240
+
/**
241
+
* Sign and submits the PLC operation to officially migrate the account
242
+
* @param {string} token - the PLC token sent in the email. If you're just wanting to run this rerun migrate with all the flags set as false except for migratePlcRecord
243
+
* @returns {Promise<void>}
244
+
*/
240
245
async signPlcOperation(token) {
241
246
const getDidCredentials =
242
247
await this.newAgent.com.atproto.identity.getRecommendedDidCredentials();
···
262
267
await this.newAgent.com.atproto.server.activateAccount();
263
268
await this.oldAgent.com.atproto.server.deactivateAccount({});
264
269
}
270
+
271
+
// Quick and dirty copy and paste of the above to get a fix out to help people without breaking or introducing any bugs to the migration service...hopefully
272
+
async deactivateOldAccount(oldHandle, oldPassword, statusUpdateHandler = null, twoFactorCode = null) {
273
+
//Copying the handle from bsky website adds some random unicodes on
274
+
oldHandle = oldHandle.replace('@', '').trim().replace(/[\u202A\u202C\u200E\u200F\u2066-\u2069]/g, '');
275
+
let usersDid;
276
+
//If it's a bsky handle just go with the entryway and let it sort everything
277
+
if (oldHandle.endsWith('.bsky.social')) {
278
+
const publicAgent = new AtpAgent({service: 'https://public.api.bsky.app'});
279
+
const resolveIdentityFromEntryway = await publicAgent.com.atproto.identity.resolveHandle({handle: oldHandle});
280
+
usersDid = resolveIdentityFromEntryway.data.did;
281
+
} else {
282
+
//Resolves the did and finds the did document for the old PDS
283
+
safeStatusUpdate(statusUpdateHandler, 'Resolving did from handle');
284
+
usersDid = await handleResolver.resolve(oldHandle);
285
+
}
286
+
287
+
const didDoc = await docResolver.resolve(usersDid);
288
+
let currentPds;
289
+
try {
290
+
currentPds = didDoc.service.filter(s => s.type === 'AtprotoPersonalDataServer')[0].serviceEndpoint;
291
+
} catch (error) {
292
+
console.error(error);
293
+
throw new Error('Could not find a PDS in the DID document.');
294
+
}
295
+
296
+
const plcLogRequest = await fetch(`https://plc.directory/${usersDid}/log`);
297
+
const plcLog = await plcLogRequest.json();
298
+
let pdsBeforeCurrent = '';
299
+
for (const log of plcLog) {
300
+
try {
301
+
const pds = log.services.atproto_pds.endpoint;
302
+
console.log(pds);
303
+
if (pds.toLowerCase() === currentPds.toLowerCase()) {
304
+
console.log('Found the PDS before the current one');
305
+
break;
306
+
}
307
+
pdsBeforeCurrent = pds;
308
+
} catch (e) {
309
+
console.log(e);
310
+
}
311
+
}
312
+
if (pdsBeforeCurrent === '') {
313
+
throw new Error('Could not find the PDS before the current one');
314
+
}
315
+
316
+
let oldAgent = new AtpAgent({service: pdsBeforeCurrent});
317
+
safeStatusUpdate(statusUpdateHandler, `Logging you in to the old PDS: ${pdsBeforeCurrent}`);
318
+
//Login to the old PDS
319
+
if (twoFactorCode === null) {
320
+
await oldAgent.login({identifier: oldHandle, password: oldPassword});
321
+
} else {
322
+
await oldAgent.login({identifier: oldHandle, password: oldPassword, authFactorToken: twoFactorCode});
323
+
}
324
+
safeStatusUpdate(statusUpdateHandler, 'Checking this isn\'t your current PDS');
325
+
if (pdsBeforeCurrent === currentPds) {
326
+
throw new Error('This is your current PDS. Login to your old account username and password');
327
+
}
328
+
329
+
let currentAccountStatus = await oldAgent.com.atproto.server.checkAccountStatus();
330
+
if (!currentAccountStatus.data.activated) {
331
+
safeStatusUpdate(statusUpdateHandler, 'All good. Your old account is not activated.');
332
+
}
333
+
safeStatusUpdate(statusUpdateHandler, 'Deactivating your OLD account');
334
+
await oldAgent.com.atproto.server.deactivateAccount({});
335
+
safeStatusUpdate(statusUpdateHandler, 'Successfully deactivated your OLD account');
336
+
}
265
337
}
266
338
267
-
export {Migrator};
339
+
export {Migrator};