diff --git a/api/app/domain/entities/user.py b/api/app/domain/entities/user.py index 8ab8c37..ba2347b 100644 --- a/api/app/domain/entities/user.py +++ b/api/app/domain/entities/user.py @@ -8,7 +8,7 @@ class UserEntity(BaseModel): first_name: str = Field(..., example='Ivan') last_name: str = Field(..., example='Ivanov') patronymic: Optional[str] = Field(None, example='Ivanov') - login: str = Field(..., example='user@example.com') + login: str = Field(..., example='ivanov74') role_id: int = Field(..., example=1) diff --git a/web-app/package-lock.json b/web-app/package-lock.json index 4b92f5e..0680458 100644 --- a/web-app/package-lock.json +++ b/web-app/package-lock.json @@ -12,6 +12,7 @@ "@react-buddy/palette-antd": "^5.3.0", "antd": "^5.23.1", "axios": "^1.7.9", + "prop-types": "^15.8.1", "react": "^18.3.1", "react-dom": "^18.3.1", "react-router-dom": "^7.1.1" @@ -3617,7 +3618,6 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -3888,7 +3888,6 @@ "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "dev": true, "license": "MIT", "dependencies": { "loose-envify": "^1.4.0", @@ -4552,7 +4551,6 @@ "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "dev": true, "license": "MIT" }, "node_modules/react-router": { diff --git a/web-app/package.json b/web-app/package.json index e811463..c1149c1 100644 --- a/web-app/package.json +++ b/web-app/package.json @@ -10,13 +10,14 @@ "preview": "vite preview" }, "dependencies": { + "@react-buddy/ide-toolbox": "^2.4.0", + "@react-buddy/palette-antd": "^5.3.0", "antd": "^5.23.1", "axios": "^1.7.9", + "prop-types": "^15.8.1", "react": "^18.3.1", "react-dom": "^18.3.1", - "react-router-dom": "^7.1.1", - "@react-buddy/ide-toolbox": "^2.4.0", - "@react-buddy/palette-antd": "^5.3.0" + "react-router-dom": "^7.1.1" }, "devDependencies": { "@eslint/js": "^9.17.0", diff --git a/web-app/src/App.jsx b/web-app/src/App.jsx index f240c23..506895e 100644 --- a/web-app/src/App.jsx +++ b/web-app/src/App.jsx @@ -1,10 +1,13 @@ -function App() { +import {BrowserRouter as Router} from "react-router-dom"; +import AppRouter from "./AppRouter.jsx"; +import {AuthProvider} from "./AuthContext.jsx"; - return ( - <> -

1234

- - ) -} +const App = () => ( + + + + + +); export default App diff --git a/web-app/src/AppRouter.jsx b/web-app/src/AppRouter.jsx new file mode 100644 index 0000000..7b6487d --- /dev/null +++ b/web-app/src/AppRouter.jsx @@ -0,0 +1,15 @@ +import {Routes, Route} from "react-router-dom"; +import PrivateRoute from "./components/PrivateRoute.jsx"; + + +const AppRouter = () => ( + + + + }> + + + +) + +export default AppRouter; \ No newline at end of file diff --git a/web-app/src/AuthContext.jsx b/web-app/src/AuthContext.jsx new file mode 100644 index 0000000..c7ea3c7 --- /dev/null +++ b/web-app/src/AuthContext.jsx @@ -0,0 +1,45 @@ +import {createContext, useState, useContext, useEffect} from "react"; +import PropTypes from "prop-types"; +import {loginUser} from "./api/LoginRequest.jsx"; + +const AuthContext = createContext(undefined); + +export const AuthProvider = ({children}) => { + const [user, setUser] = useState(null); + + useEffect(() => { + const token = localStorage.getItem("access_token"); + if (token) { + setUser({token}); + } + }, []); + + const login = async (loginData) => { + try { + const token = await loginUser(loginData); + localStorage.setItem("access_token", token); + setUser({ token }); + } catch (error) { + console.error("Login failed", error); + } + }; + + const logout = () => { + localStorage.removeItem("access_token"); + setUser(null); + }; + + return ( + + {children} + + ); +}; + +AuthProvider.propTypes = { + children: PropTypes.node.isRequired, +}; + +export const useAuth = () => { + return useContext(AuthContext); +}; diff --git a/web-app/src/api/LoginRequest.jsx b/web-app/src/api/LoginRequest.jsx new file mode 100644 index 0000000..15a6bd5 --- /dev/null +++ b/web-app/src/api/LoginRequest.jsx @@ -0,0 +1,13 @@ +import axios from "axios"; +import CONFIG from "../core/Config.jsx"; + +export const loginUser = async (loginData) => { + try { + const response = await axios.post(`${CONFIG.BASE_URL}/login/`, loginData, { + withCredentials: true, + }); + return response.data.access_token; + } catch (error) { + throw new Error("Login failed: " + error.message); + } +}; diff --git a/web-app/src/components/PrivateRoute.jsx b/web-app/src/components/PrivateRoute.jsx new file mode 100644 index 0000000..e9553ad --- /dev/null +++ b/web-app/src/components/PrivateRoute.jsx @@ -0,0 +1,14 @@ +import {Navigate, Outlet} from "react-router-dom"; +import {useAuth} from "../AuthContext.jsx"; + +const PrivateRoute = () => { + const {user} = useAuth(); + + if (!user) { + return ; + } + + return ; +}; + +export default PrivateRoute; diff --git a/web-app/src/core/Config.jsx b/web-app/src/core/Config.jsx new file mode 100644 index 0000000..78ec13c --- /dev/null +++ b/web-app/src/core/Config.jsx @@ -0,0 +1,5 @@ +const CONFIG = { + BASE_URL: 'http://localhost:8080/api/v1/', +}; + +export default CONFIG; \ No newline at end of file