feat(fe2): more reslient app reboot on fatal network error (#5363)
* feat(fe2): more reslient app reboot on fatal network error * minor adjustment
This commit is contained in:
committed by
GitHub
parent
361aa523ac
commit
427117c15d
@@ -27,6 +27,7 @@ import type { ActiveUserMainMetadataQuery } from '~~/lib/common/generated/gql/gr
|
||||
import { useScopedState } from '~/lib/common/composables/scopedState'
|
||||
import type { ApolloClient } from '@apollo/client/core'
|
||||
import { AuthFailedError } from '~/lib/auth/errors/errors'
|
||||
import { useAppErrorState } from '~/lib/core/composables/error'
|
||||
|
||||
type UseOnAuthStateChangeCallback = (
|
||||
user: MaybeNullOrUndefined<ActiveUserMainMetadataQuery['activeUser']>,
|
||||
@@ -220,6 +221,7 @@ export const useAuthManager = (
|
||||
) => {
|
||||
const { deferredApollo } = options || {}
|
||||
|
||||
const ssrEvent = useRequestEvent()
|
||||
const apiOrigin = useApiOrigin()
|
||||
const resetAuthState = useResetAuthState({ deferredApollo })
|
||||
const route = useRoute()
|
||||
@@ -230,6 +232,7 @@ export const useAuthManager = (
|
||||
const postAuthRedirect = usePostAuthRedirect()
|
||||
const { markLoggedOut } = useJustLoggedOutTracking()
|
||||
const { logger } = useSafeLogger()
|
||||
const { isFullRedirectState } = useAppErrorState()
|
||||
|
||||
/**
|
||||
* Invite token, if any
|
||||
@@ -259,6 +262,24 @@ export const useAuthManager = (
|
||||
() => dashboardToken.value || embedToken.value || authToken.value
|
||||
)
|
||||
|
||||
/**
|
||||
* Trigger full redirect that causes a full reload, instead of an in-session navigation
|
||||
*/
|
||||
const sendFullRedirect = async (relativeUrl: string) => {
|
||||
if (isFullRedirectState.value) return
|
||||
|
||||
isFullRedirectState.value = true
|
||||
|
||||
if (import.meta.client) {
|
||||
window.location.href = relativeUrl
|
||||
} else if (ssrEvent) {
|
||||
const { sendRedirect } = await import('h3')
|
||||
await sendRedirect(ssrEvent, relativeUrl)
|
||||
} else {
|
||||
logger().fatal('Failed to send full redirect')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set/clear new token value and redirect to home
|
||||
*/
|
||||
@@ -513,10 +534,10 @@ export const useAuthManager = (
|
||||
}
|
||||
|
||||
if (!options?.skipRedirect) {
|
||||
if (options?.forceFullReload && import.meta.client) {
|
||||
window.location.href = loginRoute
|
||||
} else {
|
||||
if (!options?.forceFullReload) {
|
||||
await goToLogin()
|
||||
} else {
|
||||
await sendFullRedirect(loginRoute)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,8 @@ const ENTER_STATE_AT_ERRORS_PER_MIN = 100
|
||||
export function useAppErrorState() {
|
||||
const state = useScopedState('appErrorState', () => ({
|
||||
inErrorState: ref(false),
|
||||
errorRpm: Observability.simpleRpmCounter()
|
||||
errorRpm: Observability.simpleRpmCounter(),
|
||||
isFullRedirectState: ref(false)
|
||||
}))
|
||||
const nuxtApp = useNuxtApp()
|
||||
|
||||
@@ -32,7 +33,22 @@ export function useAppErrorState() {
|
||||
)
|
||||
state.inErrorState.value = true
|
||||
}
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Similar to error state, except we don't show any UI elements to the user, we just stop processing
|
||||
* API calls etc. because we're redirecting fully away
|
||||
*/
|
||||
isFullRedirectState: state.isFullRedirectState,
|
||||
/**
|
||||
* Whether to prevent HTTP API calls
|
||||
*/
|
||||
preventHttpCalls: computed(() => state.isFullRedirectState.value),
|
||||
/**
|
||||
* Whether to prevent websocket messaging
|
||||
*/
|
||||
preventWebsocketMessaging: computed(
|
||||
() => state.isFullRedirectState.value || state.inErrorState.value
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import { SubscriptionClient } from 'subscriptions-transport-ws'
|
||||
import type { ApolloConfigResolver } from '~~/lib/core/nuxt-modules/apollo/module'
|
||||
import createUploadLink from 'apollo-upload-client/createUploadLink.mjs'
|
||||
import { WebSocketLink } from '@apollo/client/link/ws'
|
||||
import { getMainDefinition } from '@apollo/client/utilities'
|
||||
import { getMainDefinition, Observable } from '@apollo/client/utilities'
|
||||
import { Kind } from 'graphql'
|
||||
import type { GraphQLError, OperationDefinitionNode } from 'graphql'
|
||||
import type { CookieRef, NuxtApp } from '#app'
|
||||
@@ -441,7 +441,23 @@ function createLink(params: {
|
||||
logout: ReturnType<typeof useAuthManager>['logout']
|
||||
}): ApolloLink {
|
||||
const { httpEndpoint, wsClient, authToken, nuxtApp, reqId, logout } = params
|
||||
const { registerError, isErrorState } = useAppErrorState()
|
||||
const {
|
||||
registerError,
|
||||
preventHttpCalls,
|
||||
preventWebsocketMessaging,
|
||||
isFullRedirectState
|
||||
} = useAppErrorState()
|
||||
|
||||
const stopLink = new ApolloLink((operation, forward) => {
|
||||
if (preventHttpCalls.value) {
|
||||
// swallow the req, we're blocking them all
|
||||
return new Observable(() => {
|
||||
return () => {}
|
||||
})
|
||||
}
|
||||
|
||||
return forward(operation)
|
||||
})
|
||||
|
||||
const errorLink = onError((res) => {
|
||||
const logger = nuxtApp.$logger
|
||||
@@ -487,7 +503,7 @@ function createLink(params: {
|
||||
}
|
||||
|
||||
const { networkError } = res
|
||||
if (networkError && isInvalidAuth(networkError)) {
|
||||
if (networkError && isInvalidAuth(networkError) && !isFullRedirectState.value) {
|
||||
// Reset auth
|
||||
// since this may happen mid-routing, a standard router.push call may not work - do full reload
|
||||
void logout({ skipToast: true, forceFullReload: true })
|
||||
@@ -577,7 +593,7 @@ function createLink(params: {
|
||||
wsClient.use([
|
||||
{
|
||||
applyMiddleware: (_opt, next) => {
|
||||
if (isErrorState.value) {
|
||||
if (preventWebsocketMessaging.value) {
|
||||
return // never invokes next() - essentially stuck
|
||||
}
|
||||
|
||||
@@ -614,7 +630,7 @@ function createLink(params: {
|
||||
})
|
||||
})
|
||||
|
||||
return from([...(import.meta.server ? [loggerLink] : []), errorLink, link])
|
||||
return from([stopLink, ...(import.meta.server ? [loggerLink] : []), errorLink, link])
|
||||
}
|
||||
|
||||
const defaultConfigResolver: ApolloConfigResolver = () => {
|
||||
|
||||
Reference in New Issue
Block a user