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

missing blobs and better upload status

Changed files
+52 -7
src
+2 -2
index.html
··· 169 169 </form> 170 170 <div x-show="askForPlcToken" class="section"> 171 171 <form @submit.prevent="await signPlcOperation()"> 172 - <h2>Please enter your PLCToken you received in an email</h2> 172 + <h2>Please enter your PLC Token you received in an email</h2> 173 173 <div class="form-group"> 174 - <label for="plc-token">PLCToken:</label> 174 + <label for="plc-token">PLC Token:</label> 175 175 <input type="text" id="plc-token" name="plc-token" x-model="plcToken" required> 176 176 </div> 177 177 <div x-show="error" x-text="error" class="error-message"></div>
+50 -5
src/pdsmoover.js
··· 35 35 constructor() { 36 36 this.oldAgent = null; 37 37 this.newAgent = null; 38 + this.missingBlobs = []; 38 39 } 39 40 40 41 /** ··· 77 78 await oldAgent.login({identifier: oldHandle, password: password, authFactorToken: twoFactorCode}); 78 79 } 79 80 80 - safeStatusUpdate(statusUpdateHandler, 'Checking that the new PDS is an actual PDS'); 81 + safeStatusUpdate(statusUpdateHandler, 'Checking that the new PDS is an actual PDS (if the url is wrong this takes a while to error out)'); 81 82 const newAgent = new AtpAgent({service: newPdsUrl}); 82 83 const newHostDesc = await newAgent.com.atproto.server.describeServer(); 83 84 const newHostWebDid = newHostDesc.data.did; ··· 110 111 111 112 await newAgent.login({ 112 113 identifier: usersDid, 113 - password, 114 + password: password, 114 115 }); 115 116 116 117 safeStatusUpdate(statusUpdateHandler, 'Migrating your repo'); ··· 119 120 encoding: 'application/vnd.ipld.car', 120 121 }); 121 122 123 + let newAccountStatus = await newAgent.com.atproto.server.checkAccountStatus(); 122 124 safeStatusUpdate(statusUpdateHandler, 'Migrating your blobs'); 123 125 124 126 let blobCursor = undefined; 125 127 let uploadedBlobs = 0; 126 128 do { 127 - safeStatusUpdate(statusUpdateHandler, `Migrating blobs, ${uploadedBlobs}/${uploadedBlobs + 100}`); 128 - uploadedBlobs += 100; 129 + safeStatusUpdate(statusUpdateHandler, `Migrating blobs: ${uploadedBlobs}/${newAccountStatus.data.expectedBlobs}`); 130 + 129 131 const listedBlobs = await oldAgent.com.atproto.sync.listBlobs({ 130 132 did: usersDid, 131 133 cursor: blobCursor, 132 134 limit: 100, 133 135 }); 136 + 134 137 for (const cid of listedBlobs.data.cids) { 135 138 try { 136 139 //TODO may move the status update here but would have it only update like every 10 ··· 141 144 await newAgent.com.atproto.repo.uploadBlob(blobRes.data, { 142 145 encoding: blobRes.headers['content-type'], 143 146 }); 147 + uploadedBlobs++; 148 + if (uploadedBlobs % 10 === 0) { 149 + safeStatusUpdate(statusUpdateHandler, `Migrating blobs: ${uploadedBlobs}/${newAccountStatus.data.expectedBlobs}`); 150 + } 144 151 } catch (error) { 145 152 //TODO silently logging for now will do a missing blobs later 146 153 console.error(error); ··· 149 156 blobCursor = listedBlobs.data.cursor; 150 157 } while (blobCursor); 151 158 152 - //TODO NEED to do some checking on the missing blobs here 159 + newAccountStatus = await newAgent.com.atproto.server.checkAccountStatus(); 160 + if (newAccountStatus.data.expectedBlobs !== uploadedBlobs) { 161 + let totalMissingBlobs = newAccountStatus.data.expectedBlobs - uploadedBlobs; 162 + safeStatusUpdate(statusUpdateHandler, 'Looks like there are some missing blobs. Going to try and upload them now.'); 163 + //Probably should be shared between main blob uploader, but eh 164 + let missingBlobCursor = undefined; 165 + let missingUploadedBlobs = 0; 166 + do { 167 + safeStatusUpdate(statusUpdateHandler, `Migrating blobs: ${missingUploadedBlobs}/${totalMissingBlobs}`); 168 + 169 + const missingBlobs = await oldAgent.com.atproto.repo.listMissingBlobs({ 170 + did: usersDid, 171 + cursor: missingBlobCursor, 172 + limit: 100, 173 + }); 174 + 175 + for (const cid of missingBlobs.data.cids) { 176 + try { 177 + //TODO may move the status update here but would have it only update like every 10 178 + const blobRes = await oldAgent.com.atproto.sync.getBlob({ 179 + cid, 180 + }); 181 + await newAgent.com.atproto.repo.uploadBlob(blobRes.data, { 182 + encoding: blobRes.headers['content-type'], 183 + }); 184 + if (uploadedBlobs % 10 === 0) { 185 + safeStatusUpdate(statusUpdateHandler, `Migrating blobs: ${uploadedBlobs}/${uploadedBlobs}`); 186 + } 187 + uploadedBlobs++; 188 + } catch (error) { 189 + //TODO silently logging for now will do a missing blobs later 190 + console.error(error); 191 + this.missingBlobs.push(cid); 192 + } 193 + } 194 + missingBlobCursor = missingBlobs.data.cursor; 195 + } while (missingBlobCursor); 196 + 197 + } 153 198 154 199 const prefs = await oldAgent.app.bsky.actor.getPreferences(); 155 200 await newAgent.app.bsky.actor.putPreferences(prefs.data);