import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse, AxiosError, CancelTokenSource } from 'axios';
import { CookieHandler } from '@/utils/cookie.ts';
import { API_URL } from '@/utils/constants'
class AxiosService {
private axiosInstance: AxiosInstance;
private cancelTokenSources: CancelTokenSource[];
constructor(baseURL: string = API_URL, timeout: number = 50000) {
this.axiosInstance = axios.create({
baseURL,
timeout,
headers: {
'Content-Type': 'application/json',
},
});
this.cancelTokenSources = [];
this.setupInterceptors();
}
private setupInterceptors() {
this.axiosInstance.interceptors.request.use(
(config: AxiosRequestConfig) => {
this.addUserAuthentication(config);
this.addRequestTimestamp(config);
this.logRequest(config);
return config;
},
(error: AxiosError) => Promise.reject(error)
);
}
private addUserAuthentication(config: AxiosRequestConfig) {
const userToken = CookieHandler.getCookie('userToken');
if (userToken) {
config.headers!['token'] = `${userToken}`;
}
}
private addRequestTimestamp(config: AxiosRequestConfig) {
config.headers!['Request-Timestamp'] = new Date().toISOString();
}
private logRequest(config: AxiosRequestConfig) {
console.log(`Making ${config.method?.toUpperCase()} request to ${config.url}`);
}
private async makeRequest<T>(method: string, url: string, data?: any, config?: AxiosRequestConfig): Promise<T> {
const source = axios.CancelToken.source();
this.cancelTokenSources.push(source);
try {
const response: AxiosResponse<T> = await this.axiosInstance.request<T>({
method,
url,
data,
cancelToken: source.token,
...config,
});
return response.data;
} catch (error: any) {
if (axios.isCancel(error)) {
console.log('Request canceled:', error.message);
throw new Error('Request canceled');
} else if (error.response) {
const errorMessage = error.response.data.message || 'An error occurred';
throw new Error(errorMessage);
} else if (error.request) {
throw new Error('No response received');
} else {
throw new Error('Error sending request');
}
} finally {
this.removeCancelledRequests();
}
}
public async manuallyTriggerRequest<T>(method: string, url: string, data?: any, config?: AxiosRequestConfig): Promise<T> {
return await this.makeRequest<T>(method, url, data, config);
}
public async cancelRequests() {
this.cancelTokenSources.forEach(source => source.cancel('Request canceled by user'));
}
private removeCancelledRequests() {
this.cancelTokenSources = this.cancelTokenSources.filter(source => source.token.reason === undefined);
}
public async get<T>(url: string, config?: AxiosRequestConfig): Promise<T> {
try {
return await this.makeRequest<T>('GET', url, undefined, config);
} catch (error: any) {
console.error('Error in GET request:', error.message);
throw error;
}
}
public async post<T>(url: string, data: any, config?: AxiosRequestConfig): Promise<T> {
try {
return await this.makeRequest<T>('POST', url, data, config);
} catch (error: any) {
console.error('Error in POST request:', error.message);
throw error;
}
}
public async put<T>(url: string, data: any, config?: AxiosRequestConfig): Promise<T> {
try {
return await this.makeRequest<T>('PUT', url, data, config);
} catch (error: any) {
console.error('Error in PUT request:', error.message);
throw error;
}
}
public async delete<T>(url: string, config?: AxiosRequestConfig): Promise<T> {
try {
return await this.makeRequest<T>('DELETE', url, undefined, config);
} catch (error: any) {
console.error('Error in DELETE request:', error.message);
throw error;
}
}
public async sendOptions<T>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T> {
try {
const response = await this.axiosInstance.request<T>({
method: 'OPTIONS',
url: url,
data: data,
...config,
});
const allowMethods = response.headers['Access-Control-Allow-Methods'];
if (response.status === 204 && allowMethods && allowMethods.includes('POST')) {
return await this.post<T>(url, data, config);
}
return response.data;
} catch (error: any) {
console.error('Error in OPTIONS request:', error.message);
throw error;
}
}
}
export default new AxiosService();