pramod_Bug#494 : update axios client and auth repository for public/private route handling #207

Merged
vikas.nale merged 1 commits from pramod_Bug#494 into Issues_Jun_3W 2025-06-20 09:08:07 +00:00
3 changed files with 48 additions and 38 deletions
Showing only changes of commit cde398ebfb - Show all commits

View File

@ -67,9 +67,13 @@ const ResetPasswordPage = () => {
navigate("/auth/login", { replace: true }); navigate("/auth/login", { replace: true });
// setLoading(false); // setLoading(false);
} catch (error) { } catch (error) {
showToast("Link is expries or Invalid ", "error"); debugger;
setTokenExpired(true);
setLoading(false); setLoading(false);
if (error?.response?.status === 400) {
showToast("Please check valid Credentials", "error");
} else {
setTokenExpired(true);
}
} }
}; };
@ -77,7 +81,10 @@ const ResetPasswordPage = () => {
return ( return (
<AuthWrapper> <AuthWrapper>
<h4 className="mb-2 ">Invalid Link 🔒</h4> <h4 className="mb-2 ">Invalid Link 🔒</h4>
<p className="mb-4" style={{fontSize: "12px"}}>This link appears to be invalid or expired. Please use the 'Forgot Password' feature to set your new password.</p> <p className="mb-4" style={{ fontSize: "12px" }}>
This link appears to be invalid or expired. Please use the 'Forgot
Password' feature to set your new password.
</p>
<div className="text-center mb-4"> <div className="text-center mb-4">
<Link to="/auth/forgot-password" className="btn btn-outline-primary"> <Link to="/auth/forgot-password" className="btn btn-outline-primary">
Go to Forgot Password Go to Forgot Password
@ -142,7 +149,6 @@ const ResetPasswordPage = () => {
borderTopLeftRadius: 0, borderTopLeftRadius: 0,
borderBottomLeftRadius: 0, borderBottomLeftRadius: 0,
borderLeft: 0, borderLeft: 0,
}} }}
> >
{hidepass ? ( {hidepass ? (
@ -185,7 +191,6 @@ const ResetPasswordPage = () => {
borderTopLeftRadius: 0, borderTopLeftRadius: 0,
borderBottomLeftRadius: 0, borderBottomLeftRadius: 0,
borderLeft: 0, borderLeft: 0,
}} }}
> >
{hidepass1 ? ( {hidepass1 ? (

View File

@ -1,18 +1,20 @@
import { api } from "../utils/axiosClient"; import { api } from "../utils/axiosClient";
const AuthRepository = { const AuthRepository = {
login: (data) => api.post("/api/auth/login", data), // Public routes (no auth token required)
refreshToken: (data) => api.post("/api/auth/refresh-token", data), login: (data) => api.postPublic("/api/auth/login", data),
refreshToken: (data) => api.postPublic("/api/auth/refresh-token", data),
forgotPassword: (data) => api.postPublic("/api/auth/forgot-password", data),
resetPassword: (data) => api.postPublic("/api/auth/reset-password", data),
sendOTP: (data) => api.postPublic("/api/auth/send-otp", data),
verifyOTP: (data) => api.postPublic("/api/auth/login-otp", data),
register: (data) => api.postPublic("/api/auth/register", data),
sendMail: (data) => api.postPublic("/api/auth/sendmail", data),
// Protected routes (require auth token)
logout: (data) => api.post("/api/auth/logout", data), logout: (data) => api.post("/api/auth/logout", data),
profile: () => api.get(`/api/user/profile`), profile: () => api.get("/api/user/profile"),
register: (data) => api.post("api/auth/register", data), changepassword: (data) => api.post("/api/auth/change-password", data),
resetPassword: (data) => api.post("/api/auth/reset-password", data),
forgotPassword: (data) => api.post("/api/auth/forgot-password", data),
sendMail: (data) => api.post("/api/auth/sendmail", data),
changepassword: ( data ) => api.post( "/api/auth/change-password", data ),
sendOTP: ( data ) => api.post( 'api/auth/send-otp', data ),
verifyOTP:(data)=>api.post("api/auth/login-otp",data)
}; };
export default AuthRepository; export default AuthRepository;

View File

@ -5,18 +5,22 @@ import showToast from "../services/toastService";
const base_Url = process.env.VITE_BASE_URL; const base_Url = process.env.VITE_BASE_URL;
// const base_Url = "https://api.marcoaiot.com"; // const base_Url = "https://api.marcoaiot.com";
export const axiosClient = axios.create({ export const axiosClient = axios.create({
baseURL: base_Url, // Your Web API URL baseURL: base_Url,
withCredentials: false, // Required if the API uses cookies withCredentials: false,
headers: { headers: {
"Content-Type": "application/json", // Specify the content type "Content-Type": "application/json",
}, },
}); });
// Auto retry failed requests (e.g., network issues)
axiosRetry(axiosClient, { retries: 3 }); axiosRetry(axiosClient, { retries: 3 });
// Request interceptor to add Bearer token // Request Interceptor Add Bearer token if required
axiosClient.interceptors.request.use( axiosClient.interceptors.request.use(
async (config) => { async (config) => {
if (config.authRequired) { const requiresAuth = config.authRequired !== false; // default to true
if (requiresAuth) {
const token = localStorage.getItem("jwtToken"); const token = localStorage.getItem("jwtToken");
if (token) { if (token) {
config.headers["Authorization"] = `Bearer ${token}`; config.headers["Authorization"] = `Bearer ${token}`;
@ -25,25 +29,24 @@ axiosClient.interceptors.request.use(
config._retry = false; config._retry = false;
} }
} }
return config; return config;
}, },
(error) => Promise.reject(error) (error) => Promise.reject(error)
); );
// // Response interceptor to handle responses globally (optional) // 🔄 Response Interceptor Handle 401, refresh token, etc.
// Add an interceptor to handle expired tokens
axiosClient.interceptors.response.use( axiosClient.interceptors.response.use(
(response) => response, (response) => response,
async (error) => { async (error) => {
const originalRequest = error.config; const originalRequest = error.config;
// Prevent infinite loop // Skip retry for public requests or already retried ones
if (!originalRequest || originalRequest._retry) { if (!originalRequest || originalRequest._retry || originalRequest.authRequired === false) {
return Promise.reject(error); return Promise.reject(error);
} }
// Only show one toast per request // Avoid showing multiple toasts
if (!originalRequest._toastShown) { if (!originalRequest._toastShown) {
originalRequest._toastShown = true; originalRequest._toastShown = true;
@ -59,7 +62,6 @@ axiosClient.interceptors.response.use(
const isRefreshRequest = error.config.url.includes("refresh-token"); const isRefreshRequest = error.config.url.includes("refresh-token");
if (status === 401 && !isRefreshRequest) { if (status === 401 && !isRefreshRequest) {
// Mark as retried to avoid loops
originalRequest._retry = true; originalRequest._retry = true;
const refreshToken = localStorage.getItem("refreshToken"); const refreshToken = localStorage.getItem("refreshToken");
@ -70,7 +72,7 @@ axiosClient.interceptors.response.use(
} }
try { try {
// Refresh token // Refresh token call
const res = await axiosClient.post("/api/Auth/refresh-token", { const res = await axiosClient.post("/api/Auth/refresh-token", {
token: localStorage.getItem("jwtToken"), token: localStorage.getItem("jwtToken"),
refreshToken, refreshToken,
@ -78,15 +80,13 @@ axiosClient.interceptors.response.use(
const { token, refreshToken: newRefreshToken } = res.data.data; const { token, refreshToken: newRefreshToken } = res.data.data;
// Save new tokens // Save updated tokens
localStorage.setItem("jwtToken", token); localStorage.setItem("jwtToken", token);
localStorage.setItem("refreshToken", newRefreshToken); localStorage.setItem("refreshToken", newRefreshToken);
// Set Authorization header // Retry original request with new token
originalRequest.headers["Authorization"] = `Bearer ${token}`; originalRequest.headers["Authorization"] = `Bearer ${token}`;
return axiosClient(originalRequest);
// Optional: Instead of retrying, you may choose to reload app or go to home
return axiosClient(originalRequest); // <== only retry once
} catch (refreshError) { } catch (refreshError) {
redirectToLogin(); redirectToLogin();
return Promise.reject(refreshError); return Promise.reject(refreshError);
@ -96,11 +96,12 @@ axiosClient.interceptors.response.use(
showToast("An unknown error occurred.", "error"); showToast("An unknown error occurred.", "error");
} }
} }
return Promise.reject(error); return Promise.reject(error);
} }
); );
// Generic API Call // Generic API function
const apiRequest = async (method, url, data = {}, config = {}) => { const apiRequest = async (method, url, data = {}, config = {}) => {
try { try {
const response = await axiosClient({ const response = await axiosClient({
@ -116,15 +117,16 @@ const apiRequest = async (method, url, data = {}, config = {}) => {
} }
}; };
// Exported API wrapper
export const api = { export const api = {
// For public routes like login, set authRequired: false // Public routes (no token required)
postPublic: (url, data = {}, customHeaders = {}) => postPublic: (url, data = {}, customHeaders = {}) =>
apiRequest("post", url, data, { apiRequest("post", url, data, {
headers: { ...customHeaders }, headers: { ...customHeaders },
authRequired: false, authRequired: false,
}), }),
// For protected routes, authRequired defaults to true // Authenticated routes
get: (url, params = {}, customHeaders = {}) => get: (url, params = {}, customHeaders = {}) =>
apiRequest("get", url, params, { apiRequest("get", url, params, {
headers: { ...customHeaders }, headers: { ...customHeaders },
@ -149,7 +151,8 @@ export const api = {
authRequired: true, authRequired: true,
}), }),
}; };
//export default axiosClient;
// Redirect helper
function redirectToLogin() { function redirectToLogin() {
window.location.href = "/auth/login"; window.location.href = "/auth/login";
} }