app/src/services/RequestService.tstypescript
import axios, {
AxiosInstance,
AxiosError,
InternalAxiosRequestConfig,
} from "axios";
import { setupCache } from "axios-cache-interceptor";
import AuthService from "./AuthService";
import store from "@/store";
import router from "@/router";
import i18n from "@/plugins/vue-i18n";
import { logRequest, error as logError } from "@/utils/logging";
import ERROR_CODES from "@/utils/error_codes";
import env from "../utils/env";
export interface RequestConfig extends InternalAxiosRequestConfig {
requestStarted?: number;
skipRoutingToLogin?: boolean;
}
/**
* Axios request service with authentication and error handling.
* @module RequestService
*/
const ax = setupCache(
axios.create({
baseURL: getApiDomain(),
}),
{ headerInterpreter: () => 50 }
) as unknown as AxiosInstance;
ax.interceptors.response.use(
(response: any) => {
logRequest(
response.status,
Date.now() - (response.config as RequestConfig)?.requestStarted!,
response.config.url
);
return response;
},
(error: AxiosError<unknown, RequestConfig>) => {
logRequest(
error?.response?.status || 0,
Date.now() - (error?.config as RequestConfig)?.requestStarted!,
error.config?.url || ""
);
if (!error.response?.status) {
AuthService.removeToken();
store.commit("setLoginState", false);
return Promise.reject(error);
}
switch (error.response.status) {
case 401:
if (error.config?.headers?.Authorization) {
AuthService.removeToken();
store.commit("setLoginState", false);
}
if (!(error.config as RequestConfig)?.skipRoutingToLogin)
router
.push({
name: "Login",
params: { locale: i18n.global.locale.value },
})
.catch(() => {
logError(
"Redundant navigation to login",
ERROR_CODES.REDUNDANT_ROUTING
);
});
break;
case 403:
if (error.config?.headers?.Authorization) {
AuthService.removeToken();
store.commit("setLoginState", false);
}
if (!(error.config as RequestConfig)?.skipRoutingToLogin)
router
.push({
name: "Login",
params: { locale: i18n.global.locale.value },
})
.catch(() => {
logError(
"Redundant navigation to login",
ERROR_CODES.REDUNDANT_ROUTING
);
});
break;
default:
}
return Promise.reject(error);
}
);
ax.interceptors.request.use(
(config: RequestConfig) => {
const token = AuthService.getAuthToken();
if (token) {
config.headers.Authorization = "Bearer " + token;
}
const locale = i18n.global.locale.value;
if (locale) config.headers["Accept-Language"] = locale;
return config;
},
(error: AxiosError) => {
return Promise.reject(error);
}
);
ax.interceptors.request.use((config: RequestConfig) => {
config.requestStarted = Date.now();
return config;
});
/**
* Get the API domain based on the environment.
*
* @export
* @returns {("https://api.choreo-planer.de/" | "http://localhost:3000/")}
*/
export function getApiDomain() {
return env.PROD ? "https://api.choreo-planer.de/" : "http://localhost:3000/";
}
export default ax;