|
|
@@ -1,7 +1,8 @@
|
|
|
import { boot } from "quasar/wrappers";
|
|
|
-import { useAuth } from "src/composables/useAuth";
|
|
|
import { Cookies, Notify } from "quasar";
|
|
|
import axios from "axios";
|
|
|
+import { useRouter } from "vue-router";
|
|
|
+import { useAuth } from "src/composables/useAuth";
|
|
|
|
|
|
const api = axios.create({
|
|
|
baseURL: process.env.API_URL + "/api",
|
|
|
@@ -26,77 +27,83 @@ api.interceptors.request.use(
|
|
|
);
|
|
|
|
|
|
let isRefreshing = false;
|
|
|
-let validQueue = [];
|
|
|
+let failedQueue = [];
|
|
|
+
|
|
|
+const processQueue = (error, token = null) => {
|
|
|
+ failedQueue.forEach((prom) => {
|
|
|
+ if (error) {
|
|
|
+ prom.reject(error);
|
|
|
+ } else {
|
|
|
+ prom.config.headers["Authorization"] = "Bearer " + token;
|
|
|
+ prom.resolve(api(prom.config));
|
|
|
+ }
|
|
|
+ });
|
|
|
+ failedQueue = [];
|
|
|
+};
|
|
|
|
|
|
-const errorInterceptor = async (error) => {
|
|
|
- if (error.config?.retryCount) {
|
|
|
- error.config.retryCount = 0;
|
|
|
- }
|
|
|
+const errorInterceptor = async (
|
|
|
+ error,
|
|
|
+ router,
|
|
|
+ setAuthTokens,
|
|
|
+ eraseAuthTokens,
|
|
|
+) => {
|
|
|
+ const originalRequest = error.config;
|
|
|
|
|
|
- if (error.config.retryCount >= 3) {
|
|
|
+ if (error.response?.status === 422) {
|
|
|
return Promise.reject(error);
|
|
|
}
|
|
|
|
|
|
- error.config.retryCount++;
|
|
|
-
|
|
|
- if (!error.response) {
|
|
|
+ if (
|
|
|
+ error.response?.status !== 401 ||
|
|
|
+ originalRequest.url.includes("/refresh")
|
|
|
+ ) {
|
|
|
Notify.create({
|
|
|
- message: error.message,
|
|
|
+ message: error?.response?.data?.message ?? error.message,
|
|
|
type: "negative",
|
|
|
});
|
|
|
return Promise.reject(error);
|
|
|
}
|
|
|
|
|
|
- if (error.response.status === 401) {
|
|
|
- if (error?.config?.url === "/login") {
|
|
|
- Notify.create({
|
|
|
- message: error.response.data.message,
|
|
|
- type: "negative",
|
|
|
- });
|
|
|
- return Promise.reject(error);
|
|
|
- }
|
|
|
+ if (error?.config?.url === "/login") {
|
|
|
+ Notify.create({
|
|
|
+ message: error.response.data.message,
|
|
|
+ type: "negative",
|
|
|
+ });
|
|
|
+ return Promise.reject(error);
|
|
|
+ }
|
|
|
|
|
|
- if (isRefreshing) {
|
|
|
- return new Promise((resolve, reject) => {
|
|
|
- validQueue.push({ resolve, reject, config: error.config });
|
|
|
- });
|
|
|
- }
|
|
|
+ if (isRefreshing) {
|
|
|
+ return new Promise((resolve, reject) => {
|
|
|
+ failedQueue.push({ resolve, reject, config: originalRequest });
|
|
|
+ })
|
|
|
+ .then((res) => res)
|
|
|
+ .catch((err) => Promise.reject(err));
|
|
|
+ }
|
|
|
|
|
|
- isRefreshing = true;
|
|
|
- try {
|
|
|
- await useAuth().refreshToken();
|
|
|
- } catch (error) {
|
|
|
- validQueue = [];
|
|
|
- Cookies.remove("access_token");
|
|
|
- Cookies.remove("refresh_token");
|
|
|
- if (window.location.pathname !== "/login") {
|
|
|
- window.location.href = "/login";
|
|
|
- }
|
|
|
- return Promise.reject(error);
|
|
|
+ isRefreshing = true;
|
|
|
+ try {
|
|
|
+ const refreshToken = Cookies.get("refresh_token");
|
|
|
+ if (!refreshToken) {
|
|
|
+ router.push("/login");
|
|
|
+ return Promise.reject(new Error("No refresh token available."));
|
|
|
}
|
|
|
|
|
|
- try {
|
|
|
- validQueue.forEach((request) => {
|
|
|
- request.resolve(api.request(request.config));
|
|
|
- });
|
|
|
- validQueue = [];
|
|
|
-
|
|
|
- return await api.request(error.config);
|
|
|
- } catch (error) {
|
|
|
- Notify.create({
|
|
|
- message: error.response.data.message,
|
|
|
- type: "negative",
|
|
|
- });
|
|
|
- } finally {
|
|
|
- isRefreshing = false;
|
|
|
- }
|
|
|
+ const response = await api.post("/refresh", {
|
|
|
+ refresh_token: refreshToken,
|
|
|
+ });
|
|
|
+ const newAccessToken = response.data.payload.access_token;
|
|
|
+ setAuthTokens(response.data.payload);
|
|
|
+ originalRequest.headers["Authorization"] = `Bearer ${newAccessToken}`;
|
|
|
+ processQueue(null, newAccessToken);
|
|
|
+ return api(originalRequest);
|
|
|
+ } catch (err) {
|
|
|
+ processQueue(err, null);
|
|
|
+ eraseAuthTokens();
|
|
|
+ router.push("/login");
|
|
|
+ return Promise.reject(err);
|
|
|
+ } finally {
|
|
|
+ isRefreshing = false;
|
|
|
}
|
|
|
-
|
|
|
- Notify.create({
|
|
|
- message: error.response.data.message,
|
|
|
- type: "negative",
|
|
|
- });
|
|
|
- return Promise.reject(error);
|
|
|
};
|
|
|
|
|
|
const successInterceptor = (response) => {
|
|
|
@@ -110,9 +117,12 @@ const successInterceptor = (response) => {
|
|
|
};
|
|
|
|
|
|
export default boot(({ app }) => {
|
|
|
+ const router = useRouter();
|
|
|
+ const { setAuthTokens, eraseAuthTokens } = useAuth();
|
|
|
+
|
|
|
api.interceptors.response.use(
|
|
|
(response) => successInterceptor(response),
|
|
|
- (error) => errorInterceptor(error),
|
|
|
+ (error) => errorInterceptor(error, router, setAuthTokens, eraseAuthTokens),
|
|
|
);
|
|
|
|
|
|
// for use inside Vue files (Options API) through this.$axios and this.$api
|