feat(client): add auth logic

This commit is contained in:
Vladislav Syrochkin 2025-06-16 12:34:32 +05:00
parent 79cb434ebd
commit c87581c9e2
6 changed files with 70 additions and 34 deletions

View File

@ -1,10 +1,21 @@
import React from 'react';
/* eslint-disable react-hooks/exhaustive-deps */
import React, { useEffect } from 'react';
import { Route, Routes } from 'react-router-dom';
import MainLayout from './pages/MainLayout';
import ProtectedRoute from './pages/ProtectedRoute';
import LoginPage from './pages/LoginPage';
import { useSetUserSelector } from './store/userStore';
function App() {
const setUser = useSetUserSelector();
useEffect(() => {
const storedUser = localStorage.getItem('user');
if (storedUser) {
setUser(JSON.parse(storedUser));
}
}, []);
return (
<div className="App">
<Routes>

View File

@ -1,8 +1,9 @@
import axios from 'axios';
import { Access, Auth } from '../types/auth';
import { User } from '../types/user';
import { AuthService } from '../services/auth';
import { AuthService } from '../services/authService';
import axiosRetry from 'axios-retry';
import { useAuthStore } from '../store/authStore';
const baseURL = `${process.env.REACT_APP_HTTP_PROTOCOL}://${process.env.REACT_APP_API_URL}/api/v1`;
@ -15,7 +16,10 @@ const base = axios.create({
});
base.interceptors.request.use((config) => {
const token = localStorage.getItem('accessToken');
if (config.url === '/auth/refresh') {
return config;
}
const token = useAuthStore.getState().accessToken;
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
@ -43,7 +47,6 @@ base.interceptors.response.use(
async function (error) {
console.log('error', error);
const originalRequest = error.response.config;
console.log('originalRequest._retry', originalRequest);
const urlTokens = error?.request?.responseURL.split('/');
const url = urlTokens[urlTokens.length - 1];
console.log('url', url);
@ -55,26 +58,43 @@ base.interceptors.response.use(
url !== 'logout'
) {
originalRequest._retry = true;
const res = await AuthService.refresh().catch(async () => {
try {
await AuthService.refresh();
return base(originalRequest);
} catch (error) {
await AuthService.logout();
});
console.log('res', res);
return await base(originalRequest);
return new Promise(() => {});
}
}
return await Promise.reject(error);
}
);
interface newAccess {
accessToken: string;
refreshToken: string;
}
const api = {
// auth
async login(auth: Auth): Promise<Access> {
console.log(auth);
const response = await base.post<Access>('/auth', auth);
// async login(auth: Auth): Promise<Access> {
async login(auth: Auth): Promise<newAccess> {
// const response = (await base.post) <Access> ('/auth', auth);
const response = await base.post<newAccess>('/auth', auth);
return response.data;
},
async refreshToken(): Promise<Access> {
const response = await base.post<Access>('/auth/refresh');
const token = localStorage.getItem('refreshToken');
const response = await base.post<Access>(
'/auth/refresh',
{},
{
headers: {
Authorization: `Bearer ${token}`,
},
}
);
return response.data;
},
@ -83,6 +103,13 @@ const api = {
const response = await base.get<User>('/profile');
return response.data;
},
async getUsers(page: number, limit: number): Promise<any> {
const response = await base.get<User[]>(
`/account?page=${page}&limit=${limit}`
);
return response.data;
},
};
export default api;

View File

@ -3,6 +3,10 @@ import { useState } from 'react';
import ContentDrawer from '../components/ContentDrawer';
import UserCreate from '../components/UserCreate';
import { useTranslation } from 'react-i18next';
import { Button } from 'antd';
import { UserService } from '../services/userService';
import { User } from '../types/user';
import { AuthService } from '../services/authService';
export default function AccountsPage() {
const { t } = useTranslation();
@ -11,6 +15,8 @@ export default function AccountsPage() {
const showDrawer = () => setOpen(true);
const closeDrawer = () => setOpen(false);
const [accounts, setAccounts] = useState<User[]>([]);
return (
<>
<Header

View File

@ -5,7 +5,7 @@ import {
EyeTwoTone,
UserOutlined,
} from '@ant-design/icons';
import { AuthService } from '../services/auth';
import { AuthService } from '../services/authService';
import { Auth } from '../types/auth';
import { useNavigate } from 'react-router-dom';

View File

@ -9,8 +9,6 @@ import RunningProcessesPage from './RunningProcessesPage';
import AccountsPage from './AccountsPage';
import EventsListPage from './EventsListPage';
import ConfigurationPage from './ConfigurationPage';
import { useSetUserSelector } from '../store/user';
import { UserService } from '../services/user';
export default function MainLayout() {
const navigate = useNavigate();
@ -21,8 +19,6 @@ export default function MainLayout() {
const [width, setWidth] = useState<number | string>('15%');
const [collapsedWidth, setCollapsedWidth] = useState(50);
const setUser = useSetUserSelector();
const calculateWidths = () => {
const windowWidth = window.innerWidth;
const expanded = Math.min(Math.max(windowWidth * 0.15, 180), 240);
@ -58,21 +54,6 @@ export default function MainLayout() {
navigate(key);
}
useEffect(() => {
const token = localStorage.getItem('accessToken');
if (!token) {
navigate('/login');
} else {
if (localStorage.getItem('user')) {
setUser(JSON.parse(localStorage.getItem('user') as string));
} else {
UserService.getProfile().then((user) => {
setUser(user);
});
}
}
}, []);
return (
<Layout style={{ minHeight: '100vh' }}>
<Sider

View File

@ -1,8 +1,19 @@
/* eslint-disable react-hooks/exhaustive-deps */
// ProtectedRoute.js
import { Outlet } from 'react-router-dom';
import React from 'react';
import { Outlet, useNavigate } from 'react-router-dom';
import React, { useEffect } from 'react';
import { useUserSelector } from '../store/userStore';
const ProtectedRoute = (): React.JSX.Element => {
const navigate = useNavigate();
const user = useUserSelector();
useEffect(() => {
if (!user?.id) {
navigate('/login');
}
}, [user]);
return <Outlet />;
};
export default ProtectedRoute;