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

better repo check for those invite only PDSs

+68 -48
+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.8 \ 30 + --tag fatfingers23/moover_ui:0.0.9 \ 31 31 --file Dockerfiles/web-ui.Dockerfile \ 32 32 --builder desktop-linux \ 33 33 --push .
+58 -38
packages/moover/lib/pdsmoover.js
··· 139 139 ) 140 140 const newAgent = new AtpAgent({ service: newPdsUrl }) 141 141 const newHostDesc = await newAgent.com.atproto.server.describeServer() 142 + 142 143 if (this.createNewAccount) { 143 - const newHostWebDid = newHostDesc.data.did 144 + let needToCreateANewAccount = true 145 + //check to see if repo already exists 146 + try { 147 + // If successful at all means the repo is there 148 + const _ = await newAgent.com.atproto.sync.getRepoStatus({ 149 + did: usersDid, 150 + }) 151 + needToCreateANewAccount = false 152 + // Sets the migrate blobs flag to false so it moves on to just migrate missing blobs in the event of a retry 153 + this.migrateBlobs = false 154 + console.log('New check. Repo already exists, logging in') 155 + } catch (error) { 156 + //Should be good to cont, just logging in case we need it in the future for troubleshooting 157 + console.error('Expected Error on RepoStatus check.', error) 158 + } 144 159 145 - safeStatusUpdate(statusUpdateHandler, 'Creating a new account on the new PDS') 160 + if (needToCreateANewAccount) { 161 + const newHostWebDid = newHostDesc.data.did 146 162 147 - const createAuthResp = await oldAgent.com.atproto.server.getServiceAuth({ 148 - aud: newHostWebDid, 149 - lxm: 'com.atproto.server.createAccount', 150 - }) 151 - const serviceJwt = createAuthResp.data.token 163 + safeStatusUpdate(statusUpdateHandler, 'Creating a new account on the new PDS') 152 164 153 - let createAccountRequest = { 154 - did: usersDid, 155 - handle: newHandle, 156 - email: newEmail, 157 - password: password, 158 - } 159 - if (inviteCode) { 160 - createAccountRequest.inviteCode = inviteCode 161 - } 162 - if (verificationCode) { 163 - createAccountRequest.verificationCode = verificationCode 164 - } 165 - try { 166 - const createNewAccount = await newAgent.com.atproto.server.createAccount( 167 - createAccountRequest, 168 - { 169 - headers: { authorization: `Bearer ${serviceJwt}` }, 170 - encoding: 'application/json', 171 - }, 172 - ) 165 + const createAuthResp = await oldAgent.com.atproto.server.getServiceAuth({ 166 + aud: newHostWebDid, 167 + lxm: 'com.atproto.server.createAccount', 168 + }) 169 + const serviceJwt = createAuthResp.data.token 173 170 174 - if (createNewAccount.data.did !== usersDid.toString()) { 175 - throw new Error('Did not create the new account with the same did as the old account') 171 + let createAccountRequest = { 172 + did: usersDid, 173 + handle: newHandle, 174 + email: newEmail, 175 + password: password, 176 176 } 177 - } catch (error) { 178 - // Ideally should catch if the repo already exists, and if so silently log it and move along to the next step 179 - if (error?.error === 'AlreadyExists') { 180 - // Sets the migrate blobs flag to false so it moves on to just migrate missing blobs in the event of a retry 181 - this.migrateBlobs = false 182 - console.log('Repo already exists, logging in') 183 - } else { 184 - // Catches any other error and stops the migration process 185 - throw error 177 + if (inviteCode) { 178 + createAccountRequest.inviteCode = inviteCode 179 + } 180 + if (verificationCode) { 181 + createAccountRequest.verificationCode = verificationCode 182 + } 183 + try { 184 + const createNewAccount = await newAgent.com.atproto.server.createAccount( 185 + createAccountRequest, 186 + { 187 + headers: { authorization: `Bearer ${serviceJwt}` }, 188 + encoding: 'application/json', 189 + }, 190 + ) 191 + 192 + if (createNewAccount.data.did !== usersDid.toString()) { 193 + throw new Error('Did not create the new account with the same did as the old account') 194 + } 195 + } catch (error) { 196 + // Ideally should catch if the repo already exists, and if so silently log it and move along to the next step 197 + if (error?.error === 'AlreadyExists') { 198 + // Sets the migrate blobs flag to false so it moves on to just migrate missing blobs in the event of a retry 199 + this.migrateBlobs = false 200 + console.log('Repo already exists, logging in') 201 + } else { 202 + // Catches any other error and stops the migration process 203 + throw error 204 + } 186 205 } 187 206 } 188 207 } 208 + 189 209 safeStatusUpdate(statusUpdateHandler, 'Logging in with the new account') 190 210 191 211 await newAgent.login({
+1 -1
packages/moover/package.json
··· 1 1 { 2 2 "name": "@pds-moover/moover", 3 - "version": "1.0.7", 3 + "version": "1.0.8", 4 4 "description": "Utilities for ATProto PDS migrations and recovery", 5 5 "repository": { 6 6 "type": "git",
+1 -1
packages/moover/types/pdsmoover.d.ts.map
··· 1 - {"version":3,"file":"pdsmoover.d.ts","sourceRoot":"","sources":["../lib/pdsmoover.js"],"names":[],"mappings":"AASA;;;GAGG;AACH;IAEI,uBAAuB;IACvB,UADW,QAAQ,CACC;IACpB,uBAAuB;IACvB,UADW,QAAQ,CACC;IACpB,uBAAuB;IACvB,cADW,CAAC,MAAM,CAAC,CACG;IAEtB,sBAAsB;IACtB,kBADW,OAAO,CACU;IAC5B,sBAAsB;IACtB,aADW,OAAO,CACK;IACvB,sBAAsB;IACtB,cADW,OAAO,CACM;IACxB,sBAAsB;IACtB,qBADW,OAAO,CACa;IAC/B,sBAAsB;IACtB,cADW,OAAO,CACM;IACxB,sBAAsB;IACtB,kBADW,OAAO,CACU;IAC5B;;sBAEkB;IAClB,oBADQ,MAAM,CACa;IAG7B;;;;;;;;OAQG;IACH,sBAPW,QAAQ,YACR,QAAQ,YACR,MAAM,QACN,CAAC,MAAM,CAAC,cACR,MAAM,uBACN,WAAS,IAAI,iBAuBvB;IAED;;;;;;;;;;;;;;;OAeG;IACH,mBAVW,MAAM,YACN,MAAM,aACN,MAAM,YACN,MAAM,aACN,MAAM,cACN,MAAM,GAAC,IAAI,wBACX,WAAS,IAAI,kBACb,MAAM,GAAC,IAAI,yCA+MrB;IAED;;;;;OAKG;IACH,wBAJW,MAAM,gCACsB,MAAM,EAAE,GAClC,OAAO,CAAC,IAAI,CAAC,CA2BzB;IAED;;;;;;;;;;;OAWG;IACH,gCAPqB,MAAM,eACJ,MAAM,wBAClB,WAAS,IAAI,kBAEb,MAAM,GAAC,IAAI,GACT,OAAO,CAAC,IAAI,CAAC,CA+EzB;IAED;;;;;OAKG;IACH,uCAHW,MAAM,GACJ,OAAO,CAAC,IAAI,CAAC,CAkCzB;CACF;yBAxcwB,cAAc"} 1 + {"version":3,"file":"pdsmoover.d.ts","sourceRoot":"","sources":["../lib/pdsmoover.js"],"names":[],"mappings":"AASA;;;GAGG;AACH;IAEI,uBAAuB;IACvB,UADW,QAAQ,CACC;IACpB,uBAAuB;IACvB,UADW,QAAQ,CACC;IACpB,uBAAuB;IACvB,cADW,CAAC,MAAM,CAAC,CACG;IAEtB,sBAAsB;IACtB,kBADW,OAAO,CACU;IAC5B,sBAAsB;IACtB,aADW,OAAO,CACK;IACvB,sBAAsB;IACtB,cADW,OAAO,CACM;IACxB,sBAAsB;IACtB,qBADW,OAAO,CACa;IAC/B,sBAAsB;IACtB,cADW,OAAO,CACM;IACxB,sBAAsB;IACtB,kBADW,OAAO,CACU;IAC5B;;sBAEkB;IAClB,oBADQ,MAAM,CACa;IAG7B;;;;;;;;OAQG;IACH,sBAPW,QAAQ,YACR,QAAQ,YACR,MAAM,QACN,CAAC,MAAM,CAAC,cACR,MAAM,uBACN,WAAS,IAAI,iBAuBvB;IAED;;;;;;;;;;;;;;;OAeG;IACH,mBAVW,MAAM,YACN,MAAM,aACN,MAAM,YACN,MAAM,aACN,MAAM,cACN,MAAM,GAAC,IAAI,wBACX,WAAS,IAAI,kBACb,MAAM,GAAC,IAAI,yCAmOrB;IAED;;;;;OAKG;IACH,wBAJW,MAAM,gCACsB,MAAM,EAAE,GAClC,OAAO,CAAC,IAAI,CAAC,CA2BzB;IAED;;;;;;;;;;;OAWG;IACH,gCAPqB,MAAM,eACJ,MAAM,wBAClB,WAAS,IAAI,kBAEb,MAAM,GAAC,IAAI,GACT,OAAO,CAAC,IAAI,CAAC,CA+EzB;IAED;;;;;OAKG;IACH,uCAHW,MAAM,GACJ,OAAO,CAAC,IAAI,CAAC,CAkCzB;CACF;yBA5dwB,cAAc"}
+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.7" 20 + "@pds-moover/moover": "^1.0.8" 21 21 }, 22 22 "devDependencies": { 23 23 "@eslint/compat": "^1.4.0",
+5 -5
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.7 25 - version: 1.0.7(@atcute/identity@1.1.1)(vite@7.1.12(@types/node@22.19.0)) 24 + specifier: ^1.0.8 25 + version: 1.0.8(@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 ··· 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.7': 513 - resolution: {integrity: sha512-sIyNejsVFMFqg0SyLMQGTde/4kMF+HyitDRPQn7nIgQdctPi2Nzm1pPeVfec2WeAiquXAYdKDWjawolqrKSPSw==} 512 + '@pds-moover/moover@1.0.8': 513 + resolution: {integrity: sha512-Fs9yIaj5jArHeMy2DxyIwkAjoXKJ7GUlXGbwNUsPeRSjTVFouzyRK35HrhtIq0dZ+q02k0I97PVFg5lVTSeI/A==} 514 514 515 515 '@polka/url@1.0.0-next.29': 516 516 resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} ··· 1833 1833 '@atproto/lexicon': 0.5.1 1834 1834 '@atproto/xrpc': 0.7.5 1835 1835 1836 - '@pds-moover/moover@1.0.7(@atcute/identity@1.1.1)(vite@7.1.12(@types/node@22.19.0))': 1836 + '@pds-moover/moover@1.0.8(@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
+1 -1
web-ui/src/routes/moover/[[pds]]/+page.svelte
··· 408 408 {#if errorMessage !== null} 409 409 <div class="error-message">{errorMessage}</div> 410 410 411 - <div id="status-message" class="status-message">A error has occurred. Please take a screenshot of this screen for support. You can also retry by refreshing the page, it will not harm your account.</div> 411 + <div id="status-message" class="status-message">A error has occurred. Please take a screenshot of this screen for support. You can also retry by refreshing the page and entering the same information as before, it will not harm your account.</div> 412 412 413 413 {/if} 414 414