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 docker buildx build \ 28 --platform linux/arm64,linux/amd64 \ 29 --tag fatfingers23/moover_ui:latest \ 30 - --tag fatfingers23/moover_ui:0.0.8 \ 31 --file Dockerfiles/web-ui.Dockerfile \ 32 --builder desktop-linux \ 33 --push .
··· 27 docker buildx build \ 28 --platform linux/arm64,linux/amd64 \ 29 --tag fatfingers23/moover_ui:latest \ 30 + --tag fatfingers23/moover_ui:0.0.9 \ 31 --file Dockerfiles/web-ui.Dockerfile \ 32 --builder desktop-linux \ 33 --push .
+58 -38
packages/moover/lib/pdsmoover.js
··· 139 ) 140 const newAgent = new AtpAgent({ service: newPdsUrl }) 141 const newHostDesc = await newAgent.com.atproto.server.describeServer() 142 if (this.createNewAccount) { 143 - const newHostWebDid = newHostDesc.data.did 144 145 - safeStatusUpdate(statusUpdateHandler, 'Creating a new account on the new PDS') 146 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 152 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 - ) 173 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') 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 186 } 187 } 188 } 189 safeStatusUpdate(statusUpdateHandler, 'Logging in with the new account') 190 191 await newAgent.login({
··· 139 ) 140 const newAgent = new AtpAgent({ service: newPdsUrl }) 141 const newHostDesc = await newAgent.com.atproto.server.describeServer() 142 + 143 if (this.createNewAccount) { 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 + } 159 160 + if (needToCreateANewAccount) { 161 + const newHostWebDid = newHostDesc.data.did 162 163 + safeStatusUpdate(statusUpdateHandler, 'Creating a new account on the new PDS') 164 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 170 171 + let createAccountRequest = { 172 + did: usersDid, 173 + handle: newHandle, 174 + email: newEmail, 175 + password: password, 176 } 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 + } 205 } 206 } 207 } 208 + 209 safeStatusUpdate(statusUpdateHandler, 'Logging in with the new account') 210 211 await newAgent.login({
+1 -1
packages/moover/package.json
··· 1 { 2 "name": "@pds-moover/moover", 3 - "version": "1.0.7", 4 "description": "Utilities for ATProto PDS migrations and recovery", 5 "repository": { 6 "type": "git",
··· 1 { 2 "name": "@pds-moover/moover", 3 + "version": "1.0.8", 4 "description": "Utilities for ATProto PDS migrations and recovery", 5 "repository": { 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 "@atcute/client": "^4.0.5", 18 "@atcute/lexicons": "^1.2.2", 19 "@pds-moover/lexicons": "^1.0.1", 20 - "@pds-moover/moover": "^1.0.7" 21 }, 22 "devDependencies": { 23 "@eslint/compat": "^1.4.0",
··· 17 "@atcute/client": "^4.0.5", 18 "@atcute/lexicons": "^1.2.2", 19 "@pds-moover/lexicons": "^1.0.1", 20 + "@pds-moover/moover": "^1.0.8" 21 }, 22 "devDependencies": { 23 "@eslint/compat": "^1.4.0",
+5 -5
web-ui/pnpm-lock.yaml
··· 21 specifier: ^1.0.1 22 version: 1.0.1 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)) 26 devDependencies: 27 '@eslint/compat': 28 specifier: ^1.4.0 ··· 509 '@pds-moover/lexicons@1.0.1': 510 resolution: {integrity: sha512-fv5b/DtHM7FEo/JklyF9gdK0ainlb6mWjWrBe6cmSAeg9G/4O2jBlQUOqfOAICY9gOcrCpkOrk9PHgGw//JQ2A==} 511 512 - '@pds-moover/moover@1.0.7': 513 - resolution: {integrity: sha512-sIyNejsVFMFqg0SyLMQGTde/4kMF+HyitDRPQn7nIgQdctPi2Nzm1pPeVfec2WeAiquXAYdKDWjawolqrKSPSw==} 514 515 '@polka/url@1.0.0-next.29': 516 resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} ··· 1833 '@atproto/lexicon': 0.5.1 1834 '@atproto/xrpc': 0.7.5 1835 1836 - '@pds-moover/moover@1.0.7(@atcute/identity@1.1.1)(vite@7.1.12(@types/node@22.19.0))': 1837 dependencies: 1838 '@atcute/cbor': 2.3.2 1839 '@atcute/client': 4.0.5
··· 21 specifier: ^1.0.1 22 version: 1.0.1 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)) 26 devDependencies: 27 '@eslint/compat': 28 specifier: ^1.4.0 ··· 509 '@pds-moover/lexicons@1.0.1': 510 resolution: {integrity: sha512-fv5b/DtHM7FEo/JklyF9gdK0ainlb6mWjWrBe6cmSAeg9G/4O2jBlQUOqfOAICY9gOcrCpkOrk9PHgGw//JQ2A==} 511 512 + '@pds-moover/moover@1.0.8': 513 + resolution: {integrity: sha512-Fs9yIaj5jArHeMy2DxyIwkAjoXKJ7GUlXGbwNUsPeRSjTVFouzyRK35HrhtIq0dZ+q02k0I97PVFg5lVTSeI/A==} 514 515 '@polka/url@1.0.0-next.29': 516 resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} ··· 1833 '@atproto/lexicon': 0.5.1 1834 '@atproto/xrpc': 0.7.5 1835 1836 + '@pds-moover/moover@1.0.8(@atcute/identity@1.1.1)(vite@7.1.12(@types/node@22.19.0))': 1837 dependencies: 1838 '@atcute/cbor': 2.3.2 1839 '@atcute/client': 4.0.5
+1 -1
web-ui/src/routes/moover/[[pds]]/+page.svelte
··· 408 {#if errorMessage !== null} 409 <div class="error-message">{errorMessage}</div> 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> 412 413 {/if} 414
··· 408 {#if errorMessage !== null} 409 <div class="error-message">{errorMessage}</div> 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 and entering the same information as before, it will not harm your account.</div> 412 413 {/if} 414