Client side atproto account migrator in your web browser, along with services for backups and adversarial migrations. pdsmoover.com
pds atproto migrations moo cow
128
fork

Configure Feed

Select the types of activity you want to include in your feed.

captcha on restore

+258 -168
+1 -1
justfile
··· 27 27 docker buildx build \ 28 28 --platform linux/arm64,linux/amd64 \ 29 29 --tag fatfingers23/moover_ui:latest \ 30 - --tag fatfingers23/moover_ui:0.0.10 \ 30 + --tag fatfingers23/moover_ui:0.0.11 \ 31 31 --file Dockerfiles/web-ui.Dockerfile \ 32 32 --builder desktop-linux \ 33 33 --push .
+5
packages/moover/lib/restore.js
··· 69 69 * @param newEmail {string} - The new email for the new account 70 70 * @param inviteCode {string|null} - The invite code for the new PDS if it requires one 71 71 * @param cidToRestoreTo {string|null} - The cid of the plc op to restore to, used mostly to revert a fraudulent plc op. Want to give it the last valid operations cid 72 + * @param verificationCode {string|null} - The verification code from the captcha/gate flow, required if the new PDS has phoneVerificationRequired 72 73 * @param onStatus {function|null} - A function that takes a string used to update the UI. Like (status) => console.log(status) 73 74 * @returns {Promise<void>} If there is a failure during restoring the back up (after the status Success! Restoring your repo...) then your account is most likely 74 75 * recovered and future runs need to have the RestoreFromBackup flag set to true and AccountRecovery set to false. ··· 83 84 newEmail, 84 85 inviteCode, 85 86 cidToRestoreTo = null, 87 + verificationCode = null, 86 88 onStatus = null, 87 89 ) { 88 90 if (onStatus) onStatus('Resolving your handle...') ··· 205 207 } 206 208 if (inviteCode) { 207 209 createAccountRequest.inviteCode = inviteCode 210 + } 211 + if (verificationCode) { 212 + createAccountRequest.verificationCode = verificationCode 208 213 } 209 214 const _ = await this.atpAgent.com.atproto.server.createAccount(createAccountRequest, { 210 215 headers: { authorization: `Bearer ${serviceAuthToken}` },
+1 -1
packages/moover/package.json
··· 1 1 { 2 2 "name": "@pds-moover/moover", 3 - "version": "1.0.8", 3 + "version": "1.0.9", 4 4 "description": "Utilities for ATProto PDS migrations and recovery", 5 5 "repository": { 6 6 "type": "git",
+2 -1
packages/moover/types/restore.d.ts
··· 54 54 * @param newEmail {string} - The new email for the new account 55 55 * @param inviteCode {string|null} - The invite code for the new PDS if it requires one 56 56 * @param cidToRestoreTo {string|null} - The cid of the plc op to restore to, used mostly to revert a fraudulent plc op. Want to give it the last valid operations cid 57 + * @param verificationCode {string|null} - The verification code from the captcha/gate flow, required if the new PDS has phoneVerificationRequired 57 58 * @param onStatus {function|null} - A function that takes a string used to update the UI. Like (status) => console.log(status) 58 59 * @returns {Promise<void>} If there is a failure during restoring the back up (after the status Success! Restoring your repo...) then your account is most likely 59 60 * recovered and future runs need to have the RestoreFromBackup flag set to true and AccountRecovery set to false. 60 61 */ 61 - recover(rotationKey: string, rotationKeyType: string, currentHandleOrDid: string, newPDS: string, newHandle: string, newPassword: string, newEmail: string, inviteCode: string | null, cidToRestoreTo?: string | null, onStatus?: Function | null): Promise<void>; 62 + recover(rotationKey: string, rotationKeyType: string, currentHandleOrDid: string, newPDS: string, newHandle: string, newPassword: string, newEmail: string, inviteCode: string | null, cidToRestoreTo?: string | null, verificationCode?: string | null, onStatus?: Function | null): Promise<void>; 62 63 /** 63 64 * This method signs the plc operation over to the new PDS and activates the account 64 65 * Assumes you have already created a new account during the recovery process and logged in
+1 -1
packages/moover/types/restore.d.ts.map
··· 1 - {"version":3,"file":"restore.d.ts","sourceRoot":"","sources":["../lib/restore.js"],"names":[],"mappings":"wBACa,OAAO,iBAAiB,EAAE,SAAS;AAWhD;IACE;;;OAGG;IACH,gCAF6B,MAAM,EA0ClC;IAvCC;;wBAEoB;IACpB,QADU,MAAM,CACU;IAE1B;;;OAGG;IACH,mBAFU,MAAM,CAE0B;IAE1C;;;;OAIG;IACH,yBAFU,IAAI,GAAC,6BAA6B,CAET;IAEnC,uBAAuB;IACvB,UADW,QAAQ,CACC;IAEpB;;;OAGG;IACH,yBAFU,IAAI,GAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,WAAW,MAAM,EAAE,CAAC;QAAC,OAAO,EAAE,cAAc,GAAC,mBAAmB,CAAA;KAAC,CAE1E;IAEnC;;;OAGG;IACH,mBAFU,OAAO,CAEY;IAE7B;;;;OAIG;IACH,iBAFU,OAAO,CAEU;IAG7B;;;;;;;;;;;;;;;OAeG;IACH,qBAbuB,MAAM,mBACF,MAAM,sBACH,MAAM,UAClB,MAAM,aACH,MAAM,eACJ,MAAM,YACT,MAAM,cACJ,MAAM,GAAC,IAAI,mBACP,MAAM,GAAC,IAAI,aACjB,WAAS,IAAI,GACpB,OAAO,CAAC,IAAI,CAAC,CAiNzB;IAED;;;;;;;;;OASG;IACH,0FAFa,OAAO,CAAC,IAAI,CAAC,CAsCzB;CACF;uBArUsB,cAAc;8CAGS,gBAAgB;yBADrC,cAAc;+BAJa,gBAAgB;oCAAhB,gBAAgB"} 1 + {"version":3,"file":"restore.d.ts","sourceRoot":"","sources":["../lib/restore.js"],"names":[],"mappings":"wBACa,OAAO,iBAAiB,EAAE,SAAS;AAWhD;IACE;;;OAGG;IACH,gCAF6B,MAAM,EA0ClC;IAvCC;;wBAEoB;IACpB,QADU,MAAM,CACU;IAE1B;;;OAGG;IACH,mBAFU,MAAM,CAE0B;IAE1C;;;;OAIG;IACH,yBAFU,IAAI,GAAC,6BAA6B,CAET;IAEnC,uBAAuB;IACvB,UADW,QAAQ,CACC;IAEpB;;;OAGG;IACH,yBAFU,IAAI,GAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,WAAW,MAAM,EAAE,CAAC;QAAC,OAAO,EAAE,cAAc,GAAC,mBAAmB,CAAA;KAAC,CAE1E;IAEnC;;;OAGG;IACH,mBAFU,OAAO,CAEY;IAE7B;;;;OAIG;IACH,iBAFU,OAAO,CAEU;IAG7B;;;;;;;;;;;;;;;;OAgBG;IACH,qBAduB,MAAM,mBACF,MAAM,sBACH,MAAM,UAClB,MAAM,aACH,MAAM,eACJ,MAAM,YACT,MAAM,cACJ,MAAM,GAAC,IAAI,mBACP,MAAM,GAAC,IAAI,qBACT,MAAM,GAAC,IAAI,aACnB,WAAS,IAAI,GACpB,OAAO,CAAC,IAAI,CAAC,CAqNzB;IAED;;;;;;;;;OASG;IACH,0FAFa,OAAO,CAAC,IAAI,CAAC,CAsCzB;CACF;uBA1UsB,cAAc;8CAGS,gBAAgB;yBADrC,cAAc;+BAJa,gBAAgB;oCAAhB,gBAAgB"}
+1 -1
web-ui/package.json
··· 17 17 "@atcute/client": "^4.0.5", 18 18 "@atcute/lexicons": "^1.2.2", 19 19 "@pds-moover/lexicons": "^1.0.1", 20 - "@pds-moover/moover": "^1.0.8" 20 + "@pds-moover/moover": "^1.0.9" 21 21 }, 22 22 "devDependencies": { 23 23 "@eslint/compat": "^1.4.0",
+10 -10
web-ui/pnpm-lock.yaml
··· 21 21 specifier: ^1.0.1 22 22 version: 1.0.1 23 23 '@pds-moover/moover': 24 - specifier: ^1.0.8 25 - version: 1.0.8(@atcute/identity@1.1.1)(vite@7.1.12(@types/node@22.19.0)) 24 + specifier: ^1.0.9 25 + version: 1.0.9(@atcute/identity@1.1.1)(vite@7.1.12(@types/node@22.19.0)) 26 26 devDependencies: 27 27 '@eslint/compat': 28 28 specifier: ^1.4.0 ··· 91 91 '@atcute/client@4.0.5': 92 92 resolution: {integrity: sha512-R8Qen8goGmEkynYGg2m6XFlVmz0GTDvQ+9w+4QqOob+XMk8/WDpF4aImev7WKEde/rV2gjcqW7zM8E6W9NShDA==} 93 93 94 - '@atcute/crypto@2.3.0': 95 - resolution: {integrity: sha512-w5pkJKCjbNMQu+F4JRHbR3ROQyhi1wbn+GSC6WDQamcYHkZmEZk1/eoI354bIQOOfkEM6aFLv718iskrkon4GQ==} 94 + '@atcute/crypto@2.4.0': 95 + resolution: {integrity: sha512-XtEeDaSgfr92C7b1VDRvd3F9pI8tVUyy8PJAeu8IWQC7+e/GXZOSl58uWh5YP/9p1Lsa0I16uKHwogygxEwlMQ==} 96 96 97 97 '@atcute/did-plc@0.1.7': 98 98 resolution: {integrity: sha512-a7yOQNqViae3rB5/xa3U0EPJbFD9l8zOHXx6XASZ5F8+Vy2uTgXK3omurpNZ5UxRpy1ni1AMhSohXr61cqWbkg==} ··· 509 509 '@pds-moover/lexicons@1.0.1': 510 510 resolution: {integrity: sha512-fv5b/DtHM7FEo/JklyF9gdK0ainlb6mWjWrBe6cmSAeg9G/4O2jBlQUOqfOAICY9gOcrCpkOrk9PHgGw//JQ2A==} 511 511 512 - '@pds-moover/moover@1.0.8': 513 - resolution: {integrity: sha512-Fs9yIaj5jArHeMy2DxyIwkAjoXKJ7GUlXGbwNUsPeRSjTVFouzyRK35HrhtIq0dZ+q02k0I97PVFg5lVTSeI/A==} 512 + '@pds-moover/moover@1.0.9': 513 + resolution: {integrity: sha512-t3MF1tbXSBZApPlUdqdnxII7+xu/rkD0LSENKzMiPC6ihmPAa/I9oA/iyl4y9wLaLdZuIg8GSM48chALkECV0w==} 514 514 515 515 '@polka/url@1.0.0-next.29': 516 516 resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} ··· 1507 1507 '@atcute/identity': 1.1.1 1508 1508 '@atcute/lexicons': 1.2.2 1509 1509 1510 - '@atcute/crypto@2.3.0': 1510 + '@atcute/crypto@2.4.0': 1511 1511 dependencies: 1512 1512 '@atcute/multibase': 1.1.8 1513 1513 '@atcute/uint8array': 1.1.1 ··· 1517 1517 dependencies: 1518 1518 '@atcute/cbor': 2.3.2 1519 1519 '@atcute/cid': 2.4.1 1520 - '@atcute/crypto': 2.3.0 1520 + '@atcute/crypto': 2.4.0 1521 1521 '@atcute/identity': 1.1.1 1522 1522 '@atcute/lexicons': 1.2.2 1523 1523 '@atcute/multibase': 1.1.8 ··· 1833 1833 '@atproto/lexicon': 0.5.1 1834 1834 '@atproto/xrpc': 0.7.5 1835 1835 1836 - '@pds-moover/moover@1.0.8(@atcute/identity@1.1.1)(vite@7.1.12(@types/node@22.19.0))': 1836 + '@pds-moover/moover@1.0.9(@atcute/identity@1.1.1)(vite@7.1.12(@types/node@22.19.0))': 1837 1837 dependencies: 1838 1838 '@atcute/cbor': 2.3.2 1839 1839 '@atcute/client': 4.0.5 1840 - '@atcute/crypto': 2.3.0 1840 + '@atcute/crypto': 2.4.0 1841 1841 '@atcute/did-plc': 0.1.7 1842 1842 '@atcute/identity-resolver': 1.2.2(@atcute/identity@1.1.1) 1843 1843 '@atcute/lexicons': 1.2.2
+10
web-ui/src/routes/restore/+page.server.ts
··· 1 + import type {PageServerLoad} from './$types'; 2 + import {env} from '$env/dynamic/private'; 3 + 4 + export const load: PageServerLoad = async () => { 5 + const allowedPds = env.PDS_AUTOFILL.split(',').sort(); 6 + 7 + return { 8 + allowedPds 9 + }; 10 + };
+227 -153
web-ui/src/routes/restore/+page.svelte
··· 2 2 import {Restore} from '@pds-moover/moover'; 3 3 import MooHeader from '$lib/components/MooHeader.svelte'; 4 4 import LoadingSpinner from '$lib/components/LoadingSpinner.svelte'; 5 + import Captcha from '$lib/components/Captcha.svelte'; 5 6 import {env} from '$env/dynamic/public'; 6 7 import OgImage from '$lib/components/OgImage.svelte'; 8 + import {Client, simpleFetchHandler} from '@atcute/client'; 9 + import type {} from '@atcute/atproto'; 10 + import type {ComAtprotoServerDescribeServer} from '@atcute/atproto'; 11 + 12 + 13 + let {data} = $props(); 7 14 8 15 //Regexs to catch rotation key type 9 16 const HEX_REGEX = /^[0-9a-f]+$/i; ··· 37 44 let statusMessageText = $state(''); 38 45 let isSubmitting = $state(false); 39 46 47 + // Captcha state 48 + let showCaptcha = $state(false); 49 + let verificationCode = $state<string | null>(null); 50 + let pdsOptions = $state<ComAtprotoServerDescribeServer.$output | null>(null); 51 + 52 + function extractHostname(url: string): string | null { 53 + try { 54 + return new URL(url).hostname; 55 + } catch { 56 + return url; 57 + } 58 + } 59 + 60 + // Watch newPds input and auto-fetch describeServer for allowed PDS hosts 61 + $effect(() => { 62 + const hostname = extractHostname(newPds); 63 + console.log('New PDS hostname:', hostname); 64 + if (!hostname || !data.allowedPds.includes(hostname.toLowerCase())) { 65 + pdsOptions = null; 66 + return; 67 + } 68 + const pdsUrl = `https://${hostname}`; 69 + const handler = simpleFetchHandler({service: pdsUrl}); 70 + const rpc = new Client({handler}); 71 + rpc.get('com.atproto.server.describeServer', {}).then((res) => { 72 + if (!res.ok) return; 73 + pdsOptions = res.data; 74 + }).catch((e) => { 75 + console.error('Failed to describe PDS', e); 76 + }); 77 + }); 78 + 79 + const captchaVerificationRequired = $derived(pdsOptions?.phoneVerificationRequired === true); 80 + const cleanPdsHostname = $derived(extractHostname(newPds)); 81 + 40 82 function setStatus(message: string) { 41 83 console.log('Status update:', message); 42 84 statusMessageText = message; ··· 52 94 } 53 95 } 54 96 97 + function handleCaptchaSuccess(code: string) { 98 + verificationCode = code; 99 + showCaptcha = false; 100 + performRestore(); 101 + } 102 + 103 + function handleCaptchaError(error: string) { 104 + errorMessage = `Verification failed: ${error}`; 105 + isSubmitting = false; 106 + } 107 + 55 108 async function handleSubmit() { 56 109 errorMessage = null; 57 110 showStatusMessage = false; 58 111 isSubmitting = true; 59 112 113 + if (captchaVerificationRequired && recoverAccount && !verificationCode) { 114 + showCaptcha = true; 115 + return; 116 + } 117 + 118 + await performRestore(); 119 + } 120 + 121 + async function performRestore() { 60 122 try { 61 123 restoreService.RestoreFromBackup = restoreFilesFromBackup; 62 124 restoreService.AccountRecovery = recoverAccount; ··· 71 133 newEmail, 72 134 inviteCode || null, 73 135 cid || null, 136 + verificationCode, 74 137 setStatus 75 138 ); 76 139 done = true; ··· 104 167 </div> 105 168 106 169 {#if !done} 107 - <form 108 - id="restore-form" 109 - onsubmit={(e) => { 170 + {#if showCaptcha} 171 + <Captcha 172 + pdsUrl={`https://${cleanPdsHostname}`} 173 + handle={newHandle} 174 + onSuccess={handleCaptchaSuccess} 175 + onError={handleCaptchaError} 176 + /> 177 + {:else} 178 + <form 179 + id="restore-form" 180 + onsubmit={(e) => { 110 181 e.preventDefault(); 111 182 handleSubmit(); 112 183 }} 113 - > 114 - <div class="form-group"> 115 - <label for="current-handle" 116 - >Your current handle or did (found in the recovery rotation-key.txt)</label 117 - > 118 - <input 119 - id="current-handle" 120 - bind:value={currentHandle} 121 - placeholder="did:plc:rnpkyqnmsw4ipey6eotbdnnf" 122 - required 123 - /> 124 - </div> 125 - 126 - {#if recoverAccount} 184 + > 127 185 <div class="form-group"> 128 - <label for="rotationKey">Private Rotation Key</label> 186 + <label for="current-handle" 187 + >Your current handle or did (found in the recovery rotation-key.txt)</label 188 + > 129 189 <input 130 - id="rotationKey" 131 - bind:value={rotationKey} 132 - oninput={handleRotationKeyChange} 133 - placeholder="a secp256k1 or p256 in either hex or multikey format" 134 - required={recoverAccount} 190 + id="current-handle" 191 + bind:value={currentHandle} 192 + placeholder="did:plc:rnpkyqnmsw4ipey6eotbdnnf" 193 + required 135 194 /> 136 195 </div> 137 196 138 - {#if isHexKey} 197 + {#if recoverAccount} 139 198 <div class="form-group"> 140 - <fieldset style="border: none; padding: 0; margin: 0;"> 141 - <legend style="margin-bottom: 0.5rem;">Rotation Key Type</legend> 142 - <div style="display: flex; gap: 1rem;"> 143 - <label style="display: flex; align-items: center; gap: 0.5rem; cursor: pointer;"> 144 - <input 145 - type="radio" 146 - name="rotationKeyType" 147 - value="secp256k1" 148 - bind:group={rotationKeyType} 149 - /> 150 - secp256k1 151 - </label> 152 - <label style="display: flex; align-items: center; gap: 0.5rem; cursor: pointer;"> 153 - <input 154 - type="radio" 155 - name="rotationKeyType" 156 - value="p256" 157 - bind:group={rotationKeyType} 158 - /> 159 - p256 160 - </label> 161 - </div> 162 - <small class="help-text">Select the type of your hex-encoded rotation key</small> 163 - </fieldset> 199 + <label for="rotationKey">Private Rotation Key</label> 200 + <input 201 + id="rotationKey" 202 + bind:value={rotationKey} 203 + oninput={handleRotationKeyChange} 204 + placeholder="a secp256k1 or p256 in either hex or multikey format" 205 + required={recoverAccount} 206 + /> 164 207 </div> 208 + 209 + {#if isHexKey} 210 + <div class="form-group"> 211 + <fieldset style="border: none; padding: 0; margin: 0;"> 212 + <legend style="margin-bottom: 0.5rem;">Rotation Key Type</legend> 213 + <div style="display: flex; gap: 1rem;"> 214 + <label style="display: flex; align-items: center; gap: 0.5rem; cursor: pointer;"> 215 + <input 216 + type="radio" 217 + name="rotationKeyType" 218 + value="secp256k1" 219 + bind:group={rotationKeyType} 220 + /> 221 + secp256k1 222 + </label> 223 + <label style="display: flex; align-items: center; gap: 0.5rem; cursor: pointer;"> 224 + <input 225 + type="radio" 226 + name="rotationKeyType" 227 + value="p256" 228 + bind:group={rotationKeyType} 229 + /> 230 + p256 231 + </label> 232 + </div> 233 + <small class="help-text">Select the type of your hex-encoded rotation key</small> 234 + </fieldset> 235 + </div> 236 + {/if} 165 237 {/if} 166 - {/if} 167 238 168 - <div class="form-group"> 169 - <label for="newPds">New PDS</label> 170 - <input 171 - id="newPds" 172 - type="text" 173 - bind:value={newPds} 174 - placeholder="https://newpds.com" 175 - required 176 - /> 177 - </div> 178 - 179 - <div class="form-group"> 180 - <label for="newHandle">New Handle</label> 181 - <input 182 - id="newHandle" 183 - type="text" 184 - bind:value={newHandle} 185 - placeholder="you.newpds.com or thisisme.com" 186 - required 187 - /> 188 - </div> 239 + <div class="form-group"> 240 + <label for="newPds">New PDS</label> 241 + <input 242 + id="newPds" 243 + type="text" 244 + bind:value={newPds} 245 + placeholder="https://newpds.com" 246 + required 247 + /> 248 + </div> 189 249 190 - <div class="form-group"> 191 - <label for="newPassword">New Password</label> 192 - <input id="newPassword" type="password" bind:value={newPassword} required/> 193 - </div> 250 + <div class="form-group"> 251 + <label for="newHandle">New Handle</label> 252 + <input 253 + id="newHandle" 254 + type="text" 255 + bind:value={newHandle} 256 + placeholder="you.newpds.com or thisisme.com" 257 + required 258 + /> 259 + </div> 194 260 195 - <div class="form-group"> 196 - <label for="newEmail">New Email</label> 197 - <input 198 - id="newEmail" 199 - type="email" 200 - bind:value={newEmail} 201 - placeholder="you@example.com" 202 - required 203 - /> 204 - </div> 261 + <div class="form-group"> 262 + <label for="newPassword">New Password</label> 263 + <input id="newPassword" type="password" bind:value={newPassword} required/> 264 + </div> 205 265 206 - <div class="form-group"> 207 - <label for="inviteCode">Invite code (optional)</label> 208 - <input id="inviteCode" type="text" bind:value={inviteCode} placeholder="Optional"/> 209 - </div> 266 + <div class="form-group"> 267 + <label for="newEmail">New Email</label> 268 + <input 269 + id="newEmail" 270 + type="email" 271 + bind:value={newEmail} 272 + placeholder="you@example.com" 273 + required 274 + /> 275 + </div> 210 276 211 - <div class="form-group"> 212 - <button type="button" onclick={() => (showAdvanced = !showAdvanced)}> 213 - {showAdvanced ? 'Hide advanced options' : 'Show advanced options'} 214 - </button> 215 - </div> 277 + <div class="form-group"> 278 + <label for="inviteCode">Invite code (optional)</label> 279 + <input id="inviteCode" type="text" bind:value={inviteCode} placeholder="Optional"/> 280 + </div> 216 281 217 - {#if showAdvanced} 218 - <div class="advanced-options"> 219 - <div class="form-group"> 220 - <label class="moove-checkbox-label"> 221 - <input 222 - id="recoverAccount" 223 - type="checkbox" 224 - bind:checked={recoverAccount} 225 - /> 226 - Recover the did doc & account. 227 - </label> 228 - <br/> 229 - <small class="help-text">This option temporary assigns a signing key to your did doc, uses it to 230 - create a new account on the new PDS with your did, and migrates the account to the new 231 - PDS.</small> 232 - </div> 282 + <div class="form-group"> 283 + <button type="button" onclick={() => (showAdvanced = !showAdvanced)}> 284 + {showAdvanced ? 'Hide advanced options' : 'Show advanced options'} 285 + </button> 286 + </div> 233 287 234 - <div class="form-group"> 235 - <label class="moove-checkbox-label"> 236 - <input 237 - id="restoreFilesFromBackup" 238 - type="checkbox" 239 - bind:checked={restoreFilesFromBackup} 240 - /> 241 - Restores files from backup. 242 - </label> 243 - </div> 288 + {#if showAdvanced} 289 + <div class="advanced-options"> 290 + <div class="form-group"> 291 + <label class="moove-checkbox-label"> 292 + <input 293 + id="recoverAccount" 294 + type="checkbox" 295 + bind:checked={recoverAccount} 296 + /> 297 + Recover the did doc & account. 298 + </label> 299 + <br/> 300 + <small class="help-text">This option temporary assigns a signing key to your did doc, uses 301 + it to 302 + create a new account on the new PDS with your did, and migrates the account to the new 303 + PDS.</small> 304 + </div> 244 305 245 - {#if recoverAccount} 246 306 <div class="form-group"> 247 - <label for="cid">Cid (optional)</label> 248 - <input 249 - id="cid" 250 - type="text" 251 - bind:value={cid} 252 - placeholder="Specific PLC audit log CID to restore to (advanced)" 253 - /> 254 - <small class="help-text" 255 - >Leave empty for normal restore. IF someone besides you changed your did doc and 256 - stole your atproto identity or a mistake was made you have up to 72hrs to reverse that 257 - change with a valid 258 - rotation key from the last valid operation. This input allows you to restore to the 259 - last good PLC op via it's 260 - CID.</small 261 - > 307 + <label class="moove-checkbox-label"> 308 + <input 309 + id="restoreFilesFromBackup" 310 + type="checkbox" 311 + bind:checked={restoreFilesFromBackup} 312 + /> 313 + Restores files from backup. 314 + </label> 262 315 </div> 263 - {/if} 264 - </div> 265 - {/if} 316 + 317 + {#if recoverAccount} 318 + <div class="form-group"> 319 + <label for="cid">Cid (optional)</label> 320 + <input 321 + id="cid" 322 + type="text" 323 + bind:value={cid} 324 + placeholder="Specific PLC audit log CID to restore to (advanced)" 325 + /> 326 + <small class="help-text" 327 + >Leave empty for normal restore. IF someone besides you changed your did doc and 328 + stole your atproto identity or a mistake was made you have up to 72hrs to reverse 329 + that 330 + change with a valid 331 + rotation key from the last valid operation. This input allows you to restore to the 332 + last good PLC op via it's 333 + CID.</small 334 + > 335 + </div> 336 + {/if} 337 + </div> 338 + {/if} 266 339 267 - {#if errorMessage} 268 - <div class="error-message">{errorMessage}</div> 269 - {/if} 270 - {#if showStatusMessage} 271 - <div class="status-message">{statusMessageText}</div> 272 - {/if} 340 + {#if errorMessage} 341 + <div class="error-message">{errorMessage}</div> 342 + {/if} 343 + {#if showStatusMessage} 344 + <div class="status-message">{statusMessageText}</div> 345 + {/if} 273 346 274 - <div class="form-actions"> 275 - <button type="submit" disabled={isSubmitting}> 276 - {#if isSubmitting} 277 - <LoadingSpinner/> 278 - {/if} 279 - Recover and restore your account 280 - </button> 281 - </div> 282 - </form> 347 + <div class="form-actions"> 348 + <button type="submit" disabled={isSubmitting}> 349 + {#if isSubmitting} 350 + <LoadingSpinner/> 351 + {/if} 352 + Recover and restore your account 353 + </button> 354 + </div> 355 + </form> 356 + {/if} 283 357 {/if} 284 358 285 359 <!-- Done -->