Monitor relay connections for restricted responses and show error to user

This commit is contained in:
Jon Staab
2025-09-03 15:29:57 -07:00
parent 4001e877b4
commit 95698813c6
9 changed files with 226 additions and 75 deletions
+24 -7
View File
@@ -246,11 +246,7 @@ export const checkRelayAccess = async (url: string, claim = "") => {
await attemptAuth(url)
const thunk = publishThunk({
event: makeEvent(AUTH_JOIN, {tags: [["claim", claim]]}),
relays: [url],
})
const thunk = publishJoinRequest({url, claim})
const error = await getThunkError(thunk)
if (error) {
@@ -296,7 +292,7 @@ export const checkRelayConnection = async (url: string) => {
}
}
export const checkRelayAuth = async (url: string, timeout = 3000) => {
export const checkRelayAuth = async (url: string) => {
const socket = Pool.get().get(url)
const okStatuses = [AuthStatus.None, AuthStatus.Ok]
@@ -325,7 +321,7 @@ export const attemptRelayAccess = async (url: string, claim = "") => {
}
}
// Actions
// Deletions
export type DeleteParams = {
protect: boolean
@@ -351,6 +347,8 @@ export const makeDelete = ({protect, event, tags = []}: DeleteParams) => {
export const publishDelete = ({relays, ...params}: DeleteParams & {relays: string[]}) =>
publishThunk({event: makeDelete(params), relays})
// Reports
export type ReportParams = {
event: TrustedEvent
content: string
@@ -374,6 +372,8 @@ export const publishReport = ({
}: ReportParams & {relays: string[]}) =>
publishThunk({event: makeReport({event, reason, content}), relays})
// Reactions
export type ReactionParams = {
protect: boolean
event: TrustedEvent
@@ -399,6 +399,8 @@ export const makeReaction = ({protect, content, event, tags: paramTags = []}: Re
export const publishReaction = ({relays, ...params}: ReactionParams & {relays: string[]}) =>
publishThunk({event: makeReaction(params), relays})
// Comments
export type CommentParams = {
event: TrustedEvent
content: string
@@ -411,6 +413,8 @@ export const makeComment = ({event, content, tags = []}: CommentParams) =>
export const publishComment = ({relays, ...params}: CommentParams & {relays: string[]}) =>
publishThunk({event: makeComment(params), relays})
// Alerts
export type AlertParams = {
feed: Feed
description: string
@@ -494,6 +498,19 @@ export const addTrustedRelay = async (url: string) =>
export const removeTrustedRelay = async (url: string) =>
publishSettings({trusted_relays: remove(url, userSettingsValues.get().trusted_relays)})
// Join request
export type JoinRequestParams = {
url: string
claim: string
}
export const makeJoinRequest = (params: JoinRequestParams) =>
makeEvent(AUTH_JOIN, {tags: [["claim", params.claim]]})
export const publishJoinRequest = (params: JoinRequestParams) =>
publishThunk({event: makeJoinRequest(params), relays: [params.url]})
// Lightning
export const getWebLn = () => (window as any).webln
+55
View File
@@ -70,6 +70,7 @@ import {
getTagValue,
getTagValues,
verifyEvent,
makeEvent,
} from "@welshman/util"
import type {TrustedEvent, SignedEvent, PublishedList, List, Filter} from "@welshman/util"
import {Nip59, decrypt} from "@welshman/signer"
@@ -92,6 +93,8 @@ import {
signer,
makeOutboxLoader,
appContext,
getThunkError,
publishThunk,
} from "@welshman/app"
import type {Thunk, Relay} from "@welshman/app"
@@ -368,6 +371,10 @@ export const {
export const relaysPendingTrust = writable<string[]>([])
// Relays that mostly send restricted responses to requests and events
export const relaysMostlyRestricted = writable<Record<string, string>>({})
// Alerts
export type Alert = {
@@ -738,3 +745,51 @@ export const deriveSocket = (url: string) =>
return () => subs.forEach(call)
})
export const deriveTimeout = (timeout: number) => {
const store = writable<boolean>(false)
setTimeout(() => store.set(true), timeout)
return derived(store, identity)
}
export const deriveRelayAuthError = (url: string, claim = "") => {
const $signer = signer.get()
const socket = Pool.get().get(url)
const stripPrefix = (m: string) => m.replace(/^\w+: /, "")
// Kick off the auth process
socket.auth.attemptAuth($signer.sign)
// Attempt to join the relay
const thunk = publishThunk({
event: makeEvent(AUTH_JOIN, {tags: [["claim", claim]]}),
relays: [url],
})
return derived(
[relaysMostlyRestricted, deriveSocket(url)],
([$relaysMostlyRestricted, $socket]) => {
if ($socket.auth.details) {
return stripPrefix($socket.auth.details)
}
if ($relaysMostlyRestricted[url]) {
return stripPrefix($relaysMostlyRestricted[url])
}
const error = getThunkError(thunk)
if (error) {
const isIgnored = error.startsWith("mute: ")
const isEmptyInvite = !claim && error.includes("invite code")
const isStrictNip29Relay = error.includes("missing group (`h`) tag")
if (!isStrictNip29Relay && !isIgnored && !isEmptyInvite && !isStrictNip29Relay) {
return stripPrefix(error) || "join request rejected"
}
}
},
)
}