102 lines
3.1 KiB
TypeScript
102 lines
3.1 KiB
TypeScript
|
import { logout } from "@/utils/auth";
|
||
|
import { onError } from "@apollo/client/link/error";
|
||
|
import { fromPromise } from "@apollo/client/core";
|
||
|
import { refreshAccessToken } from "./utils";
|
||
|
import { GraphQLError } from "graphql";
|
||
|
import { generateTokenHeader } from "./auth";
|
||
|
|
||
|
let isRefreshing = false;
|
||
|
let pendingRequests: any[] = [];
|
||
|
|
||
|
const resolvePendingRequests = () => {
|
||
|
pendingRequests.map((callback) => callback());
|
||
|
pendingRequests = [];
|
||
|
};
|
||
|
|
||
|
const isAuthError = (graphQLError: GraphQLError | undefined) => {
|
||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||
|
// @ts-ignore
|
||
|
return graphQLError && [403, 401].includes(graphQLError.status_code);
|
||
|
};
|
||
|
|
||
|
const errorLink = onError(
|
||
|
({ graphQLErrors, networkError, forward, operation }) => {
|
||
|
console.debug("We have an apollo error", [graphQLErrors, networkError]);
|
||
|
if (
|
||
|
graphQLErrors?.some((graphQLError) => isAuthError(graphQLError)) ||
|
||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||
|
// @ts-ignore
|
||
|
networkError === 401
|
||
|
) {
|
||
|
console.debug("It's a authorization error (statusCode 401)");
|
||
|
let forwardOperation;
|
||
|
|
||
|
if (!isRefreshing) {
|
||
|
console.debug("Setting isRefreshing to true");
|
||
|
isRefreshing = true;
|
||
|
|
||
|
forwardOperation = fromPromise(
|
||
|
refreshAccessToken()
|
||
|
.then((res) => {
|
||
|
if (res !== true) {
|
||
|
// failed to refresh the token
|
||
|
throw "Failed to refresh the token";
|
||
|
}
|
||
|
resolvePendingRequests();
|
||
|
|
||
|
const context = operation.getContext();
|
||
|
const oldHeaders = context.headers;
|
||
|
|
||
|
operation.setContext({
|
||
|
headers: {
|
||
|
...oldHeaders,
|
||
|
authorization: generateTokenHeader(),
|
||
|
},
|
||
|
});
|
||
|
return true;
|
||
|
})
|
||
|
.catch((e) => {
|
||
|
console.debug("Something failed, let's logout", e);
|
||
|
pendingRequests = [];
|
||
|
// don't perform a logout since we don't have any working access/refresh tokens
|
||
|
logout(false);
|
||
|
return;
|
||
|
})
|
||
|
.finally(() => {
|
||
|
isRefreshing = false;
|
||
|
})
|
||
|
).filter((value) => Boolean(value));
|
||
|
} else {
|
||
|
forwardOperation = fromPromise(
|
||
|
new Promise((resolve) => {
|
||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||
|
// @ts-ignore
|
||
|
pendingRequests.push(() => resolve());
|
||
|
})
|
||
|
);
|
||
|
}
|
||
|
|
||
|
return forwardOperation.flatMap(() => forward(operation));
|
||
|
}
|
||
|
|
||
|
if (graphQLErrors) {
|
||
|
graphQLErrors.map(
|
||
|
(graphQLError: GraphQLError & { status_code?: number }) => {
|
||
|
if (graphQLError?.status_code !== 401) {
|
||
|
console.log(
|
||
|
`[GraphQL error]: Message: ${graphQLError.message}, Location: ${graphQLError.locations}, Path: ${graphQLError.path}`
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
);
|
||
|
}
|
||
|
|
||
|
if (networkError) {
|
||
|
console.error(`[Network error]: ${networkError}`);
|
||
|
console.debug(JSON.stringify(networkError));
|
||
|
}
|
||
|
}
|
||
|
);
|
||
|
|
||
|
export default errorLink;
|