init
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
import axios from 'axios';
|
||||
|
||||
const API_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:4000';
|
||||
|
||||
const api = axios.create({
|
||||
baseURL: `${API_URL}/api/v1`,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
|
||||
// Request interceptor to add auth token
|
||||
api.interceptors.request.use((config) => {
|
||||
if (typeof window !== 'undefined') {
|
||||
const token = localStorage.getItem('accessToken');
|
||||
if (token) {
|
||||
config.headers.Authorization = `Bearer ${token}`;
|
||||
}
|
||||
}
|
||||
return config;
|
||||
});
|
||||
|
||||
// Response interceptor for token refresh
|
||||
api.interceptors.response.use(
|
||||
(response) => response,
|
||||
async (error) => {
|
||||
const originalRequest = error.config;
|
||||
|
||||
if (error.response?.status === 401 && !originalRequest._retry) {
|
||||
originalRequest._retry = true;
|
||||
|
||||
try {
|
||||
const refreshToken = localStorage.getItem('refreshToken');
|
||||
if (!refreshToken) throw new Error('No refresh token');
|
||||
|
||||
const { data } = await axios.post(`${API_URL}/api/v1/auth/refresh`, {
|
||||
refreshToken,
|
||||
});
|
||||
|
||||
localStorage.setItem('accessToken', data.accessToken);
|
||||
localStorage.setItem('refreshToken', data.refreshToken);
|
||||
|
||||
originalRequest.headers.Authorization = `Bearer ${data.accessToken}`;
|
||||
return api(originalRequest);
|
||||
} catch {
|
||||
localStorage.removeItem('accessToken');
|
||||
localStorage.removeItem('refreshToken');
|
||||
if (typeof window !== 'undefined') {
|
||||
window.location.href = '/login';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Promise.reject(error);
|
||||
},
|
||||
);
|
||||
|
||||
export default api;
|
||||
@@ -0,0 +1,55 @@
|
||||
'use client';
|
||||
|
||||
import { create } from 'zustand';
|
||||
import api from '@/lib/api';
|
||||
import type { User, AuthResponse } from '@/types';
|
||||
|
||||
interface AuthState {
|
||||
user: User | null;
|
||||
isLoading: boolean;
|
||||
isAuthenticated: boolean;
|
||||
login: (email: string, password: string) => Promise<void>;
|
||||
register: (data: { email: string; password: string; firstName: string; lastName: string }) => Promise<void>;
|
||||
logout: () => void;
|
||||
loadUser: () => Promise<void>;
|
||||
}
|
||||
|
||||
export const useAuthStore = create<AuthState>((set) => ({
|
||||
user: null,
|
||||
isLoading: true,
|
||||
isAuthenticated: false,
|
||||
|
||||
login: async (email, password) => {
|
||||
const { data } = await api.post<AuthResponse>('/auth/login', { email, password });
|
||||
localStorage.setItem('accessToken', data.accessToken);
|
||||
localStorage.setItem('refreshToken', data.refreshToken);
|
||||
set({ user: data.user, isAuthenticated: true });
|
||||
},
|
||||
|
||||
register: async (registerData) => {
|
||||
const { data } = await api.post<AuthResponse>('/auth/register', registerData);
|
||||
localStorage.setItem('accessToken', data.accessToken);
|
||||
localStorage.setItem('refreshToken', data.refreshToken);
|
||||
set({ user: data.user, isAuthenticated: true });
|
||||
},
|
||||
|
||||
logout: () => {
|
||||
localStorage.removeItem('accessToken');
|
||||
localStorage.removeItem('refreshToken');
|
||||
set({ user: null, isAuthenticated: false });
|
||||
},
|
||||
|
||||
loadUser: async () => {
|
||||
try {
|
||||
const token = localStorage.getItem('accessToken');
|
||||
if (!token) {
|
||||
set({ isLoading: false });
|
||||
return;
|
||||
}
|
||||
const { data } = await api.get<User>('/users/me');
|
||||
set({ user: data, isAuthenticated: true, isLoading: false });
|
||||
} catch {
|
||||
set({ user: null, isAuthenticated: false, isLoading: false });
|
||||
}
|
||||
},
|
||||
}));
|
||||
Reference in New Issue
Block a user