Axios Original
FadadOussama
typescript
10 months ago
7.3 kB
14
Indexable
import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from "axios";
import { cookies } from "next/headers";
import { COOKIE_CONFIG } from "@/constants/cookies";
// Create axios instance with base configuration
const createAxiosInstance = (): AxiosInstance => {
const instance = axios.create({
baseURL: process.env.NAREVA_URL,
timeout: 30000,
});
return instance;
};
// Server-side axios instance
export const apiClient = createAxiosInstance();
// Request interceptor to add auth token
apiClient.interceptors.request.use(
async (config) => {
try {
const cookieStore = await cookies();
const token = cookieStore.get(COOKIE_CONFIG.AUTH_TOKEN.name);
if (token) {
config.headers.Authorization = `Bearer ${token.value}`;
}
// Set Content-Type based on data type
if (config.data instanceof FormData) {
// Let axios handle FormData Content-Type automatically (it will set multipart/form-data with boundary)
delete config.headers["Content-Type"];
} else {
// Set JSON Content-Type for other data types
config.headers["Content-Type"] = "application/json";
}
} catch (error) {
console.error("Error getting auth token:", error);
}
return config;
},
(error) => {
return Promise.reject(error);
}
);
// Helper function to clear auth cookies and redirect to login
async function clearAuthCookiesAndRedirect() {
try {
const cookieStore = await cookies();
// Clear auth-related cookies
cookieStore.delete(COOKIE_CONFIG.AUTH_TOKEN.name);
cookieStore.delete(COOKIE_CONFIG.REFRESH_TOKEN.name);
cookieStore.delete(COOKIE_CONFIG.USER_DATA.name);
console.log("Auth cookies cleared");
// Redirect to login - use window.location for client-side or throw error for server-side
if (typeof window !== "undefined") {
window.location.href = "/login";
} else {
// For server-side, we'll let the middleware handle the redirect
console.log("Server-side auth failure - middleware will handle redirect");
}
} catch (error) {
console.error("Error clearing cookies:", error);
}
}
// Response interceptor to handle token refresh
apiClient.interceptors.response.use(
(response: AxiosResponse) => {
return response;
},
async (error) => {
const originalRequest = error.config;
// If error is 401 and we haven't already tried to refresh
if (error.response?.status === 401 && !originalRequest._retry) {
originalRequest._retry = true;
try {
const refreshSuccess = await refreshToken();
if (refreshSuccess) {
// Get the new token and retry the original request
const cookieStore = await cookies();
const newToken = cookieStore.get(COOKIE_CONFIG.AUTH_TOKEN.name);
if (newToken) {
originalRequest.headers.Authorization = `Bearer ${newToken.value}`;
return apiClient(originalRequest);
}
} else {
// Refresh token failed - clear cookies and redirect
console.error("Refresh token failed, redirecting to login");
await clearAuthCookiesAndRedirect();
return Promise.reject(
new Error("Session expired. Please login again.")
);
}
} catch (refreshError) {
console.error("Token refresh failed:", refreshError);
await clearAuthCookiesAndRedirect();
return Promise.reject(
new Error("Authentication failed. Please login again.")
);
}
}
// If it's a 401 error after refresh attempt, redirect to login
if (error.response?.status === 401) {
console.error("401 Unauthorized - redirecting to login");
await clearAuthCookiesAndRedirect();
return Promise.reject(
new Error("Authentication failed. Please login again.")
);
}
return Promise.reject(error);
}
);
// Refresh token function
async function refreshToken(): Promise<boolean> {
try {
const cookieStore = await cookies();
const accessToken = cookieStore.get(COOKIE_CONFIG.AUTH_TOKEN.name);
const refreshTokenValue = cookieStore.get(COOKIE_CONFIG.REFRESH_TOKEN.name);
if (!accessToken || !refreshTokenValue) {
console.error("No access token or refresh token found");
return false;
}
const response = await axios.post(
`${process.env.NAREVA_URL}/api/Shared/Authenticate/RefreshToken`,
{
accessToken: accessToken.value,
refreshToken: refreshTokenValue.value,
},
{
headers: {
"Content-Type": "application/json",
},
}
);
if (response.data.success) {
// Update tokens in cookies
cookieStore.set(COOKIE_CONFIG.AUTH_TOKEN.name, response.data.data.token, {
...COOKIE_CONFIG.AUTH_TOKEN,
});
if (response.data.data.refreshToken) {
cookieStore.set(
COOKIE_CONFIG.REFRESH_TOKEN.name,
response.data.data.refreshToken,
{
...COOKIE_CONFIG.REFRESH_TOKEN,
}
);
}
return true;
}
return false;
} catch (error) {
console.error("Token refresh failed:", error);
return false;
}
}
// Helper function for making API requests with better error handling
export const makeRequest = async <T = any>(
config: AxiosRequestConfig
): Promise<T> => {
try {
const response = await apiClient(config);
return response.data;
} catch (error) {
if (axios.isAxiosError(error)) {
// Handle different error types
if (error.response) {
console.error("API Error:", error.response.data);
// Server responded with error status
throw new Error(
error.response.data?.title || `HTTP Error: ${error.response.status}`
);
} else if (error.request) {
// Request was made but no response received
throw new Error("Network error: No response from server");
} else {
// Something else happened
throw new Error(`Request error: ${error.message}`);
}
}
throw error;
}
};
// Convenience methods for different HTTP verbs
export const apiGet = <T = any>(
url: string,
config?: AxiosRequestConfig
): Promise<T> => {
return makeRequest<T>({ ...config, method: "GET", url });
};
export const apiPost = <T = any>(
url: string,
data?: any,
config?: AxiosRequestConfig
): Promise<T> => {
return makeRequest<T>({ ...config, method: "POST", url, data });
};
export const apiPut = <T = any>(
url: string,
data?: any,
config?: AxiosRequestConfig
): Promise<T> => {
return makeRequest<T>({ ...config, method: "PUT", url, data });
};
export const apiDelete = <T = any>(
url: string,
config?: AxiosRequestConfig
): Promise<T> => {
return makeRequest<T>({ ...config, method: "DELETE", url });
};
export const apiPatch = <T = any>(
url: string,
data?: any,
config?: AxiosRequestConfig
): Promise<T> => {
return makeRequest<T>({ ...config, method: "PATCH", url, data });
};
export default apiClient;Editor is loading...
Leave a Comment