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 main 101 lines 2.9 kB view raw
1import { 2 CompositeDidDocumentResolver, 3 CompositeHandleResolver, 4 DohJsonHandleResolver, 5 PlcDidDocumentResolver, 6 WebDidDocumentResolver, 7 WellKnownHandleResolver, 8} from '@atcute/identity-resolver' 9 10const handleResolver = new CompositeHandleResolver({ 11 strategy: 'race', 12 methods: { 13 dns: new DohJsonHandleResolver({ 14 dohUrl: 'https://mozilla.cloudflare-dns.com/dns-query', 15 }), 16 http: new WellKnownHandleResolver(), 17 }, 18}) 19 20const docResolver = new CompositeDidDocumentResolver({ 21 methods: { 22 plc: new PlcDidDocumentResolver(), 23 web: new WebDidDocumentResolver(), 24 }, 25}) 26 27/** 28 * Fetches a minidoc from slingshot 29 * 30 * @param identifier {string} 31 * @returns {Promise<{did: string, handle: string, pds: string}>} 32 */ 33async function getMiniDoc(identifier) { 34 const result = await fetch( 35 `https://slingshot.microcosm.blue/xrpc/blue.microcosm.identity.resolveMiniDoc?identifier=${identifier}`, 36 ) 37 if (!result.ok) { 38 throw new Error(`Failed to fetch minidoc: ${result.status} ${result.statusText}`) 39 } 40 return await result.json() 41} 42 43/** 44 * Cleans the handle of @ and some other unicode characters that used to show up when copied from the profile 45 * @param handle {string} 46 * @returns {string} 47 */ 48const cleanHandle = handle => 49 handle 50 .replace('@', '') 51 .trim() 52 .replace(/[\u202A\u202C\u200E\u200F\u2066-\u2069]/g, '') 53 54/** 55 * Convince helper to resolve a handle to a did and then find the PDS url from the did document. 56 * 57 * @param handle 58 * @returns {Promise<{usersDid: string, pds: string}>} 59 */ 60async function handleAndPDSResolver(handle) { 61 try { 62 const { did, handle: _, pds } = await getMiniDoc(handle) 63 return { usersDid: did, pds } 64 } catch (error) { 65 console.error('Failed to load mini doc, trying other routes', error) 66 } 67 68 let usersDid = null 69 if (handle.startsWith('did:')) { 70 usersDid = handle 71 } else { 72 const cleanedHandle = cleanHandle(handle) 73 usersDid = await handleResolver.resolve(cleanedHandle) 74 } 75 const didDoc = await docResolver.resolve(usersDid) 76 77 let pds 78 try { 79 pds = didDoc.service?.filter(s => s.type === 'AtprotoPersonalDataServer')[0].serviceEndpoint 80 } catch (error) { 81 throw new Error('Could not find a PDS in the DID document.') 82 } 83 return { usersDid, pds } 84} 85 86/** 87 * Fetches the DID Web from the .well-known/did.json endpoint of the server. 88 * Legacy and was helpful if the web ui and server are on the same domain, not as useful now 89 * @param baseUrl 90 * @returns {Promise<*>} 91 */ 92async function fetchPDSMooverDIDWeb(baseUrl) { 93 const response = await fetch(`${baseUrl}/.well-known/did.json`) 94 if (!response.ok) { 95 throw new Error(`Failed to fetch DID document: ${response.status}`) 96 } 97 const didDoc = await response.json() 98 return didDoc.id 99} 100 101export { handleResolver, docResolver, cleanHandle, handleAndPDSResolver, fetchPDSMooverDIDWeb }