|
| 1 | +import type { MiniDoc, NPMXProfile } from '~~/shared/types/social' |
| 2 | + |
| 3 | +//Cache keys and helpers |
| 4 | +const CACHE_PREFIX = 'atproto-profile:' |
| 5 | +const CACHE_PROFILE_MINI_DOC = (handle: string) => `${CACHE_PREFIX}${handle}:minidoc` |
| 6 | +const CACHE_PROFILE_KEY = (did: string) => `${CACHE_PREFIX}${did}:profile` |
| 7 | + |
| 8 | +const CACHE_MAX_AGE = CACHE_MAX_AGE_ONE_MINUTE * 5 |
| 9 | + |
| 10 | +/** |
| 11 | + * Logic to handle and update profile queries |
| 12 | + */ |
| 13 | +export class ProfileUtils { |
| 14 | + private readonly constellation: Constellation |
| 15 | + private readonly cache: CacheAdapter |
| 16 | + |
| 17 | + constructor() { |
| 18 | + this.constellation = new Constellation( |
| 19 | + // Passes in a fetch wrapped as cachedfetch since are already doing some heavy caching here |
| 20 | + async <T = unknown>( |
| 21 | + url: string, |
| 22 | + options: Parameters<typeof $fetch>[1] = {}, |
| 23 | + _ttl?: number, |
| 24 | + ): Promise<CachedFetchResult<T>> => { |
| 25 | + const data = (await $fetch<T>(url, options)) as T |
| 26 | + return { data, isStale: false, cachedAt: null } |
| 27 | + }, |
| 28 | + ) |
| 29 | + this.cache = getCacheAdapter('generic') |
| 30 | + } |
| 31 | + |
| 32 | + private async slingshotMiniDoc(handle: string) { |
| 33 | + const miniDocKey = CACHE_PROFILE_MINI_DOC(handle) |
| 34 | + const cachedMiniDoc = await this.cache.get<MiniDoc>(miniDocKey) |
| 35 | + |
| 36 | + let miniDoc |
| 37 | + if (cachedMiniDoc) { |
| 38 | + miniDoc = cachedMiniDoc |
| 39 | + } else { |
| 40 | + const resolveUrl = `https://${SLINGSHOT_HOST}/xrpc/blue.microcosm.identity.resolveMiniDoc?identifier=${encodeURIComponent(handle)}` |
| 41 | + console.log({ resolveUrl }) |
| 42 | + const response = await fetch(resolveUrl, { |
| 43 | + headers: { 'User-Agent': 'npmx' }, |
| 44 | + }) |
| 45 | + const value = (await response.json()) as MiniDoc |
| 46 | + |
| 47 | + miniDoc = value |
| 48 | + await this.cache.set(miniDocKey, value, CACHE_MAX_AGE) |
| 49 | + } |
| 50 | + console.log({ miniDoc }) |
| 51 | + |
| 52 | + return miniDoc |
| 53 | + } |
| 54 | + |
| 55 | + /** |
| 56 | + * Gets an npmx profile based on a handle |
| 57 | + * @param handle |
| 58 | + * @returns |
| 59 | + */ |
| 60 | + async getProfile(handle: string) { |
| 61 | + const profileKey = CACHE_PROFILE_KEY(handle) |
| 62 | + const cachedProfile = await this.cache.get<NPMXProfile>(profileKey) |
| 63 | + |
| 64 | + let profile: NPMXProfile | undefined |
| 65 | + if (cachedProfile) { |
| 66 | + profile = cachedProfile |
| 67 | + } else { |
| 68 | + const miniDoc = await this.slingshotMiniDoc(handle) |
| 69 | + const profileUri = `at://${miniDoc.did}/dev.npmx.actor.profile/self` |
| 70 | + const response = await fetch( |
| 71 | + `https://${SLINGSHOT_HOST}/xrpc/blue.microcosm.repo.getRecordByUri?at_uri=${profileUri}`, |
| 72 | + { |
| 73 | + headers: { 'User-Agent': 'npmx' }, |
| 74 | + }, |
| 75 | + ) |
| 76 | + if (response.ok) { |
| 77 | + const { value } = (await response.json()) as { value: NPMXProfile } |
| 78 | + profile = value |
| 79 | + await this.cache.set(profileKey, profile, CACHE_MAX_AGE) |
| 80 | + } |
| 81 | + } |
| 82 | + |
| 83 | + return profile |
| 84 | + } |
| 85 | +} |
0 commit comments