Client side atproto account migrator in your web browser, along with services for backups and adversarial migrations.
12 kB
267 lines
1<script lang="ts">
2 import MooHeader from '$lib/components/MooHeader.svelte';
3 import OgImage from '$lib/components/OgImage.svelte';
4 import {resolve} from '$app/paths';
5 import {Migrator} from '@pds-moover/moover';
6 import SignThePapers from './SignThePapers.svelte';
7
8 let formData = $state({
9 handle: '',
10 password: '',
11 newPds: '',
12 newEmail: '',
13 newHandle: '',
14 inviteCode: null,
15 twoFactorCode: null,
16 confirmation: false,
17 // Advanced options
18 createNewAccount: true,
19 migrateRepo: true,
20 migrateBlobs: true,
21 migrateMissingBlobs: true,
22 migratePrefs: true,
23 migratePlcRecord: true,
24 });
25
26 let migrator = $state(new Migrator());
27
28 //UI state
29 let showTwoFactorCodeInput = $state(false);
30 let showAdvance = $state(false);
31 let showStatusMessage = $state(false);
32 let askForPlcToken = $state(false);
33 let disableSubmit = $state(false);
34
35 let errorMessage: null | string = $state(null);
36 let statusMessage: null | string = $state(null);
37
38 const updateStatusHandler = (status: string) => {
39 statusMessage = status;
40 }
41
42 async function submitMoove(event: SubmitEvent & { currentTarget: EventTarget & HTMLFormElement }) {
43 event.preventDefault();
44 disableSubmit = true;
45 errorMessage = null;
46 showStatusMessage = false;
47
48 if (!formData.confirmation) {
49 errorMessage = 'Please confirm that you understand the risks of doing an account migration';
50 disableSubmit = false;
51 return;
52 }
53
54 try {
55
56 if (showTwoFactorCodeInput) {
57 if (showTwoFactorCodeInput === null) {
58 errorMessage = 'Please enter the 2FA that was sent to your email.'
59 disableSubmit = false;
60 return;
61 }
62 }
63
64 // Advance options from $state
65 migrator.createNewAccount = formData.createNewAccount;
66 migrator.migrateRepo = formData.migrateRepo;
67 migrator.migrateBlobs = formData.migrateBlobs;
68 migrator.migrateMissingBlobs = formData.migrateMissingBlobs;
69 migrator.migratePrefs = formData.migratePrefs;
70 migrator.migratePlcRecord = formData.migratePlcRecord;
71
72 console.log(migrator);
73
74 updateStatusHandler('Starting migration...');
75 showStatusMessage = true;
76 await migrator.migrate(
77 formData.handle,
78 formData.password,
79 formData.newPds,
80 formData.newEmail,
81 formData.newHandle,
82 formData.inviteCode,
83 updateStatusHandler,
84 formData.twoFactorCode,
85 );
86 if (migrator.migratePlcRecord) {
87 //I don't think disable submit is needed, but you never know.
88 disableSubmit = false;
89 askForPlcToken = true;
90 } else {
91 updateStatusHandler('Migration of your repo is complete! But the PLC operation was not done so your old account is still the valid one.');
92 }
93 } catch (error) {
94 disableSubmit = false;
95 console.error(error);
96 //@ts-expect-error: JS being js. doesn't like not having the type'
97 if (error.error === 'AuthFactorTokenRequired') {
98 showTwoFactorCodeInput = true;
99 }
100 //@ts-expect-error: JS being js. doesn't like not having the type'
101 errorMessage = error.message;
102 }
103 }
104</script>
105
106<svelte:head>
107 <title>PDS MOOver</title>
108 <meta property="og:description" content="ATProto account migration tool"/>
109 <OgImage/>
110</svelte:head>
111
112<div class="container">
113 <MooHeader title="PDS MOOver"/>
114 {#if !askForPlcToken}
115 <a href={resolve('/info')}>Idk if I trust a cow to move my atproto account to a new PDS</a>
116 <br/>
117 <a href="https://blacksky.community/profile/did:plc:g7j6qok5us4hjqlwjxwrrkjm/post/3lw3hcuojck2u">Video guide for
118 joining blacksky.app</a>
119
120 <form id="moover-form" onsubmit={submitMoove}>
121 <!-- First section: Login credentials -->
122 <div class="section">
123 <h2>Login for your current PDS</h2>
124 <div class="form-group">
125 <label for="handle">Old Handle:</label>
126 <input type="text" id="handle" name="handle" placeholder="alice.bsky.social" required
127 bind:value={formData.handle}>
128 </div>
129
130 <div class="form-group">
131 <label for="password">Old Password (Will also be your new password):</label>
132 <input type="password" id="password" name="password" required bind:value={formData.password}>
133 </div>
134 {#if showTwoFactorCodeInput}
135 <div class="form-group">
136 <label for="two-factor-code">2FA from the email sent</label>
137 <input type="text" id="two-factor-code" name="twoFactorCode"
138 bind:value={formData.twoFactorCode}>
139 <div class="error-message">Enter your 2fa code here</div>
140
141 </div>
142 {/if}
143 </div>
144
145 <!-- Second section: New account details -->
146 <div class="section">
147 <h2>Setup for the new PDS</h2>
148 <div class="form-group">
149 <label for="new-pds">New PDS (URL):</label>
150 <input type="url" id="new-pds" name="newPds" placeholder="https://coolnewpds.com"
151 required bind:value={formData.newPds}>
152 </div>
153
154 <div class="form-group">
155 <label for="new-email">New Email:</label>
156 <input type="email" id="new-email" name="newEmail" placeholder="CanBeSameEmailAsTheOldPds@email.com"
157 required bind:value={formData.newEmail}>
158 </div>
159
160 <div class="form-group">
161 <label for="new-handle">New Handle:</label>
162 <input type="text" id="new-handle" name="newHandle"
163 placeholder="username.newpds.com or mycooldomain.com" required
164 bind:value={formData.newHandle}>
165 </div>
166
167 <div class="form-group">
168 <label for="invite-code">Invite Code:</label>
169 <input type="text" id="invite-code" name="inviteCode"
170 placeholder="Invite code from your new PDS (Leave blank if you don't have one)"
171 bind:value={formData.inviteCode}>
172 </div>
173 </div>
174
175 <div class="form-group">
176 <button type="button" onclick={() => showAdvance = !showAdvance} id="advance" name="advance">Advance
177 Options
178 </button>
179 </div>
180 {#if showAdvance}
181 <div class="section" style="padding-bottom: 10px; text-align: left">
182 <h3>Pick and choose which actions to run</h3>
183 <p>Useful if a migration failed and you want to have a bit more manual control</p>
184 <div class="form-control">
185 <label class="moove-checkbox-label">
186 <input type="checkbox" id="createNewAccount" name="createNewAccount"
187 bind:checked={formData.createNewAccount}>
188 Create a New Account on the New PDS
189 </label>
190 </div>
191 <div class="form-control">
192 <label class="moove-checkbox-label">
193 <input bind:checked={formData.migrateRepo} type="checkbox" id="migrateRepo"
194 name="migrateRepo">
195 Migrate Repo
196 </label>
197 </div>
198 <div class="form-control">
199 <label class="moove-checkbox-label">
200 <input bind:checked={formData.migrateBlobs} type="checkbox" id="migrateBlobs"
201 name="migrateBlobs">
202 Migrate Blobs
203 </label>
204 </div>
205 <div class="form-control">
206 <label class="moove-checkbox-label">
207 <input bind:checked={formData.migrateMissingBlobs} type="checkbox" id="migrateMissingBlobs"
208 name="migrateMissingBlobs">
209 Migrate Missing Blobs
210 </label>
211 </div>
212 <div class="form-control">
213 <label class="moove-checkbox-label">
214 <input bind:checked={formData.migratePrefs} type="checkbox" id="migratePrefs"
215 name="migratePrefs">
216 Migrate Prefs
217 </label>
218 </div>
219 <div class="form-control">
220 <label class="moove-checkbox-label">
221 <input bind:checked={formData.migratePlcRecord} type="checkbox" id="migratePlcRecord"
222 name="migratePlcRecord">
223 Migrate PLC Record
224 </label>
225 </div>
226
227 </div>
228 {/if}
229
230 <p style="text-align: left">There are some risks that come with doing an account migration.
231 (Can view them
232 <a href="https://github.com/bluesky-social/pds/blob/main/ACCOUNT_MIGRATION.md#%EF%B8%8F-warning-%EF%B8%8F-%EF%B8%8F">here</a>)
233 and that the creator or host of this migration tool is not liable and will not be able to help you in
234 the
235 event something goes wrong. I also have read over the <a href={resolve('/info')}>extended information
236 from
237 PDS MOOver
238 about account
239 migrations.</a>
240 </p>
241 <div class="form-group">
242 <label for="confirmation" class="moove-checkbox-label">
243 <input bind:checked={formData.confirmation} type="checkbox" id="confirmation" name="confirmation"
244 required>
245 <span>I understand</span>
246 </label>
247 </div>
248 {#if errorMessage !== null}
249 <div class="error-message">{errorMessage}</div>
250 {/if}
251
252 {#if showStatusMessage}
253 <div id="warning">*Please make sure to stay on this page during the MOOve for the
254 best result
255 </div>
256 <div id="status-message" class="status-message">{statusMessage}</div>
257 {/if}
258
259 <div>
260 <button disabled={disableSubmit} type="submit">MOOve</button>
261 </div>
262 </form>
263
264 {:else}
265 <SignThePapers migrator={migrator} newHandle={formData.newHandle}/>
266 {/if}
267</div>