diff --git a/app/composables/useAuth.ts b/app/composables/useAuth.ts index 163929d..b976085 100644 --- a/app/composables/useAuth.ts +++ b/app/composables/useAuth.ts @@ -7,7 +7,8 @@ export const useAuth = () => { const { getToken, initTokens, idToken } = useLogtoTokens() const me = useState<{ id?: string | null } | null>('me', () => null) - const isAuthenticated = computed(() => !!me.value?.id) + const logtoUser = useState | null>('logto-user', () => null) + const isAuthenticated = computed(() => !!(me.value?.id || logtoUser.value)) const loggedIn = isAuthenticated /** diff --git a/app/composables/useGraphQL.ts b/app/composables/useGraphQL.ts index 0bb82ff..ed90dff 100644 --- a/app/composables/useGraphQL.ts +++ b/app/composables/useGraphQL.ts @@ -27,6 +27,7 @@ const CLIENT_MAP: Record = { export const useGraphQL = () => { const auth = useAuth() const { activeLogtoOrgId } = useActiveTeam() + const { refreshTokens } = useLogtoTokens() const getClientId = (endpoint: Endpoint, api: Api): string => { return CLIENT_MAP[`${endpoint}:${api}`] || 'default' @@ -77,20 +78,37 @@ export const useGraphQL = () => { ): Promise => { const clientId = getClientId(endpoint, api) const { client } = useApolloClient(clientId) - const context = await getAuthContext(endpoint, api) + const executeOnce = async () => { + const context = await getAuthContext(endpoint, api) + const result = await client.query({ + query: document, + variables, + context, + fetchPolicy: 'network-only' + }) - const result = await client.query({ - query: document, - variables, - context, - fetchPolicy: 'network-only' - }) + if (result.errors?.length) { + throw new Error(result.errors[0]?.message || 'GraphQL error') + } - if (result.errors?.length) { - throw new Error(result.errors[0]?.message || 'GraphQL error') + return result.data as TResult } - return result.data as TResult + try { + return await executeOnce() + } catch (error) { + const message = error instanceof Error ? error.message : String(error) + const isAuthContextFailure = message.includes('Invalid Compact JWS') + || message.includes('Context creation failed') + || message.includes('Received status code 500') + + if (endpoint === 'team' && isAuthContextFailure) { + await refreshTokens() + return await executeOnce() + } + + throw error + } } const mutate = async >( @@ -101,19 +119,36 @@ export const useGraphQL = () => { ): Promise => { const clientId = getClientId(endpoint, api) const { client } = useApolloClient(clientId) - const context = await getAuthContext(endpoint, api) + const mutateOnce = async () => { + const context = await getAuthContext(endpoint, api) + const result = await client.mutate({ + mutation: document, + variables, + context + }) - const result = await client.mutate({ - mutation: document, - variables, - context - }) + if (result.errors?.length) { + throw new Error(result.errors[0]?.message || 'GraphQL error') + } - if (result.errors?.length) { - throw new Error(result.errors[0]?.message || 'GraphQL error') + return result.data as TResult } - return result.data as TResult + try { + return await mutateOnce() + } catch (error) { + const message = error instanceof Error ? error.message : String(error) + const isAuthContextFailure = message.includes('Invalid Compact JWS') + || message.includes('Context creation failed') + || message.includes('Received status code 500') + + if (endpoint === 'team' && isAuthContextFailure) { + await refreshTokens() + return await mutateOnce() + } + + throw error + } } return { execute, mutate } diff --git a/app/plugins/00-apollo-console-filter.client.ts b/app/plugins/00-apollo-console-filter.client.ts index d40ba9c..0f9b88a 100644 --- a/app/plugins/00-apollo-console-filter.client.ts +++ b/app/plugins/00-apollo-console-filter.client.ts @@ -2,14 +2,28 @@ export default defineNuxtPlugin(() => { const originalConsoleError = console.error console.error = (...args: unknown[]) => { - const hasApolloDevtoolsWarning = args.some((arg) => { - if (typeof arg !== 'string') return false + const serializedArgs = args + .map((arg) => { + if (typeof arg === 'string') return arg + if (arg instanceof Error) return `${arg.message}\n${arg.stack || ''}` + try { + return JSON.stringify(arg) + } catch { + return String(arg) + } + }) + .join(' ') - return ( - arg.includes('connectToDevTools') && - arg.includes('devtools.enabled') + const hasApolloDevtoolsWarning = ( + ( + serializedArgs.includes('connectToDevTools') + && serializedArgs.includes('devtools.enabled') ) - }) + || ( + serializedArgs.includes('go.apollo.dev/c/err') + && serializedArgs.includes('"message":104') + ) + ) if (hasApolloDevtoolsWarning) { return diff --git a/server/middleware/me.ts b/server/middleware/me.ts index f53da30..4d3de8f 100644 --- a/server/middleware/me.ts +++ b/server/middleware/me.ts @@ -37,10 +37,10 @@ export default defineEventHandler(async (event) => { let token: string | null = null try { - token = await client.getAccessToken('https://teams.optovia.ru', organizationId) + token = await client.getIdToken() } catch { try { - token = await client.getIdToken() + token = await client.getAccessToken('https://teams.optovia.ru', organizationId) } catch { return }