|
|
|
@ -36,6 +36,17 @@ import { |
|
|
|
} from '@/constants' |
|
|
|
} from '@/constants' |
|
|
|
|
|
|
|
|
|
|
|
import { getCacheRelayUrls } from '@/lib/private-relays' |
|
|
|
import { getCacheRelayUrls } from '@/lib/private-relays' |
|
|
|
|
|
|
|
import { |
|
|
|
|
|
|
|
collectReadInboxUrlsFromRelayList, |
|
|
|
|
|
|
|
collectRemoteReadInboxUrlsFromRelayList, |
|
|
|
|
|
|
|
collectUserReadInboxUrls, |
|
|
|
|
|
|
|
collectViewerReadInboxUrls |
|
|
|
|
|
|
|
} from '@/lib/viewer-read-inboxes' |
|
|
|
|
|
|
|
import { |
|
|
|
|
|
|
|
collectUserWriteOutboxUrls, |
|
|
|
|
|
|
|
collectViewerWriteOutboxUrls, |
|
|
|
|
|
|
|
collectWriteOutboxUrlsFromRelayList |
|
|
|
|
|
|
|
} from '@/lib/viewer-write-outboxes' |
|
|
|
import { |
|
|
|
import { |
|
|
|
buildPersonalRelayKeySet, |
|
|
|
buildPersonalRelayKeySet, |
|
|
|
sanitizeRelayUrlsForFetch, |
|
|
|
sanitizeRelayUrlsForFetch, |
|
|
|
@ -141,8 +152,7 @@ import { filterRelaysForEventPublish } from '@/lib/relay-publish-filter' |
|
|
|
import { getPaymentAttestationTargetId } from '@/lib/superchat' |
|
|
|
import { getPaymentAttestationTargetId } from '@/lib/superchat' |
|
|
|
import { |
|
|
|
import { |
|
|
|
buildPublicMessagePublishRelayUrls, |
|
|
|
buildPublicMessagePublishRelayUrls, |
|
|
|
collectRecipientInboxUrls, |
|
|
|
collectRecipientInboxUrls |
|
|
|
collectSenderOutboxUrls |
|
|
|
|
|
|
|
} from '@/lib/public-message-publish-relays' |
|
|
|
} from '@/lib/public-message-publish-relays' |
|
|
|
import { buildPrioritizedWriteRelayUrls, dedupeNormalizeRelayUrlsOrdered } from '@/lib/relay-url-priority' |
|
|
|
import { buildPrioritizedWriteRelayUrls, dedupeNormalizeRelayUrlsOrdered } from '@/lib/relay-url-priority' |
|
|
|
import { |
|
|
|
import { |
|
|
|
@ -173,6 +183,7 @@ import { |
|
|
|
isWebsocketUrl, |
|
|
|
isWebsocketUrl, |
|
|
|
normalizeAnyRelayUrl, |
|
|
|
normalizeAnyRelayUrl, |
|
|
|
normalizeHttpRelayUrl, |
|
|
|
normalizeHttpRelayUrl, |
|
|
|
|
|
|
|
normalizeRelayUrlByScheme, |
|
|
|
normalizeUrl, |
|
|
|
normalizeUrl, |
|
|
|
simplifyUrl, |
|
|
|
simplifyUrl, |
|
|
|
urlMatchesConfiguredHttpIndexRelay |
|
|
|
urlMatchesConfiguredHttpIndexRelay |
|
|
|
@ -626,12 +637,11 @@ class ClientService extends EventTarget { |
|
|
|
const extra: string[] = [] |
|
|
|
const extra: string[] = [] |
|
|
|
if (pubkey) { |
|
|
|
if (pubkey) { |
|
|
|
const rl = await this.peekRelayListFromStorage(pubkey) |
|
|
|
const rl = await this.peekRelayListFromStorage(pubkey) |
|
|
|
extra.push( |
|
|
|
const [readInboxes, writeOutboxes] = await Promise.all([ |
|
|
|
...(rl.read ?? []), |
|
|
|
collectViewerReadInboxUrls(pubkey, rl), |
|
|
|
...(rl.write ?? []), |
|
|
|
collectViewerWriteOutboxUrls(pubkey, rl) |
|
|
|
...(rl.httpRead ?? []), |
|
|
|
]) |
|
|
|
...(rl.httpWrite ?? []) |
|
|
|
extra.push(...readInboxes, ...writeOutboxes) |
|
|
|
) |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
await preloadGifsIntoIdbCache(pubkey, extra) |
|
|
|
await preloadGifsIntoIdbCache(pubkey, extra) |
|
|
|
} |
|
|
|
} |
|
|
|
@ -752,6 +762,35 @@ class ClientService extends EventTarget { |
|
|
|
return { all, httpIndexBases, cacheRelayEvent } |
|
|
|
return { all, httpIndexBases, cacheRelayEvent } |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* Kind 10243 bases used to route publish targets through the HTTP index API (not WebSocket). |
|
|
|
|
|
|
|
* Refreshes from IndexedDB when the batch includes https targets so reactions/posts work right |
|
|
|
|
|
|
|
* after saving kind 10243 without waiting for a full account re-sync. |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
private async resolveViewerHttpIndexBasesForPublish( |
|
|
|
|
|
|
|
publishTargetUrls: readonly string[] |
|
|
|
|
|
|
|
): Promise<string[]> { |
|
|
|
|
|
|
|
const hasHttpTarget = publishTargetUrls.some((u) => isKind10243HttpRelayTagUrl(u.trim())) |
|
|
|
|
|
|
|
if (!hasHttpTarget) return this.viewerHttpIndexRelayBases |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const pk = this.pubkey?.trim() |
|
|
|
|
|
|
|
if (!pk) return this.viewerHttpIndexRelayBases |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
try { |
|
|
|
|
|
|
|
const rl = await this.peekRelayListFromStorage(pk) |
|
|
|
|
|
|
|
const fresh = [...(rl.httpRead ?? []), ...(rl.httpWrite ?? [])] |
|
|
|
|
|
|
|
.map((u) => normalizeHttpRelayUrl(u) || u) |
|
|
|
|
|
|
|
.filter(Boolean) |
|
|
|
|
|
|
|
if (fresh.length > 0) { |
|
|
|
|
|
|
|
this.viewerHttpIndexRelayBases = fresh |
|
|
|
|
|
|
|
return fresh |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} catch { |
|
|
|
|
|
|
|
/* keep session cache */ |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return this.viewerHttpIndexRelayBases |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** Kind 10012 + embedded NIP-51 relay sets from IndexedDB only (no network). */ |
|
|
|
/** Kind 10012 + embedded NIP-51 relay sets from IndexedDB only (no network). */ |
|
|
|
async fetchFavoriteRelaysFromStorage(pubkey: string): Promise<string[]> { |
|
|
|
async fetchFavoriteRelaysFromStorage(pubkey: string): Promise<string[]> { |
|
|
|
try { |
|
|
|
try { |
|
|
|
@ -858,48 +897,20 @@ class ClientService extends EventTarget { |
|
|
|
const socialKindBlockedSet = new Set(SOCIAL_KIND_BLOCKED_RELAY_URLS.map((u) => normalizeUrl(u) || u)) |
|
|
|
const socialKindBlockedSet = new Set(SOCIAL_KIND_BLOCKED_RELAY_URLS.map((u) => normalizeUrl(u) || u)) |
|
|
|
return dedupeNormalizeRelayUrlsOrdered( |
|
|
|
return dedupeNormalizeRelayUrlsOrdered( |
|
|
|
filterRelaysForEventPublish(relays, event.kind).filter((url) => { |
|
|
|
filterRelaysForEventPublish(relays, event.kind).filter((url) => { |
|
|
|
const n = normalizeAnyRelayUrl(url) || url |
|
|
|
const n = normalizeRelayUrlByScheme(url) || url |
|
|
|
if (isSocialKindBlockedKind(event.kind) && socialKindBlockedSet.has(n)) return false |
|
|
|
if (isSocialKindBlockedKind(event.kind) && socialKindBlockedSet.has(n)) return false |
|
|
|
return true |
|
|
|
return true |
|
|
|
}) |
|
|
|
}) |
|
|
|
) |
|
|
|
) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* Author kind 0 / 10133 publish: NIP-65 WS outbox + HTTP write (10243) + cache relays (10432). |
|
|
|
|
|
|
|
* {@link fetchRelayList} usually merges cache into `write`; this also appends 10432 tags when missing. |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
private async resolveFullMailboxWriteUrlsForPublish( |
|
|
|
|
|
|
|
pubkey: string, |
|
|
|
|
|
|
|
relayList: TRelayList |
|
|
|
|
|
|
|
): Promise<string[]> { |
|
|
|
|
|
|
|
const ws = (relayList.write ?? []) |
|
|
|
|
|
|
|
.map((u) => normalizeUrl(u) || u) |
|
|
|
|
|
|
|
.filter((u): u is string => !!u) |
|
|
|
|
|
|
|
const http = (relayList.httpWrite ?? []) |
|
|
|
|
|
|
|
.map((u) => normalizeHttpRelayUrl(u) || u) |
|
|
|
|
|
|
|
.filter((u): u is string => !!u) |
|
|
|
|
|
|
|
let merged = dedupeNormalizeRelayUrlsOrdered([...http, ...ws]) |
|
|
|
|
|
|
|
try { |
|
|
|
|
|
|
|
const cache = await getCacheRelayUrls(pubkey) |
|
|
|
|
|
|
|
if (cache.length > 0) { |
|
|
|
|
|
|
|
merged = dedupeNormalizeRelayUrlsOrdered([...merged, ...cache]) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} catch { |
|
|
|
|
|
|
|
/* ignore */ |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return merged |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private relayListHasWriteUrls(relayList: TRelayList): boolean { |
|
|
|
private relayListHasWriteUrls(relayList: TRelayList): boolean { |
|
|
|
return (relayList.write?.length ?? 0) > 0 || (relayList.httpWrite?.length ?? 0) > 0 |
|
|
|
return collectUserWriteOutboxUrls(relayList).length > 0 |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private relayListUsableForInboxOrdering(relayList: TRelayList): boolean { |
|
|
|
private relayListUsableForInboxOrdering(relayList: TRelayList): boolean { |
|
|
|
return ( |
|
|
|
return ( |
|
|
|
this.relayListHasWriteUrls(relayList) || |
|
|
|
this.relayListHasWriteUrls(relayList) || collectUserReadInboxUrls(relayList).length > 0 |
|
|
|
(relayList.read?.length ?? 0) > 0 || |
|
|
|
|
|
|
|
(relayList.httpRead?.length ?? 0) > 0 |
|
|
|
|
|
|
|
) |
|
|
|
) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@ -914,23 +925,12 @@ class ClientService extends EventTarget { |
|
|
|
return this.fetchRelayListWithPublishTimeout(pubkey) |
|
|
|
return this.fetchRelayListWithPublishTimeout(pubkey) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** NIP-65 `write` URLs for `event.pubkey`, filtered for publish (no read-only / social-kind blocks). */ |
|
|
|
/** NIP-65 / 10243 / 10432 write outboxes for `event.pubkey`, filtered for publish. */ |
|
|
|
private async getUserOutboxRelayUrlsForPublish(event: NEvent): Promise<string[]> { |
|
|
|
private async getUserOutboxRelayUrlsForPublish(event: NEvent): Promise<string[]> { |
|
|
|
try { |
|
|
|
try { |
|
|
|
const relayList = await this.peekOrFetchRelayListForPublish(event.pubkey) |
|
|
|
const relayList = await this.peekOrFetchRelayListForPublish(event.pubkey) |
|
|
|
if (!this.relayListHasWriteUrls(relayList)) { |
|
|
|
const raw = await collectViewerWriteOutboxUrls(event.pubkey, relayList) |
|
|
|
return [] |
|
|
|
if (raw.length === 0) return [] |
|
|
|
} |
|
|
|
|
|
|
|
const raw = isAuthorProfileMetadataPublishKind(event.kind) |
|
|
|
|
|
|
|
? await this.resolveFullMailboxWriteUrlsForPublish(event.pubkey, relayList) |
|
|
|
|
|
|
|
: dedupeNormalizeRelayUrlsOrdered([ |
|
|
|
|
|
|
|
...(relayList.httpWrite ?? []) |
|
|
|
|
|
|
|
.map((u) => normalizeHttpRelayUrl(u) || u) |
|
|
|
|
|
|
|
.filter((u): u is string => !!u), |
|
|
|
|
|
|
|
...(relayList.write ?? []) |
|
|
|
|
|
|
|
.map((u) => normalizeUrl(u) || u) |
|
|
|
|
|
|
|
.filter((u): u is string => !!u) |
|
|
|
|
|
|
|
]) |
|
|
|
|
|
|
|
return this.filterPublishingRelays(raw, event) |
|
|
|
return this.filterPublishingRelays(raw, event) |
|
|
|
} catch { |
|
|
|
} catch { |
|
|
|
return [] |
|
|
|
return [] |
|
|
|
@ -942,7 +942,7 @@ class ClientService extends EventTarget { |
|
|
|
userOutboxUrls: string[], |
|
|
|
userOutboxUrls: string[], |
|
|
|
relayStatuses: { url: string; success: boolean; error?: string }[] |
|
|
|
relayStatuses: { url: string; success: boolean; error?: string }[] |
|
|
|
): Promise<void> { |
|
|
|
): Promise<void> { |
|
|
|
const norm = (u: string) => normalizeAnyRelayUrl(u) || u |
|
|
|
const norm = (u: string) => normalizeRelayUrlByScheme(u) || u |
|
|
|
const hadSuccess = new Set<string>() |
|
|
|
const hadSuccess = new Set<string>() |
|
|
|
for (const r of relayStatuses) { |
|
|
|
for (const r of relayStatuses) { |
|
|
|
if (r.success) hadSuccess.add(norm(r.url)) |
|
|
|
if (r.success) hadSuccess.add(norm(r.url)) |
|
|
|
@ -1191,15 +1191,8 @@ class ClientService extends EventTarget { |
|
|
|
} |
|
|
|
} |
|
|
|
// For Report events, always include user's write relays first, then add seen relays if they're write-capable
|
|
|
|
// For Report events, always include user's write relays first, then add seen relays if they're write-capable
|
|
|
|
if (event.kind === kinds.Report) { |
|
|
|
if (event.kind === kinds.Report) { |
|
|
|
// Start with user's write relays (outboxes) - these are the primary targets for reports
|
|
|
|
|
|
|
|
const relayList = await this.fetchRelayListWithPublishTimeout(event.pubkey) |
|
|
|
const relayList = await this.fetchRelayListWithPublishTimeout(event.pubkey) |
|
|
|
const reportHttpWrites = (relayList?.httpWrite ?? []) |
|
|
|
const userWriteRelays = await collectViewerWriteOutboxUrls(event.pubkey, relayList) |
|
|
|
.map((url) => normalizeHttpRelayUrl(url) || url) |
|
|
|
|
|
|
|
.filter((u): u is string => !!u) |
|
|
|
|
|
|
|
const reportWsWrites = (relayList?.write ?? []) |
|
|
|
|
|
|
|
.map((url) => normalizeUrl(url) || url) |
|
|
|
|
|
|
|
.filter((u): u is string => !!u) |
|
|
|
|
|
|
|
const userWriteRelays = dedupeNormalizeRelayUrlsOrdered([...reportHttpWrites, ...reportWsWrites]) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Get seen relays where the reported event was found
|
|
|
|
// Get seen relays where the reported event was found
|
|
|
|
const targetEventId = event.tags.find(tagNameEquals('e'))?.[1] |
|
|
|
const targetEventId = event.tags.find(tagNameEquals('e'))?.[1] |
|
|
|
@ -1209,9 +1202,9 @@ class ClientService extends EventTarget { |
|
|
|
const allSeenRelays = this.getSeenEventRelayUrls(targetEventId) |
|
|
|
const allSeenRelays = this.getSeenEventRelayUrls(targetEventId) |
|
|
|
// Filter seen relays: only include those that are in user's write list
|
|
|
|
// Filter seen relays: only include those that are in user's write list
|
|
|
|
// This ensures we don't try to publish to read-only relays
|
|
|
|
// This ensures we don't try to publish to read-only relays
|
|
|
|
const userWriteRelaySet = new Set(userWriteRelays.map(url => normalizeAnyRelayUrl(url) || url)) |
|
|
|
const userWriteRelaySet = new Set(userWriteRelays.map((url) => normalizeRelayUrlByScheme(url) || url)) |
|
|
|
seenRelays.push(...allSeenRelays.filter(url => { |
|
|
|
seenRelays.push(...allSeenRelays.filter(url => { |
|
|
|
const normalized = normalizeAnyRelayUrl(url) || url |
|
|
|
const normalized = normalizeRelayUrlByScheme(url) || url |
|
|
|
return userWriteRelaySet.has(normalized) |
|
|
|
return userWriteRelaySet.has(normalized) |
|
|
|
})) |
|
|
|
})) |
|
|
|
} |
|
|
|
} |
|
|
|
@ -1273,7 +1266,7 @@ class ClientService extends EventTarget { |
|
|
|
this.fetchRelayListWithPublishTimeout(event.pubkey), |
|
|
|
this.fetchRelayListWithPublishTimeout(event.pubkey), |
|
|
|
recipientListsPromise |
|
|
|
recipientListsPromise |
|
|
|
]) |
|
|
|
]) |
|
|
|
const authorWrite = collectSenderOutboxUrls(authorRelayList) |
|
|
|
const authorWrite = await collectViewerWriteOutboxUrls(event.pubkey, authorRelayList) |
|
|
|
const recipientRead = dedupeNormalizeRelayUrlsOrdered( |
|
|
|
const recipientRead = dedupeNormalizeRelayUrlsOrdered( |
|
|
|
recipientRelayLists.flatMap((rl) => collectRecipientInboxUrls(rl)) |
|
|
|
recipientRelayLists.flatMap((rl) => collectRecipientInboxUrls(rl)) |
|
|
|
) |
|
|
|
) |
|
|
|
@ -1303,7 +1296,7 @@ class ClientService extends EventTarget { |
|
|
|
? this.fetchRelayListsWithPublishTimeout(senderPubkeys) |
|
|
|
? this.fetchRelayListsWithPublishTimeout(senderPubkeys) |
|
|
|
: Promise.resolve([] as TRelayList[]) |
|
|
|
: Promise.resolve([] as TRelayList[]) |
|
|
|
]) |
|
|
|
]) |
|
|
|
const authorWrite = collectSenderOutboxUrls(authorRelayList) |
|
|
|
const authorWrite = await collectViewerWriteOutboxUrls(event.pubkey, authorRelayList) |
|
|
|
const authorRead = collectRecipientInboxUrls(authorRelayList) |
|
|
|
const authorRead = collectRecipientInboxUrls(authorRelayList) |
|
|
|
const senderInboxes = dedupeNormalizeRelayUrlsOrdered( |
|
|
|
const senderInboxes = dedupeNormalizeRelayUrlsOrdered( |
|
|
|
senderRelayLists.flatMap((rl) => collectRecipientInboxUrls(rl)) |
|
|
|
senderRelayLists.flatMap((rl) => collectRecipientInboxUrls(rl)) |
|
|
|
@ -1351,16 +1344,10 @@ class ClientService extends EventTarget { |
|
|
|
}) |
|
|
|
}) |
|
|
|
spellRelayList = this.emptyRelayListForPublish() |
|
|
|
spellRelayList = this.emptyRelayListForPublish() |
|
|
|
} |
|
|
|
} |
|
|
|
const spellHttpWrites = (spellRelayList?.httpWrite ?? []) |
|
|
|
const spellWriteFilteredRaw = await collectViewerWriteOutboxUrls(event.pubkey, spellRelayList) |
|
|
|
.map((url) => normalizeHttpRelayUrl(url)) |
|
|
|
|
|
|
|
.filter((url): url is string => !!url) |
|
|
|
|
|
|
|
const spellWsWrites = (spellRelayList?.write ?? []) |
|
|
|
|
|
|
|
.map((url) => normalizeUrl(url)) |
|
|
|
|
|
|
|
.filter((url): url is string => !!url) |
|
|
|
|
|
|
|
const normalizedWrite = dedupeNormalizeRelayUrlsOrdered([...spellHttpWrites, ...spellWsWrites]) |
|
|
|
|
|
|
|
const readOnlySet = new Set(READ_ONLY_RELAY_URLS.map((u) => normalizeUrl(u) || u)) |
|
|
|
const readOnlySet = new Set(READ_ONLY_RELAY_URLS.map((u) => normalizeUrl(u) || u)) |
|
|
|
const spellWriteFiltered = normalizedWrite.filter((url) => { |
|
|
|
const spellWriteFiltered = spellWriteFilteredRaw.filter((url) => { |
|
|
|
const n = normalizeAnyRelayUrl(url) || url |
|
|
|
const n = normalizeRelayUrlByScheme(url) || url |
|
|
|
return !readOnlySet.has(n) |
|
|
|
return !readOnlySet.has(n) |
|
|
|
}) |
|
|
|
}) |
|
|
|
return this.filterPublishingRelays( |
|
|
|
return this.filterPublishingRelays( |
|
|
|
@ -1395,14 +1382,7 @@ class ClientService extends EventTarget { |
|
|
|
const relayListPromise = this.fetchRelayListWithPublishTimeout(event.pubkey) |
|
|
|
const relayListPromise = this.fetchRelayListWithPublishTimeout(event.pubkey) |
|
|
|
const [relayLists, relayList] = await Promise.all([relayListsPromise, relayListPromise]) |
|
|
|
const [relayLists, relayList] = await Promise.all([relayListsPromise, relayListPromise]) |
|
|
|
relayLists.forEach((rl) => { |
|
|
|
relayLists.forEach((rl) => { |
|
|
|
for (const u of rl.httpRead ?? []) { |
|
|
|
authorInboxFromContext.push(...collectRemoteReadInboxUrlsFromRelayList(rl)) |
|
|
|
const n = normalizeHttpRelayUrl(u) || u |
|
|
|
|
|
|
|
if (n) authorInboxFromContext.push(n) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
for (const u of rl.read ?? []) { |
|
|
|
|
|
|
|
const n = normalizeUrl(u) || u |
|
|
|
|
|
|
|
if (n) authorInboxFromContext.push(n) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}) |
|
|
|
}) |
|
|
|
if ( |
|
|
|
if ( |
|
|
|
isAuthorProfileMetadataPublishKind(event.kind) || |
|
|
|
isAuthorProfileMetadataPublishKind(event.kind) || |
|
|
|
@ -1461,15 +1441,10 @@ class ClientService extends EventTarget { |
|
|
|
writeRelays: relayList?.write?.slice(0, MAX_PUBLISH_RELAYS) ?? [] |
|
|
|
writeRelays: relayList?.write?.slice(0, MAX_PUBLISH_RELAYS) ?? [] |
|
|
|
}) |
|
|
|
}) |
|
|
|
} |
|
|
|
} |
|
|
|
const wsWrites = (relayList?.write ?? []) |
|
|
|
const userWritesOrdered = await collectViewerWriteOutboxUrls( |
|
|
|
.map((u) => normalizeUrl(u) || u) |
|
|
|
event.pubkey, |
|
|
|
.filter((u): u is string => !!u) |
|
|
|
relayList ?? this.emptyRelayListForPublish() |
|
|
|
const httpWrites = (relayList?.httpWrite ?? []) |
|
|
|
) |
|
|
|
.map((u) => normalizeHttpRelayUrl(u) || u) |
|
|
|
|
|
|
|
.filter((u): u is string => !!u) |
|
|
|
|
|
|
|
const userWritesOrdered = isAuthorProfileMetadataPublishKind(event.kind) |
|
|
|
|
|
|
|
? await this.resolveFullMailboxWriteUrlsForPublish(event.pubkey, relayList ?? this.emptyRelayListForPublish()) |
|
|
|
|
|
|
|
: dedupeNormalizeRelayUrlsOrdered([...httpWrites, ...wsWrites]) |
|
|
|
|
|
|
|
relays = this.filterPublishingRelays( |
|
|
|
relays = this.filterPublishingRelays( |
|
|
|
buildPrioritizedWriteRelayUrls({ |
|
|
|
buildPrioritizedWriteRelayUrls({ |
|
|
|
userWriteRelays: userWritesOrdered, |
|
|
|
userWriteRelays: userWritesOrdered, |
|
|
|
@ -1668,7 +1643,7 @@ class ClientService extends EventTarget { |
|
|
|
|
|
|
|
|
|
|
|
const socialKindBlockedSet = new Set(SOCIAL_KIND_BLOCKED_RELAY_URLS.map((u) => normalizeUrl(u) || u)) |
|
|
|
const socialKindBlockedSet = new Set(SOCIAL_KIND_BLOCKED_RELAY_URLS.map((u) => normalizeUrl(u) || u)) |
|
|
|
let filtered = filterRelaysForEventPublish(mergedRelayUrls, event.kind).filter((url) => { |
|
|
|
let filtered = filterRelaysForEventPublish(mergedRelayUrls, event.kind).filter((url) => { |
|
|
|
const n = normalizeAnyRelayUrl(url) || url |
|
|
|
const n = normalizeRelayUrlByScheme(url) || url |
|
|
|
if (isSocialKindBlockedKind(event.kind) && socialKindBlockedSet.has(n)) return false |
|
|
|
if (isSocialKindBlockedKind(event.kind) && socialKindBlockedSet.has(n)) return false |
|
|
|
return true |
|
|
|
return true |
|
|
|
}) |
|
|
|
}) |
|
|
|
@ -1762,6 +1737,8 @@ class ClientService extends EventTarget { |
|
|
|
const publishOpBatch = new RelayPublishOpBatch(publishBatchSource, event.id, publishTargetUrls) |
|
|
|
const publishOpBatch = new RelayPublishOpBatch(publishBatchSource, event.id, publishTargetUrls) |
|
|
|
publishOpBatch.logBegin() |
|
|
|
publishOpBatch.logBegin() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const httpIndexBasesForPublish = await this.resolveViewerHttpIndexBasesForPublish(publishTargetUrls) |
|
|
|
|
|
|
|
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
|
|
const client = this |
|
|
|
const client = this |
|
|
|
return new Promise<{ success: boolean; relayStatuses: typeof relayStatuses; successCount: number; totalCount: number }>((resolve) => { |
|
|
|
return new Promise<{ success: boolean; relayStatuses: typeof relayStatuses; successCount: number; totalCount: number }>((resolve) => { |
|
|
|
@ -1894,7 +1871,7 @@ class ClientService extends EventTarget { |
|
|
|
}, connectionTimeout + publishAckBudgetMs + 2_000) |
|
|
|
}, connectionTimeout + publishAckBudgetMs + 2_000) |
|
|
|
|
|
|
|
|
|
|
|
try { |
|
|
|
try { |
|
|
|
if (urlMatchesConfiguredHttpIndexRelay(url, this.viewerHttpIndexRelayBases)) { |
|
|
|
if (urlMatchesConfiguredHttpIndexRelay(url, httpIndexBasesForPublish)) { |
|
|
|
const base = normalizeHttpRelayUrl(url) || url |
|
|
|
const base = normalizeHttpRelayUrl(url) || url |
|
|
|
logger.debug(`[PublishEvent] Publishing to kind 10243 HTTP index relay`, { url: base }) |
|
|
|
logger.debug(`[PublishEvent] Publishing to kind 10243 HTTP index relay`, { url: base }) |
|
|
|
await Promise.race([ |
|
|
|
await Promise.race([ |
|
|
|
@ -2033,7 +2010,7 @@ class ClientService extends EventTarget { |
|
|
|
} |
|
|
|
} |
|
|
|
} catch (error) { |
|
|
|
} catch (error) { |
|
|
|
const softHttpDown = |
|
|
|
const softHttpDown = |
|
|
|
urlMatchesConfiguredHttpIndexRelay(url, this.viewerHttpIndexRelayBases) && |
|
|
|
urlMatchesConfiguredHttpIndexRelay(url, httpIndexBasesForPublish) && |
|
|
|
(error instanceof IndexRelayTransportError || isIndexRelayTransportFailure(error)) |
|
|
|
(error instanceof IndexRelayTransportError || isIndexRelayTransportFailure(error)) |
|
|
|
if (softHttpDown) { |
|
|
|
if (softHttpDown) { |
|
|
|
logger.debug('[PublishEvent] HTTP index relay unreachable', { |
|
|
|
logger.debug('[PublishEvent] HTTP index relay unreachable', { |
|
|
|
@ -4551,9 +4528,7 @@ class ClientService extends EventTarget { |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
async getMailboxStackWriteUrlsForRepublish(pubkey: string): Promise<string[]> { |
|
|
|
async getMailboxStackWriteUrlsForRepublish(pubkey: string): Promise<string[]> { |
|
|
|
const rl = await this.peekRelayListFromStorage(pubkey) |
|
|
|
const rl = await this.peekRelayListFromStorage(pubkey) |
|
|
|
const ws = (rl.write ?? []).map((u) => normalizeUrl(u) || u).filter((u): u is string => !!u) |
|
|
|
return collectViewerWriteOutboxUrls(pubkey, rl) |
|
|
|
const http = (rl.httpWrite ?? []).map((u) => normalizeHttpRelayUrl(u) || u).filter((u): u is string => !!u) |
|
|
|
|
|
|
|
return dedupeNormalizeRelayUrlsOrdered([...http, ...ws]) |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** Newest kind 10002 for `pubkey` from IndexedDB and/or session LRU (session may hold a copy not persisted yet). */ |
|
|
|
/** Newest kind 10002 for `pubkey` from IndexedDB and/or session LRU (session may hold a copy not persisted yet). */ |
|
|
|
@ -5069,8 +5044,8 @@ class ClientService extends EventTarget { |
|
|
|
if (!/^[0-9a-f]{64}$/.test(pk)) return [] |
|
|
|
if (!/^[0-9a-f]{64}$/.test(pk)) return [] |
|
|
|
const relayList = await this.fetchRelayList(pk) |
|
|
|
const relayList = await this.fetchRelayList(pk) |
|
|
|
const urls = dedupeNormalizeRelayUrlsOrdered([ |
|
|
|
const urls = dedupeNormalizeRelayUrlsOrdered([ |
|
|
|
...relayList.write.map((u) => normalizeUrl(u) || u), |
|
|
|
...collectWriteOutboxUrlsFromRelayList(relayList), |
|
|
|
...relayList.read.map((u) => normalizeUrl(u) || u), |
|
|
|
...collectReadInboxUrlsFromRelayList(relayList), |
|
|
|
...FAST_READ_RELAY_URLS.map((u) => normalizeUrl(u) || u), |
|
|
|
...FAST_READ_RELAY_URLS.map((u) => normalizeUrl(u) || u), |
|
|
|
...PROFILE_RELAY_URLS.map((u) => normalizeUrl(u) || u) |
|
|
|
...PROFILE_RELAY_URLS.map((u) => normalizeUrl(u) || u) |
|
|
|
]).filter(Boolean) |
|
|
|
]).filter(Boolean) |
|
|
|
@ -5127,7 +5102,9 @@ class ClientService extends EventTarget { |
|
|
|
let urls = [...publicReadRelayFallbackUrls()] |
|
|
|
let urls = [...publicReadRelayFallbackUrls()] |
|
|
|
if (myPubkey) { |
|
|
|
if (myPubkey) { |
|
|
|
const relayList = await this.fetchRelayList(myPubkey) |
|
|
|
const relayList = await this.fetchRelayList(myPubkey) |
|
|
|
urls = relayList.read.concat([...publicReadRelayFallbackUrls()]).slice(0, 5) |
|
|
|
urls = collectReadInboxUrlsFromRelayList(relayList) |
|
|
|
|
|
|
|
.concat([...publicReadRelayFallbackUrls()]) |
|
|
|
|
|
|
|
.slice(0, 5) |
|
|
|
} |
|
|
|
} |
|
|
|
return [{ urls, filter: { authors: pubkeys } }] |
|
|
|
return [{ urls, filter: { authors: pubkeys } }] |
|
|
|
} |
|
|
|
} |
|
|
|
|