Промежуточный коммит
This commit is contained in:
parent
3b2bd8cf85
commit
3df60c757f
@ -1,5 +1,5 @@
|
||||
import './App.css';
|
||||
import { useRoutes, Outlet, BrowserRouter } from 'react-router-dom';
|
||||
import { useRoutes, Outlet, BrowserRouter, Routes, Route } from 'react-router-dom';
|
||||
import Header from './components/common/Header';
|
||||
import MainPage from './components/pages/MainPage';
|
||||
import Footer from './components/common/Footer';
|
||||
@ -10,42 +10,38 @@ import { useState } from 'react';
|
||||
import AuthorsPage from './components/pages/dev/AuthorsPage';
|
||||
import GenresPage from './components/pages/dev/GenresPage';
|
||||
import ForumPage from './components/pages/ForumPage'
|
||||
function Router(props) {
|
||||
return useRoutes(props.rootRoute);
|
||||
}
|
||||
|
||||
import SignUpPage from './components/pages/SignUpPage';
|
||||
import PrivateRoutes from './utils/PrivateRoutes';
|
||||
export default function AppDev() {
|
||||
const[searchValue, setSearchValue] = useState();
|
||||
const routes = [
|
||||
{ index: true, element: <MainPage />},
|
||||
{ path: '', element: <MainPage/>, label: 'Главная' },
|
||||
{ path: 'Books', element: <BooksPage searchValue={searchValue} setSearchValue={setSearchValue}/>, label: 'Книги' },
|
||||
{ path: 'Authors', element: <AuthorsPage/>, label: 'Авторы' },
|
||||
{ path: 'Genres', element: <GenresPage/>, label: 'Жанры' },
|
||||
{ path: 'Forum', element:<ForumPage/>, label: 'Форум'},
|
||||
{ path: 'Login', element: <LoginPage/>},
|
||||
{ path: 'Contacts', element: <Contacts/>}
|
||||
{ path: '/', label: "Главная" },
|
||||
{ path: '/Books', label: "Книги" },
|
||||
{ path: '/Authors', label: "Авторы" },
|
||||
{ path: '/Genres', label: "Жанры" },
|
||||
{ path: '/Forum', label: 'Форум'}
|
||||
];
|
||||
const links = routes.filter(route => route.hasOwnProperty('label'));
|
||||
const rootRoute = [
|
||||
{ path: '/', element: render(links), children: routes }
|
||||
];
|
||||
|
||||
function render(links) {
|
||||
return (
|
||||
<>
|
||||
<Header links={links} single={routes[routes.length-2]} setSearchValue={setSearchValue} />
|
||||
<div className="container-fluid">
|
||||
<Outlet />
|
||||
<BrowserRouter>
|
||||
<Header links={routes} setSearchValue={setSearchValue} />
|
||||
<div>
|
||||
<Routes>
|
||||
<Route element={<PrivateRoutes/>}>
|
||||
<Route element={<MainPage/>} path="/" exact />
|
||||
<Route element={<MainPage />} path="*" />
|
||||
<Route element={<BooksPage searchValue={searchValue} setSearchValue={setSearchValue}/>} path="/Books"/>
|
||||
<Route element={<AuthorsPage/>} path="/Authors"/>
|
||||
<Route element={<GenresPage/>} path="/Genres"/>
|
||||
<Route element={<ForumPage/>} path="/Forum"/>
|
||||
<Route element={<Contacts/>} path="/Contacts"/>
|
||||
</Route>
|
||||
<Route element={<LoginPage/>} path="/Login"/>
|
||||
<Route element={<SignUpPage/>} path="/Signup"/>
|
||||
</Routes>
|
||||
</div>
|
||||
<Footer link={routes[routes.length-1]}/>
|
||||
<Footer link={{path: '/Contacts'}}/>
|
||||
</BrowserRouter>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<BrowserRouter>
|
||||
<Router rootRoute={ rootRoute } />
|
||||
</BrowserRouter>
|
||||
);
|
||||
}
|
@ -8,6 +8,7 @@ import ForumPage from './components/pages/ForumPage';
|
||||
import Contacts from './components/pages/Contacts';
|
||||
import LoginPage from './components/pages/LoginPage';
|
||||
import { useState } from 'react';
|
||||
import SignUpPage from './components/pages/SignUpPage';
|
||||
function Router(props) {
|
||||
return useRoutes(props.rootRoute);
|
||||
}
|
||||
@ -21,6 +22,7 @@ function Router(props) {
|
||||
{ path: 'authors', element: null, label: 'Авторы' },
|
||||
{ path: 'forum', element: <ForumPage/>, label: 'Форум' },
|
||||
{ path: 'login', element: <LoginPage/>},
|
||||
{ path: 'signup', element: <SignUpPage/>},
|
||||
{ path: 'contacts', element: <Contacts/>}
|
||||
];
|
||||
const links = routes.filter(route => route.hasOwnProperty('label'));
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { NavLink } from "react-router-dom";
|
||||
import { Link } from "react-router-dom";
|
||||
export default function Footer(props) {
|
||||
return (
|
||||
<footer className="mt-auto" style={{ backgroundColor: "rgb(235,185,185)" }}>
|
||||
@ -6,9 +6,9 @@ export default function Footer(props) {
|
||||
<div className="col-lg-3 col-md-6 mb-4 mb-md-0">
|
||||
<ul className="list-unstyled mb-0">
|
||||
<li>
|
||||
<NavLink className="nav-link" to={props.link.path}>
|
||||
<Link className="nav-link" to={props.link.path}>
|
||||
Контакты
|
||||
</NavLink>
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<a className="nav-link" href="#">
|
||||
|
@ -1,13 +1,21 @@
|
||||
import { useState } from "react";
|
||||
import { NavLink } from "react-router-dom";
|
||||
import { Link } from "react-router-dom";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import AuthService from "../../services/AuthService";
|
||||
|
||||
export default function Header(props) {
|
||||
const navigate = useNavigate();
|
||||
const[state, setState] = useState({
|
||||
text:""
|
||||
});
|
||||
const handleChange = (event) => {
|
||||
setState({ ...state, [event.target.id]: event.target.value });
|
||||
};
|
||||
const handleLogout = () =>{
|
||||
AuthService.logout();
|
||||
navigate("/Login");
|
||||
window.location.reload();
|
||||
};
|
||||
const handleSubmit = (event) => {
|
||||
event.preventDefault();
|
||||
props.setSearchValue(state.text)
|
||||
@ -42,9 +50,9 @@ export default function Header(props) {
|
||||
<ul className="nav nav-tabs justify-content-center border-bottom-0">
|
||||
{props.links.map((route) => (
|
||||
<li key={route.path} className="nav-item">
|
||||
<NavLink className="nav-link" to={route.path}>
|
||||
<Link className="nav-link" to={route.path}>
|
||||
{route.label}
|
||||
</NavLink>
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
@ -63,9 +71,9 @@ export default function Header(props) {
|
||||
</nav>
|
||||
</div>
|
||||
<span className="col text-end">
|
||||
<NavLink className="nav-link" to={props.single.path}>
|
||||
Вход/Регистрация
|
||||
</NavLink>
|
||||
<Link className="nav-link" onClick={handleLogout} to={""}>
|
||||
Выход
|
||||
</Link>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,8 +1,11 @@
|
||||
import React, { useEffect } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { useState } from "react";
|
||||
import AuthService from "../../services/AuthService"
|
||||
export default function LoginPage(props) {
|
||||
const formRef = React.createRef();
|
||||
const [error, setError] = useState(false)
|
||||
const navigate = useNavigate();
|
||||
const [message, setMessage] = useState("");
|
||||
const [state, setState] = useState({
|
||||
login: "",
|
||||
password: ""
|
||||
@ -10,37 +13,33 @@ export default function LoginPage(props) {
|
||||
const handleChange = (event) => {
|
||||
setState({ ...state, [event.target.name]: event.target.value});
|
||||
};
|
||||
const validateEmail = (email) => {
|
||||
return String(email)
|
||||
.toLowerCase()
|
||||
.match(
|
||||
/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
|
||||
);
|
||||
};
|
||||
const handleSubmit = (event) =>{
|
||||
if (!validateEmail(state.login) || state.password.length <=0) {
|
||||
event.preventDefault();
|
||||
setError(true)
|
||||
event.stopPropagation();
|
||||
}
|
||||
else{
|
||||
setError(false)
|
||||
setMessage("");
|
||||
AuthService.login(state.login, state.password).then(
|
||||
() => {
|
||||
navigate("/");
|
||||
window.location.reload();
|
||||
},
|
||||
(error) => {
|
||||
const resMessage =
|
||||
(error.response &&
|
||||
error.response.data &&
|
||||
error.response.data.message) ||
|
||||
error.message ||
|
||||
error.toString();
|
||||
|
||||
setMessage(resMessage);
|
||||
}
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div className="flex-shrink-0" style={{backgroundColor : 'rgb(255,255,255)'}}>
|
||||
<div className="d-flex justify-content-center">
|
||||
<form ref={formRef} noValidate className="w-50 ms-2 needs-validation" onSubmit={(e)=>handleSubmit(e)}>
|
||||
<form ref={formRef} className="w-50 ms-2" onSubmit={(e)=>handleSubmit(e)}>
|
||||
<h2 className="py-3">Вход</h2>
|
||||
<h4>Логин</h4>
|
||||
<input className="form-control my-2" name="login" value={state.login} onChange={(e)=>handleChange(e)} type="email" required />
|
||||
{
|
||||
error && !validateEmail(state.login)
|
||||
?<div>
|
||||
Пожалуйста введите электронную почту вида: "*****@**.**"
|
||||
</div>
|
||||
:<div></div>
|
||||
}
|
||||
<input className="form-control my-2" name="login" value={state.login} onChange={(e)=>handleChange(e)} type="text" required />
|
||||
<h4>Пароль</h4>
|
||||
<input
|
||||
className="form-control my-2"
|
||||
@ -50,22 +49,22 @@ export default function LoginPage(props) {
|
||||
onChange={(e)=>handleChange(e)}
|
||||
required
|
||||
/>
|
||||
{
|
||||
error && state.password.length <=0
|
||||
?<div>
|
||||
Пожалуйста введите пароль
|
||||
</div>
|
||||
:<div></div>
|
||||
}
|
||||
<div>
|
||||
<button className="btn btn-primary m-2" type="submit">
|
||||
Войти
|
||||
</button>
|
||||
<a href="" style={{marginTop: 1, marginLeft: 1}}>
|
||||
<a href="/Signup" style={{marginTop: 1, marginLeft: 1}}>
|
||||
Зарегистрируйтесь, если нет аккаунта, здесь
|
||||
</a>
|
||||
</div>
|
||||
</form>
|
||||
{message && (
|
||||
<div className="form-group">
|
||||
<div className="alert alert-danger" role="alert">
|
||||
{message}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
82
front/src/components/pages/SignUpPage.jsx
Normal file
82
front/src/components/pages/SignUpPage.jsx
Normal file
@ -0,0 +1,82 @@
|
||||
import React, { useEffect } from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import { useState } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import AuthService from "../../services/AuthService"
|
||||
export default function SignUpPage(props) {
|
||||
const formRef = React.createRef();
|
||||
const navigate = useNavigate();
|
||||
const [message, setMessage] = useState("");
|
||||
const [state, setState] = useState({
|
||||
login: "",
|
||||
password: "",
|
||||
passwordConfirm: ""
|
||||
});
|
||||
const handleChange = (event) => {
|
||||
setState({ ...state, [event.target.name]: event.target.value});
|
||||
};
|
||||
const handleSubmit = (event) =>{
|
||||
event.preventDefault();
|
||||
setMessage("");
|
||||
AuthService.register(state.login, state.password, state.passwordConfirm).then(
|
||||
() => {
|
||||
navigate("/Login");
|
||||
window.location.reload();
|
||||
},
|
||||
(error) => {
|
||||
const resMessage =
|
||||
(error.response &&
|
||||
error.response.data &&
|
||||
error.response.data.message) ||
|
||||
error.message ||
|
||||
error.toString();
|
||||
|
||||
setMessage(resMessage);
|
||||
}
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div className="flex-shrink-0" style={{backgroundColor : 'rgb(255,255,255)'}}>
|
||||
<div className="d-flex justify-content-center">
|
||||
<form ref={formRef} className="w-50 ms-2" onSubmit={(e)=>handleSubmit(e)}>
|
||||
<h2 className="py-3">Вход</h2>
|
||||
<h4>Логин</h4>
|
||||
<input className="form-control my-2" name="login" value={state.login} onChange={(e)=>handleChange(e)} type="text" required />
|
||||
<h4>Пароль</h4>
|
||||
<input
|
||||
className="form-control my-2"
|
||||
type="password"
|
||||
name="password"
|
||||
value={state.password}
|
||||
onChange={(e)=>handleChange(e)}
|
||||
required
|
||||
/>
|
||||
<h4>Подтверждение пароля</h4>
|
||||
<input
|
||||
className="form-control my-2"
|
||||
type="password"
|
||||
name="passwordConfirm"
|
||||
value={state.passwordConfirm}
|
||||
onChange={(e)=>handleChange(e)}
|
||||
required
|
||||
/>
|
||||
<div>
|
||||
<button className="btn btn-primary m-2" type="submit">
|
||||
Создать аккаунт
|
||||
</button>
|
||||
<Link className="nav-link" to={"/Login"}>
|
||||
Назад
|
||||
</Link>
|
||||
</div>
|
||||
</form>
|
||||
{message && (
|
||||
<div className="form-group">
|
||||
<div className="alert alert-danger" role="alert">
|
||||
{message}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
6
front/src/modelsDTO/UserLoginDto.js
Normal file
6
front/src/modelsDTO/UserLoginDto.js
Normal file
@ -0,0 +1,6 @@
|
||||
export default class UserLoginDto {
|
||||
constructor(data) {
|
||||
this.login = data?.login;
|
||||
this.password = data?.password;
|
||||
}
|
||||
}
|
7
front/src/modelsDTO/UserSignUpDto.js
Normal file
7
front/src/modelsDTO/UserSignUpDto.js
Normal file
@ -0,0 +1,7 @@
|
||||
export default class UserSignUpDto {
|
||||
constructor(data) {
|
||||
this.login = data?.login;
|
||||
this.password = data?.password;
|
||||
this.passwordConfirm = data?.passwordConfirm;
|
||||
}
|
||||
}
|
32
front/src/services/AuthService.js
Normal file
32
front/src/services/AuthService.js
Normal file
@ -0,0 +1,32 @@
|
||||
import axios from "axios";
|
||||
import UserSignUpDto from "../modelsDTO/UserSignUpDto";
|
||||
import UserLoginDto from "../modelsDTO/UserLoginDto";
|
||||
|
||||
const API_URL = "http://localhost:8080";
|
||||
|
||||
const register = (username, password, passwordConfirm) => {
|
||||
return axios.post(API_URL + "/signup", new UserSignUpDto({login: username,
|
||||
password: password,
|
||||
passwordConfirm: passwordConfirm}));
|
||||
};
|
||||
|
||||
const login = (username, password) => {
|
||||
return axios
|
||||
.post(API_URL + "/jwt/login", new UserLoginDto({login: username, password: password}))
|
||||
.then((response) => {
|
||||
if(response.status === 200){
|
||||
localStorage.setItem("token", response.data)
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const logout = () => {
|
||||
localStorage.removeItem("token");
|
||||
};
|
||||
const AuthService = {
|
||||
register,
|
||||
login,
|
||||
logout,
|
||||
};
|
||||
|
||||
export default AuthService;
|
14
front/src/utils/PrivateRoutes.jsx
Normal file
14
front/src/utils/PrivateRoutes.jsx
Normal file
@ -0,0 +1,14 @@
|
||||
import { Outlet, Navigate } from 'react-router-dom'
|
||||
|
||||
const PrivateRoutes = () => {
|
||||
let getPermission = false;
|
||||
let userToken = localStorage.getItem("token");
|
||||
if(userToken){
|
||||
getPermission = true;
|
||||
}
|
||||
return(
|
||||
getPermission ? <Outlet/> : <Navigate to="/Login"/>
|
||||
)
|
||||
}
|
||||
|
||||
export default PrivateRoutes
|
@ -1,6 +1,7 @@
|
||||
package com.labwork01.app.author.model;
|
||||
|
||||
import com.labwork01.app.book.model.Book;
|
||||
import com.labwork01.app.user.model.User;
|
||||
import jakarta.persistence.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@ -19,6 +20,9 @@ public class Author {
|
||||
private String patronymic;
|
||||
@OneToMany(cascade = CascadeType.REMOVE, mappedBy = "author", fetch = FetchType.EAGER)
|
||||
private List<Book> books =new ArrayList<>();
|
||||
@ManyToOne
|
||||
@JoinColumn(name= "user_id", nullable = false)
|
||||
private User user;
|
||||
|
||||
public List<Book> getBooks() {
|
||||
return books;
|
||||
@ -71,6 +75,12 @@ public class Author {
|
||||
public void setPatronymic(String patronymic) {
|
||||
this.patronymic = patronymic;
|
||||
}
|
||||
public User getUser() {
|
||||
return user;
|
||||
}
|
||||
public void setUser(User user) {
|
||||
this.user = user;
|
||||
}
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
@ -88,6 +98,7 @@ public class Author {
|
||||
"id=" + id +
|
||||
", name='" + name + '\'' + '}' +
|
||||
", surname='" + surname + '\'' + '}' +
|
||||
", patronymic='" + patronymic + '\'' + '}';
|
||||
", patronymic='" + patronymic + '\'' +
|
||||
", user='" + user.toString() + '\'' + '}' +'}';
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,12 @@ package com.labwork01.app.author.service;
|
||||
|
||||
import com.labwork01.app.author.model.Author;
|
||||
import com.labwork01.app.author.repository.AuthorRepository;
|
||||
import com.labwork01.app.user.model.User;
|
||||
import com.labwork01.app.user.model.UserRole;
|
||||
import com.labwork01.app.user.service.UserService;
|
||||
import com.labwork01.app.util.validation.ValidatorUtil;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@ -12,15 +17,25 @@ import java.util.Optional;
|
||||
@Service
|
||||
public class AuthorService {
|
||||
private final AuthorRepository authorRepository;
|
||||
private final UserService userService;
|
||||
private final ValidatorUtil validatorUtil;
|
||||
public AuthorService(AuthorRepository authorRepository, ValidatorUtil validatorUtil){
|
||||
public AuthorService(AuthorRepository authorRepository, UserService userService, ValidatorUtil validatorUtil){
|
||||
this.authorRepository = authorRepository;
|
||||
this.userService = userService;
|
||||
this.validatorUtil = validatorUtil;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public Author addAuthor(String name, String surname, String patronymic) {
|
||||
final Author author = new Author(name,surname,patronymic);
|
||||
Object currentUser = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
|
||||
if(currentUser instanceof UserDetails){
|
||||
String username = ((UserDetails)currentUser).getUsername();
|
||||
User user = userService.findByLogin(username);
|
||||
if(user.getRole() == UserRole.ADMIN){
|
||||
author.setUser(user);
|
||||
}
|
||||
}
|
||||
validatorUtil.validate(author);
|
||||
return authorRepository.save(author);
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package com.labwork01.app.book.model;
|
||||
|
||||
import com.labwork01.app.author.model.Author;
|
||||
import com.labwork01.app.genre.model.Genre;
|
||||
import com.labwork01.app.user.model.User;
|
||||
import jakarta.persistence.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@ -25,6 +26,9 @@ public class Book {
|
||||
@ManyToOne(fetch = FetchType.EAGER)
|
||||
@JoinColumn(name = "author_id", nullable = false)
|
||||
private Author author;
|
||||
@ManyToOne
|
||||
@JoinColumn(name= "user_id", nullable = false)
|
||||
private User user;
|
||||
public Book() {
|
||||
}
|
||||
|
||||
@ -89,6 +93,12 @@ public class Book {
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
public User getUser() {
|
||||
return user;
|
||||
}
|
||||
public void setUser(User user) {
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
@ -111,6 +121,7 @@ public class Book {
|
||||
", description='" + description + '\'' +
|
||||
", author='" + author.toString() + '\'' +
|
||||
", genres='" + String.join(", ",getGenresName()) + '\'' +
|
||||
", user='" + user.toString() + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,12 @@ import com.labwork01.app.book.model.Book;
|
||||
import com.labwork01.app.book.repository.BookRepository;
|
||||
import com.labwork01.app.genre.model.Genre;
|
||||
import com.labwork01.app.genre.service.GenreService;
|
||||
import com.labwork01.app.user.model.User;
|
||||
import com.labwork01.app.user.model.UserRole;
|
||||
import com.labwork01.app.user.service.UserService;
|
||||
import com.labwork01.app.util.validation.ValidatorUtil;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@ -18,11 +23,13 @@ public class BookService {
|
||||
private final BookRepository bookRepository;
|
||||
private final GenreService genreService;
|
||||
private final AuthorService authorService;
|
||||
private final UserService userService;
|
||||
private final ValidatorUtil validatorUtil;
|
||||
public BookService(BookRepository bookRepository,GenreService genreService, AuthorService authorService, ValidatorUtil validatorUtil){
|
||||
public BookService(BookRepository bookRepository,GenreService genreService, AuthorService authorService, UserService userService, ValidatorUtil validatorUtil){
|
||||
this.bookRepository = bookRepository;
|
||||
this.genreService = genreService;
|
||||
this.authorService = authorService;
|
||||
this.userService = userService;
|
||||
this.validatorUtil = validatorUtil;
|
||||
}
|
||||
|
||||
@ -33,6 +40,14 @@ public class BookService {
|
||||
for (Genre genre: genres) {
|
||||
book.addGenre(genre);
|
||||
}
|
||||
Object currentUser = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
|
||||
if(currentUser instanceof UserDetails){
|
||||
String username = ((UserDetails)currentUser).getUsername();
|
||||
User user = userService.findByLogin(username);
|
||||
if(user.getRole() == UserRole.ADMIN){
|
||||
book.setUser(user);
|
||||
}
|
||||
}
|
||||
validatorUtil.validate(book);
|
||||
return bookRepository.save(book);
|
||||
}
|
||||
|
@ -11,13 +11,17 @@ import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
|
||||
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
@EnableGlobalMethodSecurity(securedEnabled = true)
|
||||
public class SecurityConfiguration {
|
||||
private final Logger log = LoggerFactory.getLogger(SecurityConfiguration.class);
|
||||
|
||||
@ -48,6 +52,7 @@ public class SecurityConfiguration {
|
||||
.authorizeHttpRequests()
|
||||
.requestMatchers("/", SPA_URL_MASK).permitAll()
|
||||
.requestMatchers(HttpMethod.POST, UserController.URL_LOGIN).permitAll()
|
||||
.requestMatchers(HttpMethod.POST, UserController.URL_SIGNUP).permitAll()
|
||||
.anyRequest().authenticated()
|
||||
.and()
|
||||
.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class)
|
||||
@ -65,7 +70,12 @@ public class SecurityConfiguration {
|
||||
public WebSecurityCustomizer webSecurityCustomizer() {
|
||||
return (web) -> web.ignoring()
|
||||
.requestMatchers(HttpMethod.OPTIONS, "/**")
|
||||
.requestMatchers("/**/*.{js,html,css,png}")
|
||||
.requestMatchers("/*.js")
|
||||
.requestMatchers("/*.html")
|
||||
.requestMatchers("/*.css")
|
||||
.requestMatchers("/assets/**")
|
||||
.requestMatchers("/favicon.ico")
|
||||
.requestMatchers("/.js", "/.css")
|
||||
.requestMatchers("/swagger-ui/index.html")
|
||||
.requestMatchers("/webjars/**")
|
||||
.requestMatchers("/swagger-resources/**")
|
||||
|
@ -6,10 +6,7 @@ import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerF
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.servlet.config.annotation.CorsRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.ViewControllerRegistration;
|
||||
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
import org.springframework.web.servlet.config.annotation.*;
|
||||
|
||||
@Configuration
|
||||
class WebConfiguration implements WebMvcConfigurer {
|
||||
@ -21,7 +18,7 @@ class WebConfiguration implements WebMvcConfigurer {
|
||||
public void addViewControllers(ViewControllerRegistry registry) {
|
||||
registry.addViewController(SecurityConfiguration.SPA_URL_MASK).setViewName("forward:/");
|
||||
ViewControllerRegistration registration = registry.addViewController("/notFound");
|
||||
registration.setViewName("forward:/index.html");
|
||||
registration.setViewName("forward:/");
|
||||
registration.setStatusCode(HttpStatus.OK);
|
||||
|
||||
// Alternative way (404 error hits the console):
|
||||
@ -34,4 +31,16 @@ class WebConfiguration implements WebMvcConfigurer {
|
||||
container.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND, "/notFound"));
|
||||
};
|
||||
}
|
||||
@Override
|
||||
public void addResourceHandlers(
|
||||
ResourceHandlerRegistry registry) {
|
||||
registry.addResourceHandler("/static/**")
|
||||
.addResourceLocations("/WEB-INF/view/react/build/static/");
|
||||
registry.addResourceHandler("/*.js")
|
||||
.addResourceLocations("/WEB-INF/view/react/build/");
|
||||
registry.addResourceHandler("/*.json")
|
||||
.addResourceLocations("/WEB-INF/view/react/build/");
|
||||
registry.addResourceHandler("/*.ico")
|
||||
.addResourceLocations("/WEB-INF/view/react/build/");
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package com.labwork01.app.genre.model;
|
||||
|
||||
import com.labwork01.app.user.model.User;
|
||||
import jakarta.persistence.*;
|
||||
|
||||
import java.util.Objects;
|
||||
@ -11,6 +12,9 @@ public class Genre {
|
||||
private Long id;
|
||||
@Column(nullable = false)
|
||||
private String name;
|
||||
@ManyToOne
|
||||
@JoinColumn(name= "user_id", nullable = false)
|
||||
private User user;
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
@ -23,6 +27,12 @@ public class Genre {
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
public User getUser() {
|
||||
return user;
|
||||
}
|
||||
public void setUser(User user) {
|
||||
this.user = user;
|
||||
}
|
||||
public Genre() {
|
||||
}
|
||||
public Genre(String name) {
|
||||
@ -44,6 +54,7 @@ public class Genre {
|
||||
public String toString() {
|
||||
return "Genre{" +
|
||||
"id=" + id +
|
||||
", name='" + name + '\'' + '}';
|
||||
", name='" + name + '\'' +
|
||||
", user='" + user.toString() + '\'' +'}';
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,12 @@ package com.labwork01.app.genre.service;
|
||||
import com.labwork01.app.book.model.Book;
|
||||
import com.labwork01.app.genre.model.Genre;
|
||||
import com.labwork01.app.genre.repository.GenreRepository;
|
||||
import com.labwork01.app.user.model.User;
|
||||
import com.labwork01.app.user.model.UserRole;
|
||||
import com.labwork01.app.user.service.UserService;
|
||||
import com.labwork01.app.util.validation.ValidatorUtil;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@ -13,15 +18,25 @@ import java.util.Optional;
|
||||
@Service
|
||||
public class GenreService {
|
||||
private final GenreRepository genreRepository;
|
||||
private final UserService userService;
|
||||
private final ValidatorUtil validatorUtil;
|
||||
public GenreService(GenreRepository genreRepository, ValidatorUtil validatorUtil){
|
||||
public GenreService(GenreRepository genreRepository, UserService userService, ValidatorUtil validatorUtil){
|
||||
this.genreRepository = genreRepository;
|
||||
this.userService = userService;
|
||||
this.validatorUtil = validatorUtil;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public Genre addGenre(String name) {
|
||||
final Genre genre = new Genre(name);
|
||||
Object currentUser = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
|
||||
if(currentUser instanceof UserDetails){
|
||||
String username = ((UserDetails)currentUser).getUsername();
|
||||
User user = userService.findByLogin(username);
|
||||
if(user.getRole() == UserRole.ADMIN){
|
||||
genre.setUser(user);
|
||||
}
|
||||
}
|
||||
validatorUtil.validate(genre);
|
||||
return genreRepository.save(genre);
|
||||
}
|
||||
|
@ -1,23 +1,45 @@
|
||||
package com.labwork01.app.user.controller;
|
||||
|
||||
import com.labwork01.app.configuration.OpenAPI30Configuration;
|
||||
import com.labwork01.app.user.model.User;
|
||||
import com.labwork01.app.user.model.UserRole;
|
||||
import com.labwork01.app.user.service.UserService;
|
||||
import com.labwork01.app.util.validation.ValidationException;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.security.access.annotation.Secured;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
|
||||
@RestController
|
||||
public class UserController {
|
||||
public static final String URL_LOGIN = "/jwt/login";
|
||||
public static final String URL_SIGNUP = "/signup";
|
||||
public static final String URL_MAIN = "/users";
|
||||
|
||||
private final UserService userService;
|
||||
|
||||
public UserController(UserService userService) {
|
||||
this.userService = userService;
|
||||
}
|
||||
|
||||
@GetMapping(OpenAPI30Configuration.API_PREFIX + URL_MAIN)
|
||||
@Secured({UserRole.AsString.ADMIN})
|
||||
public Page<UserDto> getUsers(@RequestParam(defaultValue = "1") int page,
|
||||
@RequestParam(defaultValue = "5") int size){
|
||||
return userService.findAllPages(page, size).map(UserDto::new);
|
||||
}
|
||||
@PostMapping(URL_LOGIN)
|
||||
public String login(@RequestBody @Valid UserDto userDto) {
|
||||
return userService.loginAndGetToken(userDto);
|
||||
public String login(@RequestBody @Valid UserLoginDto userLoginDto) {
|
||||
return userService.loginAndGetToken(userLoginDto);
|
||||
}
|
||||
@PostMapping(URL_SIGNUP)
|
||||
public String signup(@RequestBody @Valid UserSignupDto userSignupDto){
|
||||
try {
|
||||
User user = userService.createUser(userSignupDto.getLogin(), userSignupDto.getPassword(), userSignupDto.getPasswordConfirm());
|
||||
return user.getLogin() + "was created";
|
||||
}
|
||||
catch(ValidationException e){
|
||||
return e.getMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,13 +6,11 @@ import com.labwork01.app.user.model.UserRole;
|
||||
public class UserDto {
|
||||
private final long id;
|
||||
private final String login;
|
||||
private final String password;
|
||||
private final UserRole role;
|
||||
|
||||
public UserDto(User user) {
|
||||
this.id = user.getId();
|
||||
this.login = user.getLogin();
|
||||
this.password = user.getPassword();
|
||||
this.role = user.getRole();
|
||||
}
|
||||
|
||||
@ -27,7 +25,4 @@ public class UserDto {
|
||||
public UserRole getRole() {
|
||||
return role;
|
||||
}
|
||||
public String getPassword(){
|
||||
return password;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,25 @@
|
||||
package com.labwork01.app.user.controller;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
|
||||
public class UserLoginDto {
|
||||
@NotBlank
|
||||
private String login;
|
||||
@NotBlank
|
||||
private String password;
|
||||
public String getLogin() {
|
||||
return login;
|
||||
}
|
||||
|
||||
public void setLogin(String login) {
|
||||
this.login = login;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
}
|
@ -2,7 +2,7 @@ package com.labwork01.app.user.service;
|
||||
|
||||
import com.labwork01.app.configuration.jwt.JwtException;
|
||||
import com.labwork01.app.configuration.jwt.JwtProvider;
|
||||
import com.labwork01.app.user.controller.UserDto;
|
||||
import com.labwork01.app.user.controller.UserLoginDto;
|
||||
import com.labwork01.app.user.model.User;
|
||||
import com.labwork01.app.user.model.UserRole;
|
||||
import com.labwork01.app.user.repository.UserRepository;
|
||||
@ -61,7 +61,7 @@ public class UserService implements UserDetailsService {
|
||||
return userRepository.save(user);
|
||||
}
|
||||
|
||||
public String loginAndGetToken(UserDto userDto) {
|
||||
public String loginAndGetToken(UserLoginDto userDto) {
|
||||
final User user = findByLogin(userDto.getLogin());
|
||||
if (user == null) {
|
||||
throw new UserNotFoundException(userDto.getLogin());
|
||||
|
Loading…
Reference in New Issue
Block a user