add: full auth functionality
add: zustand auth store add: jwt auto refresh
This commit is contained in:
@@ -1,25 +0,0 @@
|
||||
import { api } from './axiosInstance'
|
||||
|
||||
export const login = async ({ phone, password }: { phone: string; password: string }) => {
|
||||
const res = await api.post('/login', { phone, password })
|
||||
console.log(res);
|
||||
return res.data // { user: { id, name, email, token } }
|
||||
}
|
||||
|
||||
export const signup = async ({
|
||||
phone,
|
||||
password,
|
||||
}: {
|
||||
phone: string
|
||||
password: string
|
||||
}) => {
|
||||
const res = await api.post('/signup', { phone, password })
|
||||
console.log(res);
|
||||
return res.data
|
||||
}
|
||||
|
||||
export const checkUserExists = async (phone: string) => {
|
||||
const res = await api.get(`/exists/${encodeURIComponent(phone)}`)
|
||||
console.log(res);
|
||||
return res.data.exists as boolean
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import { api } from "../axiosInstance";
|
||||
|
||||
export type LoginRequestModel = {
|
||||
phone: string,
|
||||
password: string,
|
||||
}
|
||||
|
||||
export const login = async (model: LoginRequestModel) => {
|
||||
const res = await api.post('/login', model)
|
||||
return res.data
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import { api } from "../axiosInstance";
|
||||
|
||||
export const fetchMe = async () => {
|
||||
const res = await api.get('/user')
|
||||
return res.data
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import { api } from "../axiosInstance";
|
||||
|
||||
export type RefreshTokenRequestModel = {
|
||||
token: string,
|
||||
}
|
||||
|
||||
export const refreshToken = async (model: RefreshTokenRequestModel) => {
|
||||
const res = await api.post('/token', model)
|
||||
return res.data
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import { api } from "../axiosInstance";
|
||||
|
||||
export type OtpRequestModel = {
|
||||
phone: string,
|
||||
}
|
||||
|
||||
export const requestOtp = async ({ phone }: OtpRequestModel) => {
|
||||
const res = await api.get(`/otp/${encodeURIComponent(phone)}`)
|
||||
return res.data
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import { api } from "../axiosInstance";
|
||||
|
||||
export type SignupRequestModel = {
|
||||
phone: string,
|
||||
password: string,
|
||||
}
|
||||
|
||||
export const signup = async (model: SignupRequestModel) => {
|
||||
const res = await api.post('/signup', model)
|
||||
return res.data
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import { api } from "../axiosInstance";
|
||||
|
||||
export type CheckUserExistsModel = {
|
||||
phone: string,
|
||||
}
|
||||
|
||||
export const checkUserExists = async ({ phone }: CheckUserExistsModel) => {
|
||||
const res = await api.get(`/exists/${encodeURIComponent(phone)}`)
|
||||
return res.data.exists as boolean
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import { api } from "../axiosInstance";
|
||||
|
||||
export type OtpValidationRequestModel = {
|
||||
phone: string,
|
||||
otp: string,
|
||||
}
|
||||
|
||||
export const validateOtp = async (model: OtpValidationRequestModel) => {
|
||||
const res = await api.post(`/otp/check`, model)
|
||||
return res.data
|
||||
}
|
||||
@@ -1,6 +1,106 @@
|
||||
import axios from 'axios'
|
||||
import { useAuthStore } from '@/zustand/authStore';
|
||||
import axios from 'axios';
|
||||
import { refreshToken } from './auth/refresh-token';
|
||||
|
||||
export const api = axios.create({
|
||||
// Create axios instance
|
||||
const api = axios.create({
|
||||
baseURL: process.env.NEXT_PUBLIC_API_URL || 'https://api.example.com',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
})
|
||||
});
|
||||
|
||||
let isRefreshing = false;
|
||||
let failedQueue: {
|
||||
resolve: (value?: unknown) => void;
|
||||
reject: (error: unknown) => void;
|
||||
}[] = [];
|
||||
|
||||
const processQueue = (error: unknown, token: string | null = null) => {
|
||||
failedQueue.forEach(({ resolve, reject }) => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
resolve(token);
|
||||
}
|
||||
});
|
||||
failedQueue = [];
|
||||
};
|
||||
|
||||
// Attach access token to requests
|
||||
api.interceptors.request.use((config) => {
|
||||
const state = useAuthStore.getState();
|
||||
console.log('AuthStore state:', state);
|
||||
|
||||
const token = state.user?.accessToken;
|
||||
|
||||
if (token) {
|
||||
config.headers = config.headers || {};
|
||||
config.headers.Authorization = `Bearer ${token}`;
|
||||
}
|
||||
return config;
|
||||
});
|
||||
|
||||
// Response interceptor to handle 401 and refresh token logic
|
||||
api.interceptors.response.use(
|
||||
(response) => response,
|
||||
async (error) => {
|
||||
const originalRequest = error.config;
|
||||
|
||||
if (
|
||||
error.response?.status === 401 &&
|
||||
!originalRequest._retry // custom flag to prevent infinite loops
|
||||
) {
|
||||
if (isRefreshing) {
|
||||
// Queue the requests while refreshing
|
||||
return new Promise((resolve, reject) => {
|
||||
failedQueue.push({ resolve, reject });
|
||||
})
|
||||
.then((token) => {
|
||||
originalRequest.headers.Authorization = `Bearer ${token}`;
|
||||
return api(originalRequest);
|
||||
})
|
||||
.catch((err) => {
|
||||
return Promise.reject(err);
|
||||
});
|
||||
}
|
||||
|
||||
originalRequest._retry = true;
|
||||
isRefreshing = true;
|
||||
|
||||
const token = useAuthStore.getState().user?.refreshToken;
|
||||
console.log(useAuthStore.getState())
|
||||
|
||||
if (!token) {
|
||||
|
||||
useAuthStore.getState().logout();
|
||||
return Promise.reject(error);
|
||||
}
|
||||
|
||||
try {
|
||||
const data = await refreshToken({ token });
|
||||
// Update tokens in Zustand store
|
||||
useAuthStore.getState().login({
|
||||
...useAuthStore.getState().user!,
|
||||
accessToken: data.accessToken,
|
||||
refreshToken: data.refreshToken || token,
|
||||
});
|
||||
|
||||
api.defaults.headers.common.Authorization = `Bearer ${data.accessToken}`;
|
||||
originalRequest.headers.Authorization = `Bearer ${data.accessToken}`;
|
||||
|
||||
processQueue(null, data.accessToken);
|
||||
|
||||
return api(originalRequest);
|
||||
} catch (err) {
|
||||
processQueue(err, null);
|
||||
useAuthStore.getState().logout();
|
||||
return Promise.reject(err);
|
||||
} finally {
|
||||
isRefreshing = false;
|
||||
}
|
||||
}
|
||||
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
|
||||
export { api };
|
||||
|
||||
Reference in New Issue
Block a user