Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1aa85540e6 | |||
| 64ee8f8603 |
Generated
+25
@@ -39,6 +39,7 @@
|
||||
"@tanstack/react-query": "^5.96.1",
|
||||
"@tanstack/react-query-devtools": "^5.100.9",
|
||||
"axios": "^1.14.0",
|
||||
"axios-retry": "^4.5.0",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"cmdk": "^1.1.1",
|
||||
@@ -3769,6 +3770,18 @@
|
||||
"proxy-from-env": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/axios-retry": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/axios-retry/-/axios-retry-4.5.0.tgz",
|
||||
"integrity": "sha512-aR99oXhpEDGo0UuAlYcn2iGRds30k366Zfa05XWScR9QaQD4JYiP3/1Qt1u7YlefUOK+cn0CcwoL1oefavQUlQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"is-retry-allowed": "^2.2.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"axios": "0.x || 1.x"
|
||||
}
|
||||
},
|
||||
"node_modules/bail": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz",
|
||||
@@ -5695,6 +5708,18 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/is-retry-allowed": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-2.2.0.tgz",
|
||||
"integrity": "sha512-XVm7LOeLpTW4jV19QSH38vkswxoLud8sQ57YwJVTPWdiaI9I8keEhGFpBlslyVsgdQy4Opg8QOLb8YRgsyZiQg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/isexe": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
"@tanstack/react-query": "^5.96.1",
|
||||
"@tanstack/react-query-devtools": "^5.100.9",
|
||||
"axios": "^1.14.0",
|
||||
"axios-retry": "^4.5.0",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"cmdk": "^1.1.1",
|
||||
|
||||
+1
-1
@@ -1,6 +1,5 @@
|
||||
import { Toaster as Sonner } from "@/components/ui/sonner";
|
||||
import { TooltipProvider } from "@/components/ui/tooltip";
|
||||
import { ThemeProvider } from "@/contexts/ThemeContext";
|
||||
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
|
||||
import { AnimatePresence } from "framer-motion";
|
||||
import { BrowserRouter, Route, Routes, useLocation } from "react-router-dom";
|
||||
@@ -17,6 +16,7 @@ import Projects from "./pages/Projects";
|
||||
import Technologies from "./pages/Technologies";
|
||||
import { QueryProvider } from "./provider/QueryProvider";
|
||||
import OverviewPage from "./components/admin/dashboards/OverviewPage";
|
||||
import { ThemeProvider } from "./provider/ThemeProvider";
|
||||
|
||||
function AnimatedRoutes() {
|
||||
const location = useLocation();
|
||||
|
||||
@@ -1,11 +1,42 @@
|
||||
// src/api/axiosInstance.ts
|
||||
|
||||
import axios from "axios";
|
||||
import axiosRetry from "axios-retry";
|
||||
|
||||
import { API_URL } from "./config";
|
||||
|
||||
import {
|
||||
requestInterceptor,
|
||||
responseSuccessInterceptor,
|
||||
responseErrorInterceptor,
|
||||
} from "./interceptors";
|
||||
|
||||
const axiosInstance = axios.create({
|
||||
baseURL: import.meta.env.VITE_API_URL,
|
||||
timeout: 5000,
|
||||
baseURL: API_URL,
|
||||
timeout: 10000,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
|
||||
axiosInstance.interceptors.request.use(
|
||||
requestInterceptor
|
||||
);
|
||||
|
||||
axiosInstance.interceptors.response.use(
|
||||
responseSuccessInterceptor,
|
||||
responseErrorInterceptor
|
||||
);
|
||||
|
||||
axiosRetry(axiosInstance, {
|
||||
retries: 3,
|
||||
retryDelay: axiosRetry.exponentialDelay,
|
||||
|
||||
retryCondition: (error) =>
|
||||
axiosRetry.isNetworkOrIdempotentRequestError(
|
||||
error
|
||||
) ||
|
||||
error.response?.status === 429,
|
||||
});
|
||||
|
||||
export default axiosInstance;
|
||||
@@ -0,0 +1,9 @@
|
||||
const apiUrl = import.meta.env.VITE_API_URL;
|
||||
|
||||
if (!apiUrl) {
|
||||
throw new Error(
|
||||
"Missing VITE_API_URL environment variable"
|
||||
);
|
||||
}
|
||||
|
||||
export const API_URL = apiUrl;
|
||||
@@ -0,0 +1,38 @@
|
||||
// src/api/errors.ts
|
||||
|
||||
import axios from "axios";
|
||||
import { ApiError } from "./types";
|
||||
|
||||
export function normalizeApiError(error: unknown): ApiError {
|
||||
if (!axios.isAxiosError(error)) {
|
||||
return {
|
||||
status: 0,
|
||||
message: "Unexpected error occurred",
|
||||
};
|
||||
}
|
||||
|
||||
if (error.code === "ECONNABORTED") {
|
||||
return {
|
||||
status: 408,
|
||||
message: "Request timeout",
|
||||
};
|
||||
}
|
||||
|
||||
if (!error.response) {
|
||||
return {
|
||||
status: 0,
|
||||
message: "Network error",
|
||||
};
|
||||
}
|
||||
|
||||
const { status, data } = error.response;
|
||||
|
||||
return {
|
||||
status,
|
||||
message:
|
||||
data?.message ||
|
||||
data?.error ||
|
||||
"Something went wrong",
|
||||
code: data?.code,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import { getAccessToken } from "@/lib/auth";
|
||||
import { AxiosResponse, InternalAxiosRequestConfig } from "axios";
|
||||
import { normalizeApiError } from "./errors";
|
||||
|
||||
export function requestInterceptor(config: InternalAxiosRequestConfig) {
|
||||
const token = getAccessToken();
|
||||
|
||||
if (token) {
|
||||
config.headers.Authorization = `Bearer ${token}`;
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
export const responseSuccessInterceptor = (response: AxiosResponse) => response;
|
||||
|
||||
export function responseErrorInterceptor(error: unknown) {
|
||||
const normalizedError = normalizeApiError(error);
|
||||
|
||||
return Promise.reject(normalizedError);
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
export interface ApiError {
|
||||
status: number;
|
||||
message: string;
|
||||
code?: string;
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useTheme } from "@/contexts/ThemeContext";
|
||||
import { useTheme } from "@/hooks/useTheme";
|
||||
import { motion, AnimatePresence } from "framer-motion";
|
||||
import { Moon, PanelLeftClose, PanelLeftOpen, Sun } from "lucide-react";
|
||||
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
export function getAccessToken() {
|
||||
return localStorage.getItem("accessToken");
|
||||
}
|
||||
Reference in New Issue
Block a user