this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 105 lines 2.9 kB view raw
1import { resolveIdentity } from "./identity.ts"; 2import { PROFILE_NSID, validateProfile } from "./lexicons.ts"; 3import { getRecordPublic, listRecordsPublic } from "./pds.ts"; 4import { getProfileByDid, upsertProfile } from "./registry.ts"; 5 6interface ProfileRecordEnvelope { 7 uri: string; 8 cid: string; 9 value: unknown; 10 rkey: string; 11} 12 13function rkeyFromAtUri(uri: string): string | null { 14 const parts = uri.split("/"); 15 return parts.length > 0 ? parts[parts.length - 1] || null : null; 16} 17 18export async function upsertProfileFromRecord(input: { 19 did: string; 20 handle: string; 21 pdsUrl: string; 22 record: ProfileRecordEnvelope; 23 recordRev: string; 24}): Promise<boolean> { 25 const validation = validateProfile(input.record.value); 26 if (!validation.ok || !validation.value) { 27 console.warn( 28 `[profile-sync] invalid profile from ${input.did}/${input.record.rkey}: ${validation.error}`, 29 ); 30 return false; 31 } 32 33 const r = validation.value; 34 await upsertProfile({ 35 did: input.did, 36 handle: input.handle, 37 profileType: r.profileType, 38 name: r.name, 39 description: r.description, 40 mainLink: r.mainLink ?? null, 41 iosLink: r.iosLink ?? null, 42 androidLink: r.androidLink ?? null, 43 categories: r.categories ?? [], 44 subcategories: r.subcategories ?? [], 45 links: r.links ?? [], 46 screenshots: r.screenshots ?? [], 47 avatarCid: r.avatar?.ref.$link ?? null, 48 avatarMime: r.avatar?.mimeType ?? null, 49 iconCid: r.icon?.ref.$link ?? null, 50 iconMime: r.icon?.mimeType ?? null, 51 iconBwCid: r.iconBw?.ref.$link ?? null, 52 iconBwMime: r.iconBw?.mimeType ?? null, 53 pdsUrl: input.pdsUrl, 54 recordCid: input.record.cid, 55 recordRev: input.recordRev, 56 createdAt: Date.parse(r.createdAt) || Date.now(), 57 }); 58 return true; 59} 60 61export async function syncProfileByIdentifier( 62 identifier: string, 63): Promise<boolean> { 64 const identity = await resolveIdentity(identifier); 65 const canonical = await getRecordPublic( 66 identity.pdsUrl, 67 identity.did, 68 PROFILE_NSID, 69 "self", 70 ); 71 72 if (canonical) { 73 return await upsertProfileFromRecord({ 74 did: identity.did, 75 handle: identity.handle, 76 pdsUrl: identity.pdsUrl, 77 record: { ...canonical, rkey: "self" }, 78 recordRev: canonical.cid, 79 }); 80 } 81 82 const listed = await listRecordsPublic( 83 identity.pdsUrl, 84 identity.did, 85 PROFILE_NSID, 86 { 87 limit: 25, 88 reverse: true, 89 }, 90 ); 91 for (const record of listed.records) { 92 const rkey = rkeyFromAtUri(record.uri); 93 if (!rkey) continue; 94 const synced = await upsertProfileFromRecord({ 95 did: identity.did, 96 handle: identity.handle, 97 pdsUrl: identity.pdsUrl, 98 record: { ...record, rkey }, 99 recordRev: record.cid, 100 }); 101 if (synced) return true; 102 } 103 104 return (await getProfileByDid(identity.did).catch(() => null)) !== null; 105}