Client side atproto account migrator in your web browser, along with services for backups and adversarial migrations. pdsmoover.com
pds atproto migrations moo cow
at feat/batch-one-qol 233 lines 11 kB view raw
1{%- import "partials/cow-header.askama.html" as cow -%} 2 3{% extends "layout.askama.html" %} 4 5{% block meta %} 6 <meta property="og:description" content="Import missing blobs from your old PDS to your new PDS"/> 7 <meta property="og:image" content="/missing.webp"> 8{% endblock %} 9 10{% block content %} 11 12<script> 13 14 document.addEventListener('alpine:init', () => { 15 window.Alpine.data('app', () => ({ 16 showCurrentLogin: true, 17 showOldLogin: false, 18 showAdvance: false, 19 disableLoginButton: false, 20 showBlobMoveProgress: false, 21 oldPdsUrl: null, 22 showTryAgain: false, 23 error: '', 24 showStatusMessage: false, 25 statusMessage: '', 26 currentLogin: { 27 handle: '', 28 password: '', 29 twoFactorCode: '', 30 showTwoFactorCodeInput: false, 31 }, 32 oldLogin: { 33 password: '', 34 twoFactorCode: '', 35 showTwoFactorCodeInput: false, 36 }, 37 resetStatusAndErrors() { 38 this.showStatusMessage = false; 39 this.statusMessage = ''; 40 this.error = ''; 41 this.disableLoginButton = true; 42 }, 43 async handleCurrentLogin() { 44 this.resetStatusAndErrors(); 45 try { 46 const { 47 accountStatus, 48 missingBlobsCount 49 } = await window.MissingBlobs.currentAgentLogin(this.currentLogin.handle, this.currentLogin.password, this.currentLogin.twoFactorCode); 50 // const noMissingBlobs = accountStatus.expectedBlobs <= accountStatus.importedBlobs; 51 console.log(missingBlobsCount); 52 const noMissingBlobs = missingBlobsCount === 0; 53 if (noMissingBlobs) { 54 this.statusMessage = `You are good to go! You are not missing any blobs. Your account has ${accountStatus.importedBlobs} imported blobs and expects to have at least ${accountStatus.expectedBlobs} blobs. No action is required.` 55 } else { 56 this.showCurrentLogin = false; 57 this.statusMessage = `You are currently missing some blobs. Login with your old password to import the missing blobs. We will automatically find your old handle.`; 58 this.showOldLogin = true; 59 } 60 this.showStatusMessage = true; 61 62 } catch (error) { 63 console.error(error.error, error.message); 64 if (error.error === 'AuthFactorTokenRequired') { 65 this.currentLogin.showTwoFactorCodeInput = true; 66 } 67 this.error = error.message; 68 } 69 this.disableLoginButton = false; 70 }, 71 async handleOldLogin() { 72 this.resetStatusAndErrors(); 73 try { 74 await window.MissingBlobs.oldAgentLogin(this.oldLogin.password, this.oldLogin.twoFactorCode, this.oldPdsUrl); 75 this.showOldLogin = false; 76 this.showBlobMoveProgress = true; 77 this.showStatusMessage = true; 78 this.statusMessage = ''; 79 await this.migrateMissingBlobs(); 80 } catch (error) { 81 console.error(error.error, error.message); 82 if (error.error === 'AuthFactorTokenRequired') { 83 this.oldLogin.showTwoFactorCodeInput = true; 84 } 85 this.error = error.message; 86 } 87 this.disableLoginButton = false; 88 }, 89 updateStatusHandler(status) { 90 console.log("Status update:", status); 91 document.getElementById("missing-status-message").innerText = status; 92 93 }, 94 async migrateMissingBlobs() { 95 try { 96 this.resetStatusAndErrors(); 97 this.showStatusMessage = true; 98 this.showTryAgain = false; 99 const { 100 accountStatus, 101 missingBlobsCount 102 } = await window.MissingBlobs.migrateMissingBlobs(this.updateStatusHandler); 103 const noMissingBlobs = missingBlobsCount === 0; 104 // const noMissingBlobs = accountStatus.expectedBlobs === accountStatus.importedBlobs; 105 if (noMissingBlobs) { 106 this.statusMessage = `You are good to go! You have all ${accountStatus.importedBlobs} of the expected ${accountStatus.expectedBlobs} blobs. You're done!!` 107 } else { 108 this.statusMessage = `Expected blobs: ${accountStatus.expectedBlobs} Imported blobs: ${accountStatus.importedBlobs}`; 109 this.showTryAgain = true; 110 } 111 } catch (error) { 112 console.error(error.error, error.message); 113 this.error = error.message; 114 this.showTryAgain = true; 115 } 116 this.disableLoginButton = false; 117 }, 118 toggleAdvanceMenu() { 119 this.showAdvance = !this.showAdvance; 120 } 121 122 })); 123 }); 124 </script> 125 126</head> 127<body> 128<div class="container" x-data="app"> 129 130 131 132 {% call cow::cow_header("Missing Blobs Importer", "<img src='/missing.webp' alt='Cartoon milk cow on a missing poster' style='max-width: 100%; max-height: 100%; object-fit: contain;'>") %} 133 134 <a href="https://blacksky.community/profile/did:plc:g7j6qok5us4hjqlwjxwrrkjm/post/3lyylumcpok2c">How to video 135 guide</a> 136 137 138 <!-- First section: Current Login credentials --> 139 140 <form x-show="showCurrentLogin" id="moover-form" @submit.prevent="await handleCurrentLogin()"> 141 <div class="section"> 142 <h2>Login for your current PDS</h2> 143 <div class="form-group"> 144 <label for="current_handle">Current Handle:</label> 145 <input type="text" id="current_handle" name="handle" placeholder="alice.bsky.social" 146 x-model="currentLogin.handle" 147 required> 148 </div> 149 150 <div class="form-group"> 151 <label for="current_password">Current Password:</label> 152 <input type="password" id="current_password" name="password" x-model="currentLogin.password" required> 153 </div> 154 155 <div x-show="currentLogin.showTwoFactorCodeInput" class="form-group"> 156 <label for="current_two-factor-code">2FA from the email sent</label> 157 <input type="text" id="current_two-factor-code" name="two-factor-code" 158 x-model="currentLogin.twoFactorCode"> 159 <div class="error-message">Enter your 2fa code here</div> 160 </div> 161 <div x-show="error" x-text="error" class="error-message"></div> 162 <div x-show="showStatusMessage" x-text="statusMessage" class="status-message"></div> 163 164 <div> 165 <button x-bind:disabled="disableLoginButton" type="submit">Login</button> 166 </div> 167 </div> 168 </form> 169 170 <!-- Second section: Old Login credentials --> 171 172 <form x-show="showOldLogin" @submit.prevent="await handleOldLogin()"> 173 <div class="section"> 174 <h2>Password for your OLD PDS</h2> 175 <p>We only need your password for your old account. We can find your old handle from your current login.</p> 176 <div class="form-group"> 177 <label for="password">OLD Password:</label> 178 <input type="password" id="password" name="password" x-model="oldLogin.password" required> 179 </div> 180 181 <div x-show="oldLogin.showTwoFactorCodeInput" class="form-group"> 182 <label for="two-factor-code">2FA from the email sent</label> 183 <input type="text" id="two-factor-code" name="two-factor-code" x-model="oldLogin.twoFactorCode"> 184 <div class="error-message">Enter your 2fa code here</div> 185 </div> 186 187 <div x-show="showAdvance" class="form-group show-advance"> 188 <label for="old_pds">This is optional. If you do not know your old PDS url please leave it blank. We 189 will find it for you. </label> 190 <input type="url" id="old_pds" name="two-factor-code" 191 placeholder="(Optional) Your old PDS URL" x-model="oldPdsUrl"> 192 </div> 193 <div class="form-group"> 194 <button type="button" @click="toggleAdvanceMenu()" id="advance" name="advance">Advance Options 195 </button> 196 </div> 197 198 199 <div x-show="error" x-text="error" class="error-message"></div> 200 <div x-show="showStatusMessage" x-text="statusMessage" class="status-message"></div> 201 202 <div> 203 <button x-bind:disabled="disableLoginButton" type="submit">Login and start the import of missing blobs 204 </button> 205 </div> 206 </div> 207 </form> 208 209 <!-- Third section: Progress while uploading blobs--> 210 211 <div x-show="showBlobMoveProgress"> 212 <div x-show="showStatusMessage" id="warning">*This will take a while. Please do not close this tab. And watch 213 the status message below for updates 214 </div> 215 <div x-show="showStatusMessage" x-text="statusMessage" id="missing-status-message" class="status-message"></div> 216 <div x-show="error" x-text="error" class="error-message"></div> 217 <div x-show="showTryAgain"> 218 <p style="color: yellow">We were unable to import all of your previous blobs, please try again. If it is 219 still 220 not completing give 221 it a few hours and come back and try again. It may be rate limited. Re running this tool does not harm 222 your account.</p> 223 <br> 224 <button x-on:click="await migrateMissingBlobs()">Try again</button> 225 </div> 226 227 228 </div> 229 230 231</div> 232 233{% endblock %}