fix: query all 3 Nostr relays in parallel, pick latest profile (BUG-F8)

fetchNostrProfile now uses Promise.allSettled to query all relays
concurrently. Aggregates results with latest-created_at-wins strategy
instead of stopping at the first relay that responds.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Dorian
2026-03-13 00:10:11 +00:00
co-authored by Claude Opus 4.6
parent c288b23c13
commit 8b72bef22e
+26 -16
View File
@@ -581,26 +581,33 @@ async function fetchNostrProfilePic(pk: string): Promise<string | null> {
return profile?.picture || null return profile?.picture || null
} }
/** Fetch full Nostr profile (kind:0 metadata) with caching */ /** Fetch full Nostr profile (kind:0 metadata) with caching.
* Queries all relays in parallel, picks result with latest created_at. */
async function fetchNostrProfile(pk: string): Promise<NostrProfile | null> { async function fetchNostrProfile(pk: string): Promise<NostrProfile | null> {
const cached = profileCache.get(pk) const cached = profileCache.get(pk)
if (cached && Date.now() - cached.ts < PROFILE_TTL) return cached.data if (cached && Date.now() - cached.ts < PROFILE_TTL) return cached.data
for (const relay of RELAYS) { const results = await Promise.allSettled(
try { RELAYS.map(relay => queryRelayProfile(relay, pk))
const profile = await queryRelayProfile(relay, pk) )
if (profile) {
profileCache.set(pk, { data: profile, ts: Date.now() }) // Pick the profile with the latest created_at (newest wins)
return profile let best: { profile: NostrProfile; createdAt: number } | null = null
} for (const r of results) {
} catch { if (r.status !== 'fulfilled' || !r.value) continue
continue if (!best || r.value.createdAt > best.createdAt) {
best = r.value
} }
} }
if (best) {
profileCache.set(pk, { data: best.profile, ts: Date.now() })
return best.profile
}
return null return null
} }
function queryRelayProfile(url: string, pk: string): Promise<NostrProfile | null> { function queryRelayProfile(url: string, pk: string): Promise<{ profile: NostrProfile; createdAt: number } | null> {
return new Promise((resolve) => { return new Promise((resolve) => {
let timedOut = false let timedOut = false
const timeout = setTimeout(() => { const timeout = setTimeout(() => {
@@ -625,11 +632,14 @@ function queryRelayProfile(url: string, pk: string): Promise<NostrProfile | null
clearTimeout(timeout) clearTimeout(timeout)
ws.close() ws.close()
resolve({ resolve({
displayName: meta.display_name || meta.name || null, profile: {
about: meta.about || null, displayName: meta.display_name || meta.name || null,
picture: meta.picture || null, about: meta.about || null,
banner: meta.banner || null, picture: meta.picture || null,
nip05: meta.nip05 || null, banner: meta.banner || null,
nip05: meta.nip05 || null,
},
createdAt: data[2].created_at || 0,
}) })
} else if (data[0] === 'EOSE') { } else if (data[0] === 'EOSE') {
clearTimeout(timeout) clearTimeout(timeout)