Client side atproto account migrator in your web browser, along with services for backups and adversarial migrations.

headers

Changed files
+52 -5
web-ui
src
routes
backups
restore
+1 -3
web-ui/src/routes/backups/+page.svelte
··· 10 10 11 11 12 12 // Service instances 13 - let tempBackupService = new BackupService(`did:web:${PUBLIC_XRPC_BASE}`); 14 - tempBackupService.atCuteCredentialManager 15 - 16 13 let backupService = $state(new BackupService(`did:web:${PUBLIC_XRPC_BASE}`)); 17 14 let plcOps = $state(new PlcOps()); 18 15 ··· 90 87 </script> 91 88 92 89 <svelte:head> 90 + <title>PDS MOOver - Backups</title> 93 91 <meta property="og:description" content="PDS MOOver backups"/> 94 92 <meta property="og:image" content="/halloween_moover.webp"/> 95 93 </svelte:head>
+51 -2
web-ui/src/routes/restore/+page.svelte
··· 3 3 import MooHeader from '$lib/components/MooHeader.svelte'; 4 4 import LoadingSpinner from '$lib/components/LoadingSpinner.svelte'; 5 5 import {PUBLIC_XRPC_BASE} from '$env/static/public'; 6 + import OgImage from '$lib/components/OgImage.svelte'; 7 + 8 + //Regexs to catch rotation key type 9 + const HEX_REGEX = /^[0-9a-f]+$/i; 10 + const MULTIKEY_REGEX = /^z[a-km-zA-HJ-NP-Z1-9]+$/; 6 11 7 12 // Service instances 8 13 let restoreService = $state(new Restore(`https://${PUBLIC_XRPC_BASE}`)); ··· 17 22 let inviteCode = $state(''); 18 23 let cid = $state(''); 19 24 25 + // Rotation key type detection and selection 26 + let isHexKey = $state(false); 27 + let rotationKeyType = $state<'secp256k1' | 'p256'>('secp256k1'); 28 + 20 29 // Advanced options 21 30 let restoreOnlyFilesFromBackup = $state(false); 22 31 ··· 34 43 showStatusMessage = true; 35 44 } 36 45 46 + function handleRotationKeyChange() { 47 + const trimmedKey = rotationKey.trim(); 48 + if (trimmedKey && HEX_REGEX.test(trimmedKey)) { 49 + isHexKey = true; 50 + } else { 51 + isHexKey = false; 52 + } 53 + } 54 + 37 55 async function handleSubmit() { 38 56 error = null; 39 57 showStatusMessage = false; ··· 45 63 restoreService.AccountRecovery = !restoreOnlyFilesFromBackup; 46 64 47 65 // The TypeScript signature requires rotationKeyType as second parameter 48 - // Default to 'secp256k1' as mentioned in the comments 49 66 await restoreService.recover( 50 67 rotationKey, 51 - 'secp256k1', // rotationKeyType - defaults to secp256k1 68 + rotationKeyType, 52 69 currentHandle, 53 70 newPds, 54 71 newHandle, ··· 70 87 </script> 71 88 72 89 <svelte:head> 90 + <title>PDS MOOver - Restore</title> 73 91 <meta property="og:description" content="Restore your ATProto account using a rotation key"/> 92 + <OgImage/> 74 93 </svelte:head> 75 94 76 95 <div class="container"> ··· 112 131 <input 113 132 id="rotationKey" 114 133 bind:value={rotationKey} 134 + oninput={handleRotationKeyChange} 115 135 placeholder="a secp256k1 or p256 in either hex or multikey format" 116 136 required={!restoreOnlyFilesFromBackup} 117 137 /> 118 138 </div> 139 + 140 + {#if isHexKey} 141 + <div class="form-group"> 142 + <fieldset style="border: none; padding: 0; margin: 0;"> 143 + <legend style="margin-bottom: 0.5rem;">Rotation Key Type</legend> 144 + <div style="display: flex; gap: 1rem;"> 145 + <label style="display: flex; align-items: center; gap: 0.5rem; cursor: pointer;"> 146 + <input 147 + type="radio" 148 + name="rotationKeyType" 149 + value="secp256k1" 150 + bind:group={rotationKeyType} 151 + /> 152 + secp256k1 153 + </label> 154 + <label style="display: flex; align-items: center; gap: 0.5rem; cursor: pointer;"> 155 + <input 156 + type="radio" 157 + name="rotationKeyType" 158 + value="p256" 159 + bind:group={rotationKeyType} 160 + /> 161 + p256 162 + </label> 163 + </div> 164 + <small class="help-text">Select the type of your hex-encoded rotation key</small> 165 + </fieldset> 166 + </div> 167 + {/if} 119 168 {/if} 120 169 121 170 <div class="form-group">