posts are done
This commit is contained in:
@@ -1,31 +1,89 @@
|
||||
import axios, { type AxiosRequestConfig } from "axios";
|
||||
import { AUTH_FLAG } from "@/context/AuthContext";
|
||||
|
||||
// použij tohle pro API vyžadující autentizaci
|
||||
export const privateApi = axios.create({
|
||||
withCredentials: true, // potřebuje HttpOnly cookies
|
||||
withCredentials: true,
|
||||
baseURL: '',
|
||||
});
|
||||
|
||||
// Set baseURL at runtime (using Function to hide from orval's esbuild)
|
||||
try {
|
||||
const getEnv = new Function('return import.meta.env.VITE_BACKEND_URL');
|
||||
privateApi.defaults.baseURL = getEnv() || "http://localhost:8000";
|
||||
} catch {
|
||||
privateApi.defaults.baseURL = "http://localhost:8000";
|
||||
}
|
||||
let isRefreshing = false;
|
||||
let failedQueue: Array<{
|
||||
resolve: (value?: unknown) => void;
|
||||
reject: (reason?: any) => void;
|
||||
}> = [];
|
||||
|
||||
const processQueue = (error: any = null) => {
|
||||
failedQueue.forEach((promise) => {
|
||||
if (error) {
|
||||
promise.reject(error);
|
||||
} else {
|
||||
promise.resolve();
|
||||
}
|
||||
});
|
||||
failedQueue = [];
|
||||
};
|
||||
|
||||
privateApi.interceptors.response.use(
|
||||
(res) => res,
|
||||
async (error) => {
|
||||
const original = error.config;
|
||||
|
||||
if (error.response?.status === 400 && error.response?.data) {
|
||||
const data = error.response.data;
|
||||
if (typeof data === 'object' && !Array.isArray(data)) {
|
||||
const firstKey = Object.keys(data)[0];
|
||||
if (firstKey && Array.isArray(data[firstKey]) && data[firstKey].length > 0) {
|
||||
error.message = data[firstKey][0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (error.response?.status === 401 && !original._retry) {
|
||||
if (original.url?.includes("/api/account/logout/")) {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
|
||||
if (error.response?.data?.code === "user_not_found") {
|
||||
processQueue(error);
|
||||
isRefreshing = false;
|
||||
localStorage.removeItem(AUTH_FLAG);
|
||||
window.location.href = "/social/login";
|
||||
return Promise.reject(error);
|
||||
}
|
||||
|
||||
if (original.url?.includes("/api/account/token/refresh/")) {
|
||||
processQueue(error);
|
||||
isRefreshing = false;
|
||||
localStorage.removeItem(AUTH_FLAG);
|
||||
try {
|
||||
await privateApi.post("/api/account/logout/");
|
||||
} catch {
|
||||
// cookies may already be expired
|
||||
}
|
||||
window.location.href = "/social/login";
|
||||
return Promise.reject(error);
|
||||
}
|
||||
|
||||
if (isRefreshing) {
|
||||
return new Promise((resolve, reject) => {
|
||||
failedQueue.push({ resolve, reject });
|
||||
})
|
||||
.then(() => privateApi(original))
|
||||
.catch((err) => Promise.reject(err));
|
||||
}
|
||||
|
||||
original._retry = true;
|
||||
isRefreshing = true;
|
||||
|
||||
try {
|
||||
await privateApi.post("/api/account/token/refresh/");
|
||||
processQueue();
|
||||
return privateApi(original);
|
||||
} catch {
|
||||
// optional: logout
|
||||
} catch (refreshError) {
|
||||
processQueue(refreshError);
|
||||
return Promise.reject(refreshError);
|
||||
} finally {
|
||||
isRefreshing = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,15 +91,10 @@ privateApi.interceptors.response.use(
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
export const privateMutator = async <T>(
|
||||
config: AxiosRequestConfig
|
||||
): Promise<T> => {
|
||||
// If sending FormData, remove Content-Type header to let axios set it with boundary
|
||||
export const privateMutator = async <T>(config: AxiosRequestConfig): Promise<T> => {
|
||||
if (config.data instanceof FormData) {
|
||||
delete config.headers?.['Content-Type'];
|
||||
}
|
||||
|
||||
const response = await privateApi.request<T>(config);
|
||||
return response.data;
|
||||
};
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user