taskcafe/frontend/src/index.tsx

147 lines
4.3 KiB
TypeScript
Raw Normal View History

2020-04-10 04:40:22 +02:00
import React from 'react';
import ReactDOM from 'react-dom';
import axios from 'axios';
import createAuthRefreshInterceptor from 'axios-auth-refresh';
2020-04-10 04:40:22 +02:00
import { ApolloProvider } from '@apollo/react-hooks';
import { ApolloClient } from 'apollo-client';
import { InMemoryCache } from 'apollo-cache-inmemory';
import { HttpLink } from 'apollo-link-http';
import { onError } from 'apollo-link-error';
import { ApolloLink, Observable, fromPromise } from 'apollo-link';
import { getAccessToken, getNewToken, setAccessToken } from 'shared/utils/accessToken';
import App from './App';
// https://able.bio/AnasT/apollo-graphql-async-access-token-refresh--470t1c8
let forward$;
let isRefreshing = false;
let pendingRequests: any = [];
2020-06-13 00:21:58 +02:00
const refreshAuthLogic = (failedRequest: any) =>
axios.post('http://localhost:3333/auth/refresh_token', {}, { withCredentials: true }).then(tokenRefreshResponse => {
setAccessToken(tokenRefreshResponse.data.accessToken);
failedRequest.response.config.headers.Authorization = `Bearer ${tokenRefreshResponse.data.accessToken}`;
return Promise.resolve();
});
createAuthRefreshInterceptor(axios, refreshAuthLogic);
2020-04-10 04:40:22 +02:00
const resolvePendingRequests = () => {
pendingRequests.map((callback: any) => callback());
pendingRequests = [];
};
2020-04-10 22:31:12 +02:00
const resolvePromise = (resolve: () => void) => {
pendingRequests.push(() => resolve());
};
const resetPendingRequests = () => {
pendingRequests = [];
};
const setRefreshing = (newVal: boolean) => {
isRefreshing = newVal;
};
2020-04-10 04:40:22 +02:00
const errorLink = onError(({ graphQLErrors, networkError, operation, forward }) => {
if (graphQLErrors) {
for (const err of graphQLErrors) {
2020-04-10 22:31:12 +02:00
if (err.extensions && err.extensions.code) {
switch (err.extensions.code) {
case 'UNAUTHENTICATED':
if (!isRefreshing) {
setRefreshing(true);
forward$ = fromPromise(
getNewToken()
.then((response: any) => {
setAccessToken(response.accessToken);
resolvePendingRequests();
return response.accessToken;
})
.catch(() => {
resetPendingRequests();
// TODO
// Handle token refresh errors e.g clear stored tokens, redirect to login, ...
return undefined;
})
.finally(() => {
setRefreshing(false);
}),
).filter(value => Boolean(value));
} else {
forward$ = fromPromise(new Promise(resolvePromise));
}
return forward$.flatMap(() => forward(operation));
default:
// pass
}
2020-04-10 04:40:22 +02:00
}
}
}
if (networkError) {
console.log(`[Network error]: ${networkError}`);
}
2020-04-10 22:31:12 +02:00
return undefined;
2020-04-10 04:40:22 +02:00
});
const requestLink = new ApolloLink(
(operation, forward) =>
new Observable((observer: any) => {
let handle: any;
Promise.resolve(operation)
2020-04-10 22:31:12 +02:00
.then((op: any) => {
2020-04-10 04:40:22 +02:00
const accessToken = getAccessToken();
if (accessToken) {
2020-04-10 22:31:12 +02:00
op.setContext({
2020-04-10 04:40:22 +02:00
headers: {
Authorization: `Bearer ${accessToken}`,
},
});
}
})
.then(() => {
handle = forward(operation).subscribe({
next: observer.next.bind(observer),
error: observer.error.bind(observer),
complete: observer.complete.bind(observer),
});
})
.catch(observer.error.bind(observer));
return () => {
2020-04-10 22:31:12 +02:00
if (handle) {
handle.unsubscribe();
}
2020-04-10 04:40:22 +02:00
};
}),
);
const client = new ApolloClient({
link: ApolloLink.from([
onError(({ graphQLErrors, networkError }) => {
2020-04-10 22:31:12 +02:00
if (graphQLErrors) {
2020-04-10 04:40:22 +02:00
graphQLErrors.forEach(({ message, locations, path }) =>
console.log(`[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`),
);
2020-04-10 22:31:12 +02:00
}
if (networkError) {
console.log(`[Network error]: ${networkError}`);
}
2020-04-10 04:40:22 +02:00
}),
errorLink,
requestLink,
new HttpLink({
uri: 'http://localhost:3333/graphql',
credentials: 'same-origin',
}),
]),
cache: new InMemoryCache(),
});
ReactDOM.render(
<ApolloProvider client={client}>
<App />
</ApolloProvider>,
document.getElementById('root'),
);