use an env var for configurable proxy header (#8863)

This commit is contained in:
hailey
2025-08-26 17:13:11 -07:00
committed by GitHub
parent 39c6984c6e
commit a1ed59e0ee
21 changed files with 108 additions and 41 deletions
+3
View File
@@ -16,6 +16,9 @@ EXPO_PUBLIC_LOG_LEVEL=debug
# Enable debug logs for specific logger instances
EXPO_PUBLIC_LOG_DEBUG=session
# Bluesky appview DID
EXPO_PUBLIC_BLUESKY_PROXY_DID=
# Chat service DID
EXPO_PUBLIC_CHAT_PROXY_DID=
+6
View File
@@ -62,6 +62,12 @@ export const LOG_LEVEL = (process.env.EXPO_PUBLIC_LOG_LEVEL || 'info') as
*/
export const LOG_DEBUG: string = process.env.EXPO_PUBLIC_LOG_DEBUG || ''
/**
* The DID of the Bluesky appview to proxy to
*/
export const BLUESKY_PROXY_DID: Did =
process.env.EXPO_PUBLIC_BLUESKY_PROXY_DID || 'did:web:api.bsky.app'
/**
* The DID of the chat service to proxy to
*/
+13
View File
@@ -1,6 +1,9 @@
import {type Insets, Platform} from 'react-native'
import {type AppBskyActorDefs} from '@atproto/api'
import {type ProxyHeaderValue} from '#/state/session/agent'
import {BLUESKY_PROXY_DID, CHAT_PROXY_DID} from '#/env'
export const LOCAL_DEV_SERVICE =
Platform.OS === 'android' ? 'http://10.0.2.2:2583' : 'http://localhost:2583'
export const STAGING_SERVICE = 'https://staging.bsky.dev'
@@ -211,6 +214,16 @@ export const PUBLIC_STAGING_APPVIEW_DID = 'did:web:api.staging.bsky.dev'
export const DEV_ENV_APPVIEW = `http://localhost:2584` // always the same
export const BLUESKY_PROXY_HEADER: ProxyHeaderValue = `${BLUESKY_PROXY_DID}#bsky_appview`
export const BLUESKY_SERVICE_HEADERS = {
'atproto-proxy': BLUESKY_PROXY_HEADER,
}
export const DM_SERVICE_HEADERS = {
'atproto-proxy': `${CHAT_PROXY_DID}#bsky_chat`,
}
export const webLinks = {
tos: `https://bsky.social/about/support/tos`,
privacy: `https://bsky.social/about/support/privacy-policy`,
+3 -3
View File
@@ -1,7 +1,6 @@
import React, {useState} from 'react'
import {ActivityIndicator, Keyboard, View} from 'react-native'
import {ComAtprotoServerDescribeServer} from '@atproto/api'
import {BskyAgent} from '@atproto/api'
import {type ComAtprotoServerDescribeServer} from '@atproto/api'
import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import * as EmailValidator from 'email-validator'
@@ -9,6 +8,7 @@ import * as EmailValidator from 'email-validator'
import {isNetworkError} from '#/lib/strings/errors'
import {cleanError} from '#/lib/strings/errors'
import {logger} from '#/logger'
import {Agent} from '#/state/session/agent'
import {atoms as a, useTheme} from '#/alf'
import {Button, ButtonText} from '#/components/Button'
import {FormError} from '#/components/forms/FormError'
@@ -55,7 +55,7 @@ export const ForgotPasswordForm = ({
setIsProcessing(true)
try {
const agent = new BskyAgent({service: serviceUrl})
const agent = new Agent(null, {service: serviceUrl})
await agent.com.atproto.server.requestPasswordReset({email})
onEmailSent()
} catch (e: any) {
+2 -2
View File
@@ -1,6 +1,5 @@
import {useState} from 'react'
import {ActivityIndicator, View} from 'react-native'
import {BskyAgent} from '@atproto/api'
import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
@@ -9,6 +8,7 @@ import {isNetworkError} from '#/lib/strings/errors'
import {cleanError} from '#/lib/strings/errors'
import {checkAndFormatResetCode} from '#/lib/strings/password'
import {logger} from '#/logger'
import {Agent} from '#/state/session/agent'
import {atoms as a, useTheme} from '#/alf'
import {Button, ButtonText} from '#/components/Button'
import {FormError} from '#/components/forms/FormError'
@@ -63,7 +63,7 @@ export const SetNewPasswordForm = ({
setIsProcessing(true)
try {
const agent = new BskyAgent({service: serviceUrl})
const agent = new Agent(null, {service: serviceUrl})
await agent.com.atproto.server.resetPassword({
token: formattedCode,
password,
+1 -1
View File
@@ -10,6 +10,7 @@ import EventEmitter from 'eventemitter3'
import {nanoid} from 'nanoid/non-secure'
import {networkRetry} from '#/lib/async/retry'
import {DM_SERVICE_HEADERS} from '#/lib/constants'
import {isNetworkError} from '#/lib/strings/errors'
import {Logger} from '#/logger'
import {isNative} from '#/platform/detection'
@@ -33,7 +34,6 @@ import {
} from '#/state/messages/convo/types'
import {type MessagesEventBus} from '#/state/messages/events/agent'
import {type MessagesEventBusError} from '#/state/messages/events/types'
import {DM_SERVICE_HEADERS} from '#/state/queries/messages/const'
const logger = Logger.create(Logger.Context.ConversationAgent)
+1 -1
View File
@@ -3,6 +3,7 @@ import EventEmitter from 'eventemitter3'
import {nanoid} from 'nanoid/non-secure'
import {networkRetry} from '#/lib/async/retry'
import {DM_SERVICE_HEADERS} from '#/lib/constants'
import {isNetworkError} from '#/lib/strings/errors'
import {Logger} from '#/logger'
import {
@@ -17,7 +18,6 @@ import {
type MessagesEventBusParams,
MessagesEventBusStatus,
} from '#/state/messages/events/types'
import {DM_SERVICE_HEADERS} from '#/state/queries/messages/const'
const logger = Logger.create(Logger.Context.DMsAgent)
+4 -3
View File
@@ -1,4 +1,4 @@
import {Agent, ComAtprotoTempCheckHandleAvailability} from '@atproto/api'
import {ComAtprotoTempCheckHandleAvailability} from '@atproto/api'
import {useQuery} from '@tanstack/react-query'
import {
@@ -10,6 +10,7 @@ import {createFullHandle} from '#/lib/strings/handles'
import {logger} from '#/logger'
import {useDebouncedValue} from '#/components/live/utils'
import * as bsky from '#/types/bsky'
import {Agent} from '../session/agent'
export const RQKEY_handleAvailability = (
handle: string,
@@ -74,7 +75,7 @@ export async function checkHandleAvailability(
},
) {
if (serviceDid === BSKY_SERVICE_DID) {
const agent = new Agent({service: BSKY_SERVICE})
const agent = new Agent(null, {service: BSKY_SERVICE})
// entryway has a special API for handle availability
const {data} = await agent.com.atproto.temp.checkHandleAvailability({
handle,
@@ -109,7 +110,7 @@ export async function checkHandleAvailability(
}
} else {
// 3rd party PDSes won't have this API so just try and resolve the handle
const agent = new Agent({service: PUBLIC_BSKY_SERVICE})
const agent = new Agent(null, {service: PUBLIC_BSKY_SERVICE})
try {
const res = await agent.resolveHandle({
handle,
@@ -1,9 +1,12 @@
import {ChatBskyConvoAcceptConvo, ChatBskyConvoListConvos} from '@atproto/api'
import {
type ChatBskyConvoAcceptConvo,
type ChatBskyConvoListConvos,
} from '@atproto/api'
import {useMutation, useQueryClient} from '@tanstack/react-query'
import {DM_SERVICE_HEADERS} from '#/lib/constants'
import {logger} from '#/logger'
import {useAgent} from '#/state/session'
import {DM_SERVICE_HEADERS} from './const'
import {
RQKEY as CONVO_LIST_KEY,
RQKEY_ROOT as CONVO_LIST_ROOT_KEY,
-5
View File
@@ -1,5 +0,0 @@
import {CHAT_PROXY_DID} from '#/env'
export const DM_SERVICE_HEADERS = {
'atproto-proxy': `${CHAT_PROXY_DID}#bsky_chat`,
}
+4 -4
View File
@@ -1,17 +1,17 @@
import {ChatBskyConvoDefs} from '@atproto/api'
import {type ChatBskyConvoDefs} from '@atproto/api'
import {
QueryClient,
type QueryClient,
useMutation,
useQuery,
useQueryClient,
} from '@tanstack/react-query'
import {DM_SERVICE_HEADERS} from '#/lib/constants'
import {STALE} from '#/state/queries'
import {DM_SERVICE_HEADERS} from '#/state/queries/messages/const'
import {useOnMarkAsRead} from '#/state/queries/messages/list-conversations'
import {useAgent} from '#/state/session'
import {
ConvoListQueryData,
type ConvoListQueryData,
getConvoFromQueryData,
RQKEY_ROOT as LIST_CONVOS_KEY,
} from './list-conversations'
@@ -1,6 +1,6 @@
import {useQuery} from '@tanstack/react-query'
import {DM_SERVICE_HEADERS} from '#/state/queries/messages/const'
import {DM_SERVICE_HEADERS} from '#/lib/constants'
import {useAgent} from '#/state/session'
import {STALE} from '..'
@@ -1,8 +1,8 @@
import {ChatBskyConvoGetConvoForMembers} from '@atproto/api'
import {type ChatBskyConvoGetConvoForMembers} from '@atproto/api'
import {useMutation, useQueryClient} from '@tanstack/react-query'
import {DM_SERVICE_HEADERS} from '#/lib/constants'
import {logger} from '#/logger'
import {DM_SERVICE_HEADERS} from '#/state/queries/messages/const'
import {useAgent} from '#/state/session'
import {precacheConvoQuery} from './conversation'
@@ -1,13 +1,16 @@
import {useMemo} from 'react'
import {ChatBskyConvoLeaveConvo, ChatBskyConvoListConvos} from '@atproto/api'
import {
type ChatBskyConvoLeaveConvo,
type ChatBskyConvoListConvos,
} from '@atproto/api'
import {
useMutation,
useMutationState,
useQueryClient,
} from '@tanstack/react-query'
import {DM_SERVICE_HEADERS} from '#/lib/constants'
import {logger} from '#/logger'
import {DM_SERVICE_HEADERS} from '#/state/queries/messages/const'
import {useAgent} from '#/state/session'
import {RQKEY_ROOT as CONVO_LIST_KEY} from './list-conversations'
@@ -13,10 +13,10 @@ import {
} from '@tanstack/react-query'
import throttle from 'lodash.throttle'
import {DM_SERVICE_HEADERS} from '#/lib/constants'
import {useCurrentConvoId} from '#/state/messages/current-convo-id'
import {useMessagesEventBus} from '#/state/messages/events'
import {useModerationOpts} from '#/state/preferences/moderation-opts'
import {DM_SERVICE_HEADERS} from '#/state/queries/messages/const'
import {useAgent, useSession} from '#/state/session'
import {useLeftConvos} from './leave-conversation'
@@ -1,11 +1,15 @@
import {
ChatBskyConvoDefs,
ChatBskyConvoListConvos,
ChatBskyConvoMuteConvo,
type ChatBskyConvoDefs,
type ChatBskyConvoListConvos,
type ChatBskyConvoMuteConvo,
} from '@atproto/api'
import {InfiniteData, useMutation, useQueryClient} from '@tanstack/react-query'
import {
type InfiniteData,
useMutation,
useQueryClient,
} from '@tanstack/react-query'
import {DM_SERVICE_HEADERS} from '#/state/queries/messages/const'
import {DM_SERVICE_HEADERS} from '#/lib/constants'
import {useAgent} from '#/state/session'
import {RQKEY as CONVO_KEY} from './conversation'
import {RQKEY_ROOT as CONVO_LIST_KEY} from './list-conversations'
@@ -1,8 +1,8 @@
import {ChatBskyConvoListConvos} from '@atproto/api'
import {type ChatBskyConvoListConvos} from '@atproto/api'
import {useMutation, useQueryClient} from '@tanstack/react-query'
import {DM_SERVICE_HEADERS} from '#/lib/constants'
import {logger} from '#/logger'
import {DM_SERVICE_HEADERS} from '#/state/queries/messages/const'
import {useAgent} from '#/state/session'
import {RQKEY as CONVO_LIST_KEY} from './list-conversations'
+3 -2
View File
@@ -1,6 +1,7 @@
import {BskyAgent} from '@atproto/api'
import {useQuery} from '@tanstack/react-query'
import {Agent} from '../session/agent'
const RQKEY_ROOT = 'service'
export const RQKEY = (serviceUrl: string) => [RQKEY_ROOT, serviceUrl]
@@ -8,7 +9,7 @@ export function useServiceQuery(serviceUrl: string) {
return useQuery({
queryKey: RQKEY(serviceUrl),
queryFn: async () => {
const agent = new BskyAgent({service: serviceUrl})
const agent = new Agent(null, {service: serviceUrl})
const res = await agent.com.atproto.server.describeServer()
return res.data
},
+41 -3
View File
@@ -1,8 +1,19 @@
import {AtpSessionData, AtpSessionEvent, BskyAgent} from '@atproto/api'
import {
Agent as BaseAgent,
type AtprotoServiceType,
type AtpSessionData,
type AtpSessionEvent,
BskyAgent,
type Did,
} from '@atproto/api'
import {type FetchHandler} from '@atproto/api/dist/agent'
import {type SessionManager} from '@atproto/api/dist/session-manager'
import {TID} from '@atproto/common-web'
import {type FetchHandlerOptions} from '@atproto/xrpc'
import {networkRetry} from '#/lib/async/retry'
import {
BLUESKY_PROXY_HEADER,
BSKY_SERVICE,
DISCOVER_SAVED_FEED,
IS_PROD_SERVICE,
@@ -19,12 +30,17 @@ import {
configureModerationForAccount,
configureModerationForGuest,
} from './moderation'
import {SessionAccount} from './types'
import {type SessionAccount} from './types'
import {isSessionExpired, isSignupQueued} from './util'
export type ProxyHeaderValue = `${Did}#${AtprotoServiceType}`
export function createPublicAgent() {
configureModerationForGuest() // Side effect but only relevant for tests
return new BskyAppAgent({service: PUBLIC_BSKY_SERVICE})
const agent = new BskyAppAgent({service: PUBLIC_BSKY_SERVICE})
agent.configureProxy(BLUESKY_PROXY_HEADER)
return agent
}
export async function createAgentAndResume(
@@ -61,6 +77,8 @@ export async function createAgentAndResume(
}
}
agent.configureProxy(BLUESKY_PROXY_HEADER)
return agent.prepare(gates, moderation, onSessionChange)
}
@@ -93,6 +111,9 @@ export async function createAgentAndLogin(
const account = agentToSessionAccountOrThrow(agent)
const gates = tryFetchGates(account.did, 'prefer-fresh-gates')
const moderation = configureModerationForAccount(agent, account)
agent.configureProxy(BLUESKY_PROXY_HEADER)
return agent.prepare(gates, moderation, onSessionChange)
}
@@ -180,6 +201,8 @@ export async function createAgentAndCreateAccount(
logger.error(e, {message: `session: failed snoozeEmailConfirmationPrompt`})
}
agent.configureProxy(BLUESKY_PROXY_HEADER)
return agent.prepare(gates, moderation, onSessionChange)
}
@@ -234,7 +257,22 @@ export function sessionAccountToSession(
}
}
export class Agent extends BaseAgent {
constructor(
proxyHeader: ProxyHeaderValue | null,
options: SessionManager | FetchHandler | FetchHandlerOptions,
) {
super(options)
if (proxyHeader) {
this.configureProxy(proxyHeader)
}
}
}
// Not exported. Use factories above to create it.
// WARN: In the factories above, we _manually set a proxy header_ for the agent after we do whatever it is we are supposed to do.
// Ideally, we wouldn't be doing this. However, since there is so much logic that requires making calls to the PDS right now, it
// feels safer to just let those run as-is and set the header afterward.
let realFetch = globalThis.fetch
class BskyAppAgent extends BskyAgent {
persistSessionHandler: ((event: AtpSessionEvent) => void) | undefined =
+1 -1
View File
@@ -10,6 +10,7 @@ import {LinearGradient} from 'expo-linear-gradient'
import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {DM_SERVICE_HEADERS} from '#/lib/constants'
import {usePalette} from '#/lib/hooks/usePalette'
import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
import {cleanError} from '#/lib/strings/errors'
@@ -17,7 +18,6 @@ import {colors, gradients, s} from '#/lib/styles'
import {useTheme} from '#/lib/ThemeContext'
import {isAndroid, isWeb} from '#/platform/detection'
import {useModalControls} from '#/state/modals'
import {DM_SERVICE_HEADERS} from '#/state/queries/messages/const'
import {useAgent, useSession, useSessionApi} from '#/state/session'
import {atoms as a, useTheme as useNewTheme} from '#/alf'
import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '#/components/icons/CircleInfo'
@@ -31,6 +31,7 @@ import {useNavigation} from '@react-navigation/native'
import {useQueryClient} from '@tanstack/react-query'
import {MAX_POST_LINES} from '#/lib/constants'
import {DM_SERVICE_HEADERS} from '#/lib/constants'
import {useAnimatedValue} from '#/lib/hooks/useAnimatedValue'
import {usePalette} from '#/lib/hooks/usePalette'
import {makeProfileLink} from '#/lib/routes/links'
@@ -41,7 +42,6 @@ import {sanitizeHandle} from '#/lib/strings/handles'
import {niceDate} from '#/lib/strings/time'
import {s} from '#/lib/styles'
import {logger} from '#/logger'
import {DM_SERVICE_HEADERS} from '#/state/queries/messages/const'
import {type FeedNotification} from '#/state/queries/notifications/feed'
import {unstableCacheProfileView} from '#/state/queries/unstable-profile-cache'
import {useAgent} from '#/state/session'