diff --git a/src/components/RelayStatusDisplay/index.tsx b/src/components/RelayStatusDisplay/index.tsx index b91fe77..18e530a 100644 --- a/src/components/RelayStatusDisplay/index.tsx +++ b/src/components/RelayStatusDisplay/index.tsx @@ -1,4 +1,4 @@ -import { Check, X, AlertCircle } from 'lucide-react' +import { Check, X } from 'lucide-react' import { simplifyUrl } from '@/lib/url' interface RelayStatus { @@ -50,10 +50,10 @@ export default function RelayStatusDisplay({ {simplifyUrl(status.url)} - {status.authAttempted && ( -
- -
+ {status.authAttempted && !status.success && ( + + (auth failed) + )} diff --git a/src/services/client.service.ts b/src/services/client.service.ts index b2a47ad..ced50ad 100644 --- a/src/services/client.service.ts +++ b/src/services/client.service.ts @@ -142,9 +142,12 @@ class ClientService extends EventTarget { // Use current user's relay list const relayList = this.pubkey ? await this.fetchRelayList(this.pubkey) : { write: [], read: [] } + console.log('DEBUG: User relay list write URLs:', relayList?.write) const senderWriteRelays = relayList?.write.slice(0, 6) ?? [] + console.log('DEBUG: Selected sender write relays:', senderWriteRelays) const recipientReadRelays = Array.from(new Set(_additionalRelayUrls)) relays = senderWriteRelays.concat(recipientReadRelays) + console.log('DEBUG: Final relay URLs before optimization:', relays) } if (!relays.length) { @@ -238,6 +241,7 @@ class ClientService extends EventTarget { totalCount: number }> { const uniqueRelayUrls = this.optimizeRelaySelection(Array.from(new Set(relayUrls))) + console.log('DEBUG: uniqueRelayUrls after optimization:', uniqueRelayUrls) const relayStatuses: Array<{ url: string @@ -260,13 +264,17 @@ class ClientService extends EventTarget { const checkCompletion = () => { if (resolved) return - // If one third of the relays have accepted the event, consider it a success - const isSuccess = successCount >= Math.max(1, Math.ceil(uniqueRelayUrls.length / 3)) - if (isSuccess && !resolved) { - this.emitNewEvent(event) + // Wait for all relays to complete before resolving (don't complete early) + // This ensures we show the full relay status information + if (finishedCount >= uniqueRelayUrls.length && !resolved) { + const isSuccess = successCount > 0 + if (isSuccess) { + this.emitNewEvent(event) + } resolved = true + console.log('DEBUG: Publishing completed. relayStatuses:', relayStatuses) resolve({ - success: true, + success: isSuccess, relayStatuses, successCount, totalCount: uniqueRelayUrls.length @@ -274,35 +282,26 @@ class ClientService extends EventTarget { return } - if (finishedCount >= uniqueRelayUrls.length && !resolved) { - if (successCount > 0) { - this.emitNewEvent(event) - resolved = true - resolve({ - success: true, - relayStatuses, - successCount, - totalCount: uniqueRelayUrls.length - }) - } else { - resolved = true - const aggregateError = new AggregateError( - errors.map( - ({ url, error }) => { - let errorMsg = 'Unknown error' - if (error instanceof Error) { - errorMsg = error.message || 'Empty error message' - } else if (error !== null && error !== undefined) { - errorMsg = String(error) - } - return new Error(`Failed to publish to ${url}: ${errorMsg}`) + // Handle case where no relays succeed + if (finishedCount >= uniqueRelayUrls.length && !resolved && successCount === 0) { + resolved = true + console.log('DEBUG: All relays failed. relayStatuses:', relayStatuses) + const aggregateError = new AggregateError( + errors.map( + ({ url, error }) => { + let errorMsg = 'Unknown error' + if (error instanceof Error) { + errorMsg = error.message || 'Empty error message' + } else if (error !== null && error !== undefined) { + errorMsg = String(error) } - ) + return new Error(`Failed to publish to ${url}: ${errorMsg}`) + } ) - // Attach relay statuses to the error so they can be displayed - ;(aggregateError as any).relayStatuses = relayStatuses - reject(aggregateError) - } + ) + // Attach relay statuses to the error so they can be displayed + ;(aggregateError as any).relayStatuses = relayStatuses + reject(aggregateError) } } @@ -329,6 +328,7 @@ class ClientService extends EventTarget { // eslint-disable-next-line @typescript-eslint/no-this-alias const that = this + console.log('DEBUG: Attempting to publish to relay:', url) try { // Throttle requests to prevent "too many concurrent REQs" errors await this.throttleRequest(url) @@ -336,7 +336,9 @@ class ClientService extends EventTarget { const relay = await this.pool.ensureRelay(url) relay.publishTimeout = 8_000 // 8s + console.log('DEBUG: Publishing to relay:', url) await relay.publish(event) + console.log('DEBUG: Successfully published to relay:', url) this.trackEventSeenOn(event.id, relay) this.recordSuccess(url) successCount++ @@ -349,6 +351,7 @@ class ClientService extends EventTarget { checkCompletion() } catch (error) { + console.log('DEBUG: Failed to publish to relay:', url, 'Error:', error) let errorMessage = 'Unknown error' if (error instanceof Error) { errorMessage = error.message || 'Empty error message' @@ -384,17 +387,31 @@ class ClientService extends EventTarget { error.message.startsWith('auth-required') && !!that.signer ) { + console.log('DEBUG: Attempting authentication for relay:', url) try { // Throttle auth requests too await this.throttleRequest(url) const relay = await this.pool.ensureRelay(url) - await relay.auth((authEvt: EventTemplate) => { + + // Attempt auth with proper timeout handling + console.log('DEBUG: Starting auth for relay:', url) + + const authPromise = relay.auth((authEvt: EventTemplate) => { // Ensure the auth event has the correct pubkey const authEventWithPubkey = { ...authEvt, pubkey: that.pubkey } return that.signer!.signEvent(authEventWithPubkey) }) + + const authTimeoutPromise = new Promise((_, reject) => { + setTimeout(() => reject(new Error('Auth timeout')), 8000) // 8s timeout + }) + + await Promise.race([authPromise, authTimeoutPromise]) + console.log('DEBUG: Auth successful for relay:', url) + await relay.publish(event) + console.log('DEBUG: Publish successful for relay:', url) this.trackEventSeenOn(event.id, relay) this.recordSuccess(url) successCount++ @@ -408,6 +425,7 @@ class ClientService extends EventTarget { checkCompletion() } catch (authError) { + console.log('DEBUG: Auth failed for relay:', url, 'Error:', authError) let authErrorMessage = 'Unknown auth error' if (authError instanceof Error) { authErrorMessage = authError.message || 'Empty auth error message' @@ -1479,7 +1497,7 @@ class ClientService extends EventTarget { return getRelayListFromEvent(event) } return { - write: BIG_RELAY_URLS, + write: Array.from(new Set([...FAST_WRITE_RELAY_URLS, ...BIG_RELAY_URLS])).slice(0, 8), // Combine fast write + big relays for better redundancy (deduplicated) read: BIG_RELAY_URLS, originalRelays: [] } @@ -1730,11 +1748,6 @@ class ClientService extends EventTarget { // Skip empty or invalid URLs if (!url || typeof url !== 'string') return false - // Skip localhost URLs that might be misconfigured - if (url.includes('localhost:7777') || url.includes('localhost:5173')) { - return false - } - // Skip relays with open circuit breaker if (this.isCircuitBreakerOpen(url)) { console.warn(`Skipping relay with open circuit breaker: ${url}`)