refactor: Авторизация через Redux Toolkit
Удален AuthContext, логика авторизации перенесена в Redux. Добавлены authSlice и authApi для управления состоянием авторизации.
This commit is contained in:
parent
0c326d815a
commit
4d903ee8c5
@ -1,21 +1,41 @@
|
|||||||
import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react";
|
import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react";
|
||||||
|
import { logout } from "../Redux/Slices/authSlice.js";
|
||||||
import CONFIG from "../Core/сonfig.js";
|
import CONFIG from "../Core/сonfig.js";
|
||||||
|
|
||||||
|
const baseQuery = fetchBaseQuery({
|
||||||
|
baseUrl: CONFIG.BASE_URL,
|
||||||
|
prepareHeaders: (headers) => {
|
||||||
|
const token = localStorage.getItem("access_token");
|
||||||
|
if (token) {
|
||||||
|
headers.set("Authorization", `Bearer ${token}`);
|
||||||
|
}
|
||||||
|
headers.set("Content-Type", "application/json");
|
||||||
|
return headers;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const baseQueryWithAuth = async (args, api, extraOptions) => {
|
||||||
|
const result = await baseQuery(args, api, extraOptions);
|
||||||
|
if (result.error && result.error.status === 401) {
|
||||||
|
localStorage.removeItem("access_token");
|
||||||
|
api.dispatch(logout());
|
||||||
|
window.location.href = "/login";
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
export const authApi = createApi({
|
export const authApi = createApi({
|
||||||
reducerPath: 'authApi',
|
reducerPath: "authApi",
|
||||||
baseQuery: fetchBaseQuery({
|
baseQuery: baseQueryWithAuth,
|
||||||
baseUrl: CONFIG.BASE_URL,
|
|
||||||
}),
|
|
||||||
endpoints: (builder) => ({
|
endpoints: (builder) => ({
|
||||||
login: builder.mutation({
|
login: builder.mutation({
|
||||||
query: (credentials) => ({
|
query: (credentials) => ({
|
||||||
url: '/auth/login/',
|
url: "/login/",
|
||||||
method: 'POST',
|
method: "POST",
|
||||||
body: credentials
|
body: credentials,
|
||||||
})
|
}),
|
||||||
|
}),
|
||||||
}),
|
}),
|
||||||
})
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export const { useLoginMutation } = authApi;
|
export const { useLoginMutation } = authApi;
|
||||||
@ -1,6 +1,5 @@
|
|||||||
import {BrowserRouter as Router} from "react-router-dom";
|
import {BrowserRouter as Router} from "react-router-dom";
|
||||||
import AppRouter from "./AppRouter.jsx";
|
import AppRouter from "./AppRouter.jsx";
|
||||||
import {AuthProvider} from "../Hooks/AuthContext.jsx";
|
|
||||||
import "/src/Styles/app.css";
|
import "/src/Styles/app.css";
|
||||||
import {Provider} from "react-redux";
|
import {Provider} from "react-redux";
|
||||||
import store from "../Redux/store.js";
|
import store from "../Redux/store.js";
|
||||||
@ -13,11 +12,9 @@ dayjs.locale('ru');
|
|||||||
const App = () => (
|
const App = () => (
|
||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
<Router>
|
<Router>
|
||||||
<AuthProvider>
|
|
||||||
<ConfigProvider locale={locale}>
|
<ConfigProvider locale={locale}>
|
||||||
<AppRouter/>
|
<AppRouter/>
|
||||||
</ConfigProvider>
|
</ConfigProvider>
|
||||||
</AuthProvider>
|
|
||||||
</Router>
|
</Router>
|
||||||
</Provider>
|
</Provider>
|
||||||
);
|
);
|
||||||
|
|||||||
22
web-app/src/App/ErrorBoundary.jsx
Normal file
22
web-app/src/App/ErrorBoundary.jsx
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import { Component } from "react";
|
||||||
|
|
||||||
|
class ErrorBoundary extends Component {
|
||||||
|
state = { hasError: false };
|
||||||
|
|
||||||
|
static getDerivedStateFromError() {
|
||||||
|
return { hasError: true };
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidCatch(error, info) {
|
||||||
|
console.error("ErrorBoundary caught:", error, info);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
if (this.state.hasError) {
|
||||||
|
return <div>Произошла ошибка</div>;
|
||||||
|
}
|
||||||
|
return this.props.children;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ErrorBoundary;
|
||||||
@ -1,8 +1,8 @@
|
|||||||
import { Navigate, Outlet } from "react-router-dom";
|
import { Navigate, Outlet } from "react-router-dom";
|
||||||
import {useAuth} from "../Hooks/AuthContext.jsx";
|
import { useSelector } from "react-redux";
|
||||||
|
|
||||||
const PrivateRoute = () => {
|
const PrivateRoute = () => {
|
||||||
const {user} = useAuth();
|
const { user } = useSelector((state) => state.auth);
|
||||||
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return <Navigate to="/login" />;
|
return <Navigate to="/login" />;
|
||||||
@ -11,5 +11,4 @@ const PrivateRoute = () => {
|
|||||||
return <Outlet />;
|
return <Outlet />;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export default PrivateRoute;
|
export default PrivateRoute;
|
||||||
@ -11,7 +11,7 @@ import {
|
|||||||
LogoutOutlined,
|
LogoutOutlined,
|
||||||
MessageOutlined
|
MessageOutlined
|
||||||
} from "@ant-design/icons";
|
} from "@ant-design/icons";
|
||||||
import {useAuth} from "../../Hooks/AuthContext.jsx";
|
import useAuthUtils from "../../Hooks/useAuthUtils.js";
|
||||||
|
|
||||||
const {Content, Footer, Sider} = Layout;
|
const {Content, Footer, Sider} = Layout;
|
||||||
|
|
||||||
@ -24,7 +24,7 @@ const MainLayout = () => {
|
|||||||
const [collapsed, setCollapsed] = useState(true);
|
const [collapsed, setCollapsed] = useState(true);
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const {logout} = useAuth();
|
const {logoutAndRedirect} = useAuthUtils();
|
||||||
|
|
||||||
const menuItems = [
|
const menuItems = [
|
||||||
getItem("Главная", "/", <HomeOutlined/>),
|
getItem("Главная", "/", <HomeOutlined/>),
|
||||||
@ -42,7 +42,7 @@ const MainLayout = () => {
|
|||||||
|
|
||||||
const handleMenuClick = ({key}) => {
|
const handleMenuClick = ({key}) => {
|
||||||
if (key === "logout") {
|
if (key === "logout") {
|
||||||
logout();
|
logoutAndRedirect();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
navigate(key);
|
navigate(key);
|
||||||
|
|||||||
@ -1,73 +1,48 @@
|
|||||||
import {Form, Input, Button, Row, Col, Typography} from 'antd';
|
import {Form, Input, Button, Row, Col, Typography} from "antd";
|
||||||
import {useEffect, useState} from 'react';
|
import {useSelector} from "react-redux";
|
||||||
import {useAuth} from "../../../Hooks/AuthContext.jsx";
|
import useLoginPage from "./useLoginPage.js";
|
||||||
import {useNavigate} from "react-router-dom";
|
import useLoginPageUI from "./useLoginPageUI.js";
|
||||||
|
|
||||||
const {Title} = Typography;
|
const {Title} = Typography;
|
||||||
|
|
||||||
const LoginPage = () => {
|
const LoginPage = () => {
|
||||||
const {user, login} = useAuth();
|
const {error} = useSelector((state) => state.auth);
|
||||||
const [loading, setLoading] = useState(false);
|
const {onFinish, isLoading} = useLoginPage();
|
||||||
const [error, setError] = useState(null);
|
const {containerStyle, formContainerStyle, titleStyle, errorStyle, labels} = useLoginPageUI();
|
||||||
|
|
||||||
const navigate = useNavigate();
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (user) {
|
|
||||||
navigate("/");
|
|
||||||
}
|
|
||||||
document.title = "Авторизация";
|
|
||||||
}, [user, navigate]);
|
|
||||||
|
|
||||||
const onFinish = async (values) => {
|
|
||||||
setLoading(true);
|
|
||||||
setError(null);
|
|
||||||
|
|
||||||
try {
|
|
||||||
await login(values);
|
|
||||||
navigate("/");
|
|
||||||
} catch (error) {
|
|
||||||
setError(`Ошибка при входе: ${error.message}`);
|
|
||||||
} finally {
|
|
||||||
setLoading(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Row justify="center" align="middle" style={{minHeight: '100vh'}}>
|
<Row justify="center" align="middle" style={containerStyle}>
|
||||||
<Col xs={24} sm={18} md={12} lg={8} xl={6}>
|
<Col xs={24} sm={18} md={12} lg={8} xl={6}>
|
||||||
<div style={{padding: 20, border: '1px solid #ddd', borderRadius: 8}}>
|
<div style={formContainerStyle}>
|
||||||
<Title level={2} style={{textAlign: 'center'}}>Авторизация</Title>
|
<Title level={2} style={titleStyle}>
|
||||||
|
{labels.title}
|
||||||
|
</Title>
|
||||||
|
|
||||||
{error && <div style={{color: 'red', marginBottom: 15}}>{error}</div>}
|
{error && (
|
||||||
|
<div style={errorStyle}>
|
||||||
|
{labels.errorPrefix}
|
||||||
|
{error}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
<Form
|
<Form name="login" initialValues={{remember: true}} onFinish={onFinish}>
|
||||||
name="login"
|
|
||||||
initialValues={{remember: true}}
|
|
||||||
onFinish={onFinish}
|
|
||||||
>
|
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name="login"
|
name="login"
|
||||||
rules={[{required: true, message: 'Пожалуйста, введите логин'}]}
|
rules={[{required: true, message: labels.loginRequired}]}
|
||||||
>
|
>
|
||||||
<Input placeholder="Логин"/>
|
<Input placeholder={labels.loginPlaceholder}/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name="password"
|
name="password"
|
||||||
rules={[{required: true, message: 'Пожалуйста, введите пароль'}]}
|
rules={[{required: true, message: labels.passwordRequired}]}
|
||||||
>
|
>
|
||||||
<Input.Password placeholder="Пароль"/>
|
<Input.Password placeholder={labels.passwordPlaceholder}/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
<Form.Item>
|
<Form.Item>
|
||||||
<Button
|
<Button type="primary" htmlType="submit" block loading={isLoading}>
|
||||||
type="primary"
|
{labels.submitButton}
|
||||||
htmlType="submit"
|
|
||||||
block
|
|
||||||
loading={loading}
|
|
||||||
>
|
|
||||||
Войти
|
|
||||||
</Button>
|
</Button>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Form>
|
</Form>
|
||||||
|
|||||||
36
web-app/src/Components/Pages/LoginPage/useLoginPage.js
Normal file
36
web-app/src/Components/Pages/LoginPage/useLoginPage.js
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import { useDispatch } from "react-redux";
|
||||||
|
import { notification } from "antd";
|
||||||
|
import {setError, setUser} from "../../../Redux/Slices/authSlice.js";
|
||||||
|
import {useLoginMutation} from "../../../Api/authApi.js";
|
||||||
|
|
||||||
|
const useLoginPage = () => {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const [loginUser, { isLoading }] = useLoginMutation();
|
||||||
|
|
||||||
|
const onFinish = async (loginData) => {
|
||||||
|
try {
|
||||||
|
const response = await loginUser(loginData).unwrap();
|
||||||
|
const token = response.access_token || response.token;
|
||||||
|
if (!token) {
|
||||||
|
throw new Error("Токен не получен от сервера");
|
||||||
|
}
|
||||||
|
localStorage.setItem("access_token", token);
|
||||||
|
dispatch(setUser({ token }));
|
||||||
|
} catch (error) {
|
||||||
|
const errorMessage = error?.data?.message || "Не удалось войти";
|
||||||
|
dispatch(setError(errorMessage));
|
||||||
|
notification.error({
|
||||||
|
message: "Ошибка при входе",
|
||||||
|
description: errorMessage,
|
||||||
|
placement: "topRight",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
onFinish,
|
||||||
|
isLoading,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useLoginPage;
|
||||||
58
web-app/src/Components/Pages/LoginPage/useLoginPageUI.js
Normal file
58
web-app/src/Components/Pages/LoginPage/useLoginPageUI.js
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
import { useEffect } from "react";
|
||||||
|
import { useNavigate } from "react-router-dom";
|
||||||
|
import { useSelector } from "react-redux";
|
||||||
|
import { Grid } from "antd";
|
||||||
|
|
||||||
|
const { useBreakpoint } = Grid;
|
||||||
|
|
||||||
|
const useLoginPageUI = () => {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const { user } = useSelector((state) => state.auth);
|
||||||
|
const screens = useBreakpoint();
|
||||||
|
|
||||||
|
const containerStyle = {
|
||||||
|
minHeight: "100vh",
|
||||||
|
};
|
||||||
|
|
||||||
|
const formContainerStyle = {
|
||||||
|
padding: screens.xs ? 10 : 20,
|
||||||
|
border: "1px solid #ddd",
|
||||||
|
borderRadius: 8,
|
||||||
|
};
|
||||||
|
|
||||||
|
const titleStyle = {
|
||||||
|
textAlign: "center",
|
||||||
|
};
|
||||||
|
|
||||||
|
const errorStyle = {
|
||||||
|
color: "red",
|
||||||
|
marginBottom: 15,
|
||||||
|
};
|
||||||
|
|
||||||
|
const labels = {
|
||||||
|
title: "Авторизация",
|
||||||
|
loginPlaceholder: "Логин",
|
||||||
|
passwordPlaceholder: "Пароль",
|
||||||
|
submitButton: "Войти",
|
||||||
|
loginRequired: "Пожалуйста, введите логин",
|
||||||
|
passwordRequired: "Пожалуйста, введите пароль",
|
||||||
|
errorPrefix: "Ошибка при входе: ",
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (user) {
|
||||||
|
navigate("/");
|
||||||
|
}
|
||||||
|
document.title = labels.title;
|
||||||
|
}, [user, navigate]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
containerStyle,
|
||||||
|
formContainerStyle,
|
||||||
|
titleStyle,
|
||||||
|
errorStyle,
|
||||||
|
labels,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useLoginPageUI;
|
||||||
@ -1,67 +0,0 @@
|
|||||||
import {createContext, useState, useContext, useEffect} from "react";
|
|
||||||
import PropTypes from "prop-types";
|
|
||||||
import loginUser from "../old_api/auth/loginRequest.js";
|
|
||||||
import {Spin} from "antd";
|
|
||||||
import {useNavigate} from "react-router-dom";
|
|
||||||
import createApi from "../Core/axiosConfig.js";
|
|
||||||
|
|
||||||
const AuthContext = createContext(undefined);
|
|
||||||
|
|
||||||
const AuthProvider = ({children}) => {
|
|
||||||
const [user, setUser] = useState(null);
|
|
||||||
const [isLoading, setIsLoading] = useState(true);
|
|
||||||
const navigate = useNavigate();
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const token = localStorage.getItem("access_token");
|
|
||||||
if (token) {
|
|
||||||
setUser({token});
|
|
||||||
}
|
|
||||||
setIsLoading(false);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
|
|
||||||
const login = async (loginData) => {
|
|
||||||
try {
|
|
||||||
const token = await loginUser(loginData, api);
|
|
||||||
localStorage.setItem("access_token", token);
|
|
||||||
setUser({token});
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Login failed", error);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const logoutAndRedirect = () => {
|
|
||||||
localStorage.removeItem("access_token");
|
|
||||||
setUser(null);
|
|
||||||
navigate("/login");
|
|
||||||
};
|
|
||||||
|
|
||||||
const logout = () => {
|
|
||||||
localStorage.removeItem("access_token");
|
|
||||||
setUser(null);
|
|
||||||
};
|
|
||||||
|
|
||||||
if (isLoading) {
|
|
||||||
return <Spin/>;
|
|
||||||
}
|
|
||||||
|
|
||||||
const api = createApi(logoutAndRedirect);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<AuthContext.Provider value={{user, login, logout, logoutAndRedirect, api}}>
|
|
||||||
{children}
|
|
||||||
</AuthContext.Provider>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
AuthProvider.propTypes = {
|
|
||||||
children: PropTypes.node.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
const useAuth = () => {
|
|
||||||
return useContext(AuthContext);
|
|
||||||
};
|
|
||||||
|
|
||||||
export {useAuth, AuthProvider};
|
|
||||||
18
web-app/src/Hooks/useAuthUtils.js
Normal file
18
web-app/src/Hooks/useAuthUtils.js
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import { useDispatch } from "react-redux";
|
||||||
|
import { useNavigate } from "react-router-dom";
|
||||||
|
import { logout } from "../Redux/Slices/authSlice.js";
|
||||||
|
|
||||||
|
const useAuthUtils = () => {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
const logoutAndRedirect = () => {
|
||||||
|
localStorage.removeItem("access_token");
|
||||||
|
dispatch(logout());
|
||||||
|
navigate("/login");
|
||||||
|
};
|
||||||
|
|
||||||
|
return { logoutAndRedirect };
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useAuthUtils;
|
||||||
49
web-app/src/Redux/Slices/authSlice.js
Normal file
49
web-app/src/Redux/Slices/authSlice.js
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";
|
||||||
|
|
||||||
|
export const checkAuth = createAsyncThunk("auth/checkAuth", async () => {
|
||||||
|
const token = localStorage.getItem("access_token");
|
||||||
|
if (token) {
|
||||||
|
return { token };
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
const initialState = {
|
||||||
|
user: null,
|
||||||
|
isLoading: true,
|
||||||
|
error: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
const authSlice = createSlice({
|
||||||
|
name: "auth",
|
||||||
|
initialState,
|
||||||
|
reducers: {
|
||||||
|
setUser(state, action) {
|
||||||
|
state.user = action.payload;
|
||||||
|
state.isLoading = false;
|
||||||
|
state.error = null;
|
||||||
|
},
|
||||||
|
setError(state, action) {
|
||||||
|
state.error = action.payload;
|
||||||
|
state.isLoading = false;
|
||||||
|
},
|
||||||
|
logout(state) {
|
||||||
|
state.user = null;
|
||||||
|
state.error = null;
|
||||||
|
state.isLoading = false;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
extraReducers: (builder) => {
|
||||||
|
builder
|
||||||
|
.addCase(checkAuth.fulfilled, (state, action) => {
|
||||||
|
state.user = action.payload;
|
||||||
|
state.isLoading = false;
|
||||||
|
})
|
||||||
|
.addCase(checkAuth.rejected, (state) => {
|
||||||
|
state.isLoading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const { setUser, setError, logout } = authSlice.actions;
|
||||||
|
export default authSlice.reducer;
|
||||||
@ -15,6 +15,8 @@ import {scheduledAppointmentsApi} from "../Api/scheduledAppointmentsApi.js";
|
|||||||
import {appointmentTypesApi} from "../Api/appointmentTypesApi.js";
|
import {appointmentTypesApi} from "../Api/appointmentTypesApi.js";
|
||||||
import {usersApi} from "../Api/usersApi.js";
|
import {usersApi} from "../Api/usersApi.js";
|
||||||
import usersReducer from "./Slices/usersSlice.js";
|
import usersReducer from "./Slices/usersSlice.js";
|
||||||
|
import {authApi} from "../Api/authApi.js";
|
||||||
|
import authReducer from "./Slices/authSlice.js";
|
||||||
|
|
||||||
export const store = configureStore({
|
export const store = configureStore({
|
||||||
reducer: {
|
reducer: {
|
||||||
@ -42,7 +44,10 @@ export const store = configureStore({
|
|||||||
[appointmentTypesApi.reducerPath]: appointmentTypesApi.reducer,
|
[appointmentTypesApi.reducerPath]: appointmentTypesApi.reducer,
|
||||||
|
|
||||||
[usersApi.reducerPath]: usersApi.reducer,
|
[usersApi.reducerPath]: usersApi.reducer,
|
||||||
usersUI: usersReducer
|
usersUI: usersReducer,
|
||||||
|
|
||||||
|
auth: authReducer,
|
||||||
|
[authApi.reducerPath]: authApi.reducer,
|
||||||
},
|
},
|
||||||
middleware: (getDefaultMiddleware) => (
|
middleware: (getDefaultMiddleware) => (
|
||||||
getDefaultMiddleware().concat(
|
getDefaultMiddleware().concat(
|
||||||
@ -56,6 +61,7 @@ export const store = configureStore({
|
|||||||
scheduledAppointmentsApi.middleware,
|
scheduledAppointmentsApi.middleware,
|
||||||
appointmentTypesApi.middleware,
|
appointmentTypesApi.middleware,
|
||||||
usersApi.middleware,
|
usersApi.middleware,
|
||||||
|
authApi.middleware,
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,14 +0,0 @@
|
|||||||
import CONFIG from "../../Core/сonfig.js";
|
|
||||||
|
|
||||||
const loginUser = async (loginData, api) => {
|
|
||||||
const response = await api.post(`${CONFIG.BASE_URL}/login/`, loginData, {
|
|
||||||
withCredentials: true,
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return response.data.access_token;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default loginUser;
|
|
||||||
Loading…
x
Reference in New Issue
Block a user