Compare commits
56 Commits
LabWork03
...
LabWork06R
| Author | SHA1 | Date | |
|---|---|---|---|
| a36cf7602e | |||
| 6c4676d7e1 | |||
| 02b25cd834 | |||
| 03db68af7d | |||
|
|
7dbe6221ac | ||
|
|
7762a31191 | ||
| 1d7e424614 | |||
| 39fa662ca5 | |||
| b43b77ac12 | |||
| 9e6b5cc2db | |||
| 6346309870 | |||
| 864614acea | |||
| b3f98c6325 | |||
| 8d3470086b | |||
| 03e08d61c2 | |||
| 1711bb58e8 | |||
| 48cb6e0069 | |||
| 6f31895209 | |||
| 4a77884293 | |||
| 28e4942727 | |||
| 0c89bb621d | |||
| bc84497586 | |||
| 917457737a | |||
| e4adadadd0 | |||
| f626c3a2f4 | |||
| 982eaa2916 | |||
| 0d47401bda | |||
| e7fc79a52b | |||
| c3ebc3dbf6 | |||
| bfd9a5782a | |||
| 9ffcbf46b1 | |||
| 9d25cecd0a | |||
| 821864756b | |||
| ffe8c883fc | |||
| a2b914e92b | |||
| 27f60ab933 | |||
| 5e9c107b87 | |||
| 1117bed0cd | |||
| 0d7ee43b26 | |||
|
|
3cd78dbcfc | ||
|
|
ff8f6edaa7 | ||
|
|
62139c07ed | ||
|
|
f132446fff | ||
| c73632f42f | |||
| 13c1c40aa5 | |||
| b908b68951 | |||
| 25e327f935 | |||
| e273980147 | |||
| d3a42cd753 | |||
| 1cfcffa2e9 | |||
| d1c72b538a | |||
| 282f57099b | |||
| 4c199c456b | |||
| ef720bbc01 | |||
| 254ddf6f54 | |||
| a09eae2e6f |
18
build.gradle
18
build.gradle
@@ -11,14 +11,26 @@ sourceCompatibility = '17'
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
jar {
|
||||
enabled = false
|
||||
}
|
||||
dependencies {
|
||||
implementation(project(':front'))
|
||||
annotationProcessor "org.springframework.boot:spring-boot-configuration-processor"
|
||||
implementation 'org.springframework.boot:spring-boot-starter-web'
|
||||
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
|
||||
implementation 'org.springframework.boot:spring-boot-starter-validation'
|
||||
implementation 'com.h2database:h2:2.1.210'
|
||||
implementation group: 'org.springdoc', name: 'springdoc-openapi-ui', version: '1.6.5'
|
||||
implementation 'org.springframework.boot:spring-boot-starter-security'
|
||||
implementation 'com.auth0:java-jwt:4.4.0'
|
||||
implementation 'org.hibernate.validator:hibernate-validator'
|
||||
implementation 'org.springframework.boot:spring-boot-devtools'
|
||||
implementation 'org.springdoc:springdoc-openapi-ui:1.6.5'
|
||||
implementation 'org.webjars:bootstrap:5.1.3'
|
||||
implementation 'org.webjars:jquery:3.6.0'
|
||||
implementation 'org.webjars:font-awesome:6.1.0'
|
||||
testImplementation 'org.springframework.boot:spring-boot-starter-test'
|
||||
//implementation group: 'org.springdoc', name: 'springdoc-openapi-ui', version: '1.6.5'
|
||||
|
||||
}
|
||||
|
||||
tasks.named('test') {
|
||||
|
||||
BIN
data.mv.db
Normal file
BIN
data.mv.db
Normal file
Binary file not shown.
2065
data.trace.db
Normal file
2065
data.trace.db
Normal file
File diff suppressed because it is too large
Load Diff
26
front/.gitignore
vendored
Normal file
26
front/.gitignore
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
.parcel-cache
|
||||
58
front/build.gradle
Normal file
58
front/build.gradle
Normal file
@@ -0,0 +1,58 @@
|
||||
import com.github.gradle.node.util.PlatformHelper
|
||||
import groovy.text.SimpleTemplateEngine
|
||||
|
||||
plugins {
|
||||
id 'java'
|
||||
id 'com.github.node-gradle.node' version '3.5.1'
|
||||
id "de.undercouch.download" version '5.3.1'
|
||||
}
|
||||
|
||||
node {
|
||||
version = '18.15.0'
|
||||
download = true
|
||||
}
|
||||
|
||||
jar.dependsOn 'npmBuild'
|
||||
|
||||
clean.dependsOn 'npmClean'
|
||||
|
||||
nodeSetup.dependsOn 'downloadNode'
|
||||
|
||||
jar {
|
||||
from 'dist'
|
||||
into 'static'
|
||||
final devHost = 'http://localhost:8080'
|
||||
final prodHost = ''
|
||||
filesMatching('index.html') {
|
||||
filter { line -> line.replaceAll(devHost, prodHost) }
|
||||
}
|
||||
}
|
||||
|
||||
task downloadNode(type: Download) {
|
||||
final helper = new PlatformHelper()
|
||||
final templateData = [
|
||||
"url" : node.distBaseUrl.get(),
|
||||
"version": node.version.get(),
|
||||
"os" : helper.osName,
|
||||
"arch" : helper.osArch,
|
||||
"ext" : helper.windows ? 'zip' : 'tar.gz'
|
||||
]
|
||||
final urlTemplate = '${url}/v${version}/node-v${version}-${os}-${arch}.${ext}'
|
||||
final engine = new SimpleTemplateEngine()
|
||||
final url = engine.createTemplate(urlTemplate).make(templateData).toString()
|
||||
final String destDir = '.gradle/'
|
||||
file(destDir).mkdirs()
|
||||
src url
|
||||
dest destDir
|
||||
overwrite false
|
||||
}
|
||||
|
||||
tasks.register('npmBuild', NpmTask) {
|
||||
dependsOn npmInstall
|
||||
args = ['run-script', 'build']
|
||||
}
|
||||
|
||||
tasks.register('npmClean', NpmTask) {
|
||||
dependsOn npmInstall
|
||||
args = ['run-script', 'clean']
|
||||
}
|
||||
16
front/index.html
Normal file
16
front/index.html
Normal file
@@ -0,0 +1,16 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<script type="module" src="/node_modules/bootstrap/dist/js/bootstrap.min.js"></script>
|
||||
<link rel="stylesheet" href="/node_modules/bootstrap/dist/css/bootstrap.min.css">
|
||||
<link rel="stylesheet" href="/node_modules/@fortawesome/fontawesome-free/css/all.min.css">
|
||||
<title>ReManga</title>
|
||||
</head>
|
||||
<body >
|
||||
<div id="app" class="d-flex flex-column h-100" ></div>
|
||||
<script type="module" src="/src/main.jsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
3026
front/package-lock.json
generated
Normal file
3026
front/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
24
front/package.json
Normal file
24
front/package.json
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"name": "front",
|
||||
"version": "1.0.0",
|
||||
"source": "index.html",
|
||||
"scripts": {
|
||||
"start": "parcel --port 3000",
|
||||
"build": "npm run clean && parcel build",
|
||||
"clean": "rimraf dist"
|
||||
},
|
||||
"dependencies": {
|
||||
"bootstrap": "5.3.0-alpha2",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-router-dom": "^6.4.4",
|
||||
"@fortawesome/fontawesome-free": "^6.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^18.0.24",
|
||||
"@types/react-dom": "^18.0.8",
|
||||
"parcel": "2.8.3",
|
||||
"process": "^0.11.10",
|
||||
"rimraf": "4.4.0"
|
||||
}
|
||||
}
|
||||
49
front/src/App.jsx
Normal file
49
front/src/App.jsx
Normal file
@@ -0,0 +1,49 @@
|
||||
import { Routes, BrowserRouter, Route } from 'react-router-dom';
|
||||
import Header from './components/Header';
|
||||
import CreatorAction from './Main/CreatorAction';
|
||||
import ReaderAction from './Main/ReaderAction';
|
||||
import UsersPage from './Main/UsersPage';
|
||||
import Catalog from './Main/Catalog';
|
||||
import LoginPage from './Main/LoginPage';
|
||||
import SingupPage from './Main/SingupPage';
|
||||
import PrivateRoutes from "./components/PrivateRoutes";
|
||||
import MangaPage from "./Main/MangaPage";
|
||||
|
||||
function Router(props) {
|
||||
return useRoutes(props.rootRoute);
|
||||
}
|
||||
|
||||
export default function App() {
|
||||
|
||||
const links = [
|
||||
{ path: 'catalog', label: "Catalog", userGroup: "AUTH" },
|
||||
{ path: 'readerAction', label: "ReaderAction", userGroup: "USER" },
|
||||
{ path: 'creatorAction', label: "CreatorAction", userGroup: "ADMIN" },
|
||||
{ path: 'users', label: "Users", userGroup: "ADMIN" }
|
||||
];
|
||||
return (
|
||||
<>
|
||||
<BrowserRouter>
|
||||
<Header links={links}></Header>
|
||||
<div className="content-div">
|
||||
<Routes>
|
||||
<Route element={<LoginPage />} path="/login" />
|
||||
<Route element={<SingupPage />} path="/singup" />
|
||||
<Route element={<PrivateRoutes userGroup="AUTH" />}>
|
||||
<Route element={<MangaPage />} path="/mangapage" />
|
||||
<Route element={<Catalog />} path="/catalog" />
|
||||
<Route element={<Catalog />} path="*" />
|
||||
</Route>
|
||||
<Route element={<PrivateRoutes userGroup="USER" />}>
|
||||
<Route element={<ReaderAction />} path="/readerAction" />
|
||||
</Route>
|
||||
<Route element={<PrivateRoutes userGroup="ADMIN" />}>
|
||||
<Route element={<UsersPage />} path="/users" />
|
||||
<Route element={<CreatorAction />} path="/creatorAction" />
|
||||
</Route>
|
||||
</Routes>
|
||||
</div>
|
||||
</BrowserRouter>
|
||||
</>
|
||||
);
|
||||
}
|
||||
9
front/src/Dto/Manga-Dto.js
Normal file
9
front/src/Dto/Manga-Dto.js
Normal file
@@ -0,0 +1,9 @@
|
||||
export default class MangaDto {
|
||||
constructor(args) {
|
||||
this.id = args.id || null;
|
||||
this.creatorId = args.creatorId || 0;
|
||||
this.mangaName = args.mangaName || "";
|
||||
this.image = args.image;
|
||||
this.chapterCount = args.chapterCount || 0;
|
||||
}
|
||||
}
|
||||
8
front/src/Dto/user-singup-dto.js
Normal file
8
front/src/Dto/user-singup-dto.js
Normal file
@@ -0,0 +1,8 @@
|
||||
export default class UserSignupDto {
|
||||
constructor(args) {
|
||||
this.login = args.login;
|
||||
this.email = args.email;
|
||||
this.password = args.password;
|
||||
this.passwordConfirm = args.passwordConfirm;
|
||||
}
|
||||
}
|
||||
47
front/src/Main/Catalog.jsx
Normal file
47
front/src/Main/Catalog.jsx
Normal file
@@ -0,0 +1,47 @@
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { NavLink } from 'react-router-dom';
|
||||
|
||||
export default function Catalog() {
|
||||
|
||||
const host = "http://localhost:8080/api/1.0";
|
||||
|
||||
const [mangs, setMangs] = useState([]);
|
||||
|
||||
useEffect(() => {
|
||||
getMangs()
|
||||
.then(_data =>setMangs(_data));
|
||||
console.log(2);
|
||||
console.log(mangs);
|
||||
},[]);
|
||||
|
||||
const getTokenForHeader = function () {
|
||||
return "Bearer " + localStorage.getItem("token");
|
||||
}
|
||||
|
||||
const getMangs = async function () {
|
||||
const requestParams = {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Authorization": getTokenForHeader(),
|
||||
}
|
||||
};
|
||||
const response = await fetch(host + "/manga", requestParams);
|
||||
const _data = await response.json()
|
||||
console.log(_data);
|
||||
return _data;
|
||||
}
|
||||
|
||||
return (
|
||||
<article className="p-2 catalog_article">
|
||||
<div className = "catalog_wrapper">
|
||||
<h1>Каталог</h1>
|
||||
<div className="p-2 d-flex flex-wrap">
|
||||
{mangs.map((manga, index) => (
|
||||
<NavLink key={manga.id} to={`/mangapage?id=${manga.id}`}><img src={manga.image} alt={manga.mangaName} className="slideshow"/></NavLink>
|
||||
))}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
);
|
||||
}
|
||||
213
front/src/Main/CreatorAction.jsx
Normal file
213
front/src/Main/CreatorAction.jsx
Normal file
@@ -0,0 +1,213 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { NavLink } from 'react-router-dom';
|
||||
import TableCreator from '../components/Table/TableCreator';
|
||||
import MangaDto from "../Dto/Manga-Dto";
|
||||
import MangaCreatorList from "../components/List/MangaCreatorList";
|
||||
import AddMangaModal from "../components/Modal/AddMangaModal";
|
||||
import EditMangaModal from "../components/Modal/EditMangaModal";
|
||||
|
||||
export default function CreatorAction() {
|
||||
|
||||
const host = "http://localhost:8080/api/1.0";
|
||||
|
||||
const [creatorData, setCreatorData] = useState([]);
|
||||
|
||||
const [creator, setCreator] = useState([]);
|
||||
|
||||
const [mangaId, setMangaId] = useState(0);
|
||||
|
||||
const [chapterCount, setChapterCount] = useState(0);
|
||||
|
||||
const [mangaName, setMangaName] = useState("");
|
||||
|
||||
const [mangaModel, setMangaModel] = useState(new MangaDto({}));
|
||||
|
||||
|
||||
|
||||
const getTokenForHeader = function () {
|
||||
return "Bearer " + localStorage.getItem("token");
|
||||
}
|
||||
|
||||
/* useEffect(() => {
|
||||
getCreatorData()
|
||||
.then(_data =>setCreatorData(_data));
|
||||
},[]);*/
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
getCreator().then(_data => {
|
||||
setCreator(_data)
|
||||
console.log(_data);
|
||||
});
|
||||
}, []);
|
||||
|
||||
const getCreator = async function () {
|
||||
const requestParams = {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": getTokenForHeader(),
|
||||
}
|
||||
};
|
||||
let login = localStorage.getItem("user");
|
||||
console.log(host + `/creator/` + login);
|
||||
const response = await fetch(host + `/creator/` + login, requestParams);
|
||||
const _data = await response.json()
|
||||
return _data;
|
||||
}
|
||||
|
||||
const getCreatorData = async function () {
|
||||
const requestParams = {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Authorization": getTokenForHeader(),
|
||||
}
|
||||
};
|
||||
const response = await fetch(host + "/creator", requestParams);
|
||||
console.log(response);
|
||||
const _data = await response.json()
|
||||
return _data;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
const updateButton = (e) =>{
|
||||
e.preventDefault();
|
||||
update().then((result) => {
|
||||
alert(`Manga[id=${result.id}, mangaName=${result.mangaName}, chapterCount=${result.chapterCount}]`);
|
||||
getCreator(creator.id)
|
||||
.then(_data =>setCreator(_data));
|
||||
});
|
||||
}
|
||||
|
||||
const update = async function (){
|
||||
console.info('Try to update item');
|
||||
if (mangaId === 0) {
|
||||
return;
|
||||
}
|
||||
mangaModel.id = mangaId;
|
||||
mangaModel.chapterCount = chapterCount;
|
||||
mangaModel.creatorId = creator.id;
|
||||
mangaModel.image = imageURL;
|
||||
mangaModel.mangaName = mangaName;
|
||||
const requestParams = {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": getTokenForHeader(),
|
||||
},
|
||||
body: JSON.stringify(mangaModel),
|
||||
};
|
||||
const response = await fetch(host + `/manga/` + mangaModel.id, requestParams);
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
const setMangaIdButton = (id) =>{
|
||||
setMangaId(id);
|
||||
}
|
||||
|
||||
const removeButton = (id) =>{
|
||||
remove(id).then(() => {
|
||||
getCreator(creator.id)
|
||||
.then(_data =>setCreator(_data));
|
||||
});
|
||||
}
|
||||
|
||||
const remove = async function (id){
|
||||
console.info('Try to remove item');
|
||||
if (id !== 0) {
|
||||
if (!confirm('Do you really want to remove this item?')) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
const requestParams = {
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": getTokenForHeader(),
|
||||
}
|
||||
};
|
||||
const response = await fetch(host + `/manga/` + id, requestParams);
|
||||
}
|
||||
|
||||
const createButton = (e) =>{
|
||||
e.preventDefault()
|
||||
create().then((result) => {
|
||||
alert(`Manga[id=${result.id}, mangaName=${result.mangaName}, chapterCount=${result.chapterCount}]`);
|
||||
getCreator(creator.id)
|
||||
.then(_data =>setCreator(_data));
|
||||
});
|
||||
}
|
||||
|
||||
const create = async function (){
|
||||
mangaModel.chapterCount = chapterCount;
|
||||
mangaModel.creatorId = creator.id;
|
||||
mangaModel.image = imageURL;
|
||||
mangaModel.mangaName = mangaName;
|
||||
console.log(mangaModel);
|
||||
const requestParams = {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": getTokenForHeader(),
|
||||
},
|
||||
body: JSON.stringify(mangaModel),
|
||||
};
|
||||
const response = await fetch(host + `/manga`, requestParams);
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
const [imageURL, setImageURL] = useState();
|
||||
const fileReader = new FileReader();
|
||||
|
||||
fileReader.onloadend = () => {
|
||||
setImageURL(fileReader.result);
|
||||
};
|
||||
|
||||
const handleOnChange = (event) => {
|
||||
event.preventDefault();
|
||||
if (event.target.files && event.target.files.length) {
|
||||
const file = event.target.files[0];
|
||||
fileReader.readAsDataURL(file);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<main>
|
||||
<div className="container" id="root-div">
|
||||
<div className="content">
|
||||
<h1>Creator</h1>
|
||||
<form id="form">
|
||||
<div className="d-flex mt-3">
|
||||
<div className="d-grid col-sm-2">
|
||||
<button type="button" className="btn btn-success" data-bs-toggle="modal" data-bs-target="#exampleModal2">Добавить</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<MangaCreatorList
|
||||
creator={creator}
|
||||
setMangaIdButton={setMangaIdButton}
|
||||
removeButton={removeButton}
|
||||
/>
|
||||
<EditMangaModal
|
||||
chapterCount={chapterCount}
|
||||
setChapterCount={setChapterCount}
|
||||
handleOnChange={handleOnChange}
|
||||
updateButton={updateButton}
|
||||
/>
|
||||
<AddMangaModal
|
||||
chapterCount={chapterCount}
|
||||
setChapterCount={setChapterCount}
|
||||
mangaName={mangaName}
|
||||
setMangaName={setMangaName}
|
||||
handleOnChange={handleOnChange}
|
||||
createButton={createButton}
|
||||
/>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
90
front/src/Main/LoginPage.jsx
Normal file
90
front/src/Main/LoginPage.jsx
Normal file
@@ -0,0 +1,90 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import { Link, useNavigate } from 'react-router-dom';
|
||||
import { useRef } from "react";
|
||||
|
||||
const hostURL = "http://localhost:8080";
|
||||
|
||||
const LoginPage = function () {
|
||||
|
||||
const loginInput = useRef();
|
||||
const passwordInput = useRef();
|
||||
const navigate = useNavigate();
|
||||
|
||||
useEffect(() => {
|
||||
}, []);
|
||||
|
||||
const login = async function (login, password) {
|
||||
const requestParams = {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({login: login, password: password}),
|
||||
};
|
||||
const response = await fetch(hostURL + "/jwt/login", requestParams);
|
||||
const result = await response.text();
|
||||
if (response.status === 200) {
|
||||
localStorage.setItem("token", result);
|
||||
localStorage.setItem("user", login);
|
||||
getRole(result);
|
||||
} else {
|
||||
localStorage.removeItem("token");
|
||||
localStorage.removeItem("user");
|
||||
localStorage.removeItem("role");
|
||||
}
|
||||
}
|
||||
|
||||
const getRole = async function (token) {
|
||||
const requestParams = {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
};
|
||||
const requestUrl = hostURL + `/who_am_i?token=${token}`;
|
||||
const response = await fetch(requestUrl, requestParams);
|
||||
const result = await response.text();
|
||||
localStorage.setItem("role", result);
|
||||
window.dispatchEvent(new Event("storage"));
|
||||
navigate("/reader");
|
||||
}
|
||||
|
||||
const loginFormOnSubmit = function (event) {
|
||||
event.preventDefault();
|
||||
login(loginInput.current.value, passwordInput.current.value);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
<form onSubmit={(event) => loginFormOnSubmit(event)}>
|
||||
<div className="mb-3">
|
||||
<p className="mb-1">Login</p>
|
||||
<input className="form-control"
|
||||
type="text" required autoFocus
|
||||
ref={loginInput} />
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<p className="mb-1">Password</p>
|
||||
<input className="form-control"
|
||||
type="password" required
|
||||
ref={passwordInput} />
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<button type="submit" className="btn btn-success">
|
||||
Sing in
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<p>
|
||||
<span>Not a member yet? </span>
|
||||
<Link to="/singup">Sing Up here</Link>
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default LoginPage;
|
||||
119
front/src/Main/MangaPage.jsx
Normal file
119
front/src/Main/MangaPage.jsx
Normal file
@@ -0,0 +1,119 @@
|
||||
import React, {useState, useEffect} from 'react';
|
||||
import MangaDto from '../Dto/Manga-Dto';
|
||||
import ReaderList from "../components/List/ReaderList";
|
||||
|
||||
export default function MangaPage() {
|
||||
|
||||
const [mangaModel, setMangaModel] = useState(new MangaDto({}));
|
||||
|
||||
const [readerData, setReaderData] = useState([]);
|
||||
|
||||
const host = "http://localhost:8080/api/1.0";
|
||||
|
||||
const getTokenForHeader = function () {
|
||||
return "Bearer " + localStorage.getItem("token");
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const quryString = window.location.search;
|
||||
const urlParams = new URLSearchParams(quryString);
|
||||
const id = urlParams.get('id');
|
||||
getCreator(id)
|
||||
.then(_data =>setMangaModel(_data));
|
||||
getReaderData(id)
|
||||
.then(_data =>setReaderData(_data));
|
||||
console.log(readerData);
|
||||
}, []);
|
||||
|
||||
const transformer = (mangaModel) => new MangaDto(mangaModel);
|
||||
const url = "manga/";
|
||||
|
||||
const getReaderData = async function (id) {
|
||||
const requestParams = {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": getTokenForHeader(),
|
||||
}
|
||||
};
|
||||
const response = await fetch(host + `/manga/` + id + `/readers`, requestParams);
|
||||
const _data = await response.json()
|
||||
console.log(_data);
|
||||
return _data;
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
console.log(mangaModel);
|
||||
console.log(readerData);
|
||||
}, [mangaModel]);
|
||||
|
||||
useEffect(() => {
|
||||
console.log(readerData);
|
||||
});
|
||||
|
||||
|
||||
const getCreator = async function (id) {
|
||||
const requestParams = {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": getTokenForHeader(),
|
||||
}
|
||||
};
|
||||
const response = await fetch(host + `/manga/` + id, requestParams);
|
||||
const _data = await response.json()
|
||||
return _data;
|
||||
}
|
||||
|
||||
|
||||
const addMangaButton = (e) =>{
|
||||
e.preventDefault()
|
||||
getReaderData(253)
|
||||
.then(_data =>setReaderData(_data));
|
||||
console.log(readerData);
|
||||
}
|
||||
|
||||
return (
|
||||
<main className="p-3">
|
||||
<div className="container d-flex" >
|
||||
<div className="d-flex flex-column">
|
||||
<img className="img_style01" style={{borderRadius: "3%"}}src={mangaModel.image} alt={mangaModel.mangaName}/>
|
||||
</div>
|
||||
<div className="container table text-white fs-4 ms-4">
|
||||
<div className="row text-white fw-bold fs-3">О манге</div>
|
||||
<div className="row">
|
||||
<div className="col-xs-6 col-sm-3">Год производства</div>
|
||||
<div className="col-xs-6 col-sm-3">1000</div>
|
||||
</div>
|
||||
<div className="row">
|
||||
<div className="col-xs-6 col-sm-3">Страна</div>
|
||||
<div className="col-xs-6 col-sm-3">Россия</div>
|
||||
</div>
|
||||
<div className="row">
|
||||
<div className="col-xs-6 col-sm-3">Жанр</div>
|
||||
<div className="col-xs-6 col-sm-3">Драма</div>
|
||||
</div>
|
||||
<div className="row">
|
||||
<div className="col-xs-6 col-sm-3">Количество глав</div>
|
||||
<div className="col-xs-6 col-sm-3">{mangaModel.chapterCount}</div>
|
||||
</div>
|
||||
<div className="row">
|
||||
<div className="col-xs-6 col-sm-3">Возраст</div>
|
||||
<div className="col-xs-6 col-sm-3">16+</div>
|
||||
</div>
|
||||
<div className="row text-white fw-bold fs-3">Описание</div>
|
||||
<div className="row">
|
||||
<div className="col-xs-6 col-sm-12">
|
||||
<p>Ким Кон Чжа спокойно живет в своей халупе, завидуя всем популярным охотникам. Однажды его желание быть лучше всех сбывается и он получает легендарный навык “Копирование способностей”... ценой своей жизни.</p>
|
||||
<p>Прежде чем он успевает понять это, его убивает охотник №1, Летний дух! Но это активирует его навык, и теперь он скопировал новый, “Путешествие во времени после смерти”.</p>
|
||||
<p>Как Ким Кон Чжа же будет использовать эти навыки, чтобы победить конкурентов и подняться на вершину?</p>
|
||||
</div>
|
||||
</div>
|
||||
<ReaderList
|
||||
readers={mangaModel.readers}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
195
front/src/Main/ReaderAction.jsx
Normal file
195
front/src/Main/ReaderAction.jsx
Normal file
@@ -0,0 +1,195 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import TableReader from '../components/Table/TableReader';
|
||||
import { NavLink } from 'react-router-dom';
|
||||
import MangaReaderList from "../components/List/MangaReaderList";
|
||||
import AddMangaReaderModal from "../components/Modal/AddMangaReaderModal";
|
||||
|
||||
export default function ReaderAction() {
|
||||
|
||||
const host = "http://localhost:8080/api/1.0";
|
||||
|
||||
const [mangaData, setMangaData] = useState([]);
|
||||
|
||||
const [readerData, setReaderData] = useState([]);
|
||||
|
||||
const [reader, setReader] = useState([]);
|
||||
|
||||
const [mangaId, setMangaId] = useState(0);
|
||||
|
||||
const [chapterCount, setChapterCount] = useState(0);
|
||||
|
||||
const [mangaName, setMangaName] = useState("");
|
||||
|
||||
const getTokenForHeader = function () {
|
||||
return "Bearer " + localStorage.getItem("token");
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const quryString = window.location.search;
|
||||
const urlParams = new URLSearchParams(quryString);
|
||||
const id = urlParams.get('id');
|
||||
console.log(id);
|
||||
if (id !== null) setReaderId(id);
|
||||
getReaderData()
|
||||
.then(_data =>setReaderData(_data));
|
||||
getMangaData()
|
||||
.then(_data =>setMangaData(_data));
|
||||
console.log(2);
|
||||
console.log(readerData);
|
||||
},[]);
|
||||
|
||||
const getReaderData = async function () {
|
||||
const requestParams = {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Authorization": getTokenForHeader(),
|
||||
}
|
||||
};
|
||||
const response = await fetch(host + "/reader", requestParams);
|
||||
const _data = await response.json()
|
||||
console.log(_data);
|
||||
return _data;
|
||||
}
|
||||
|
||||
const getMangaData = async function () {
|
||||
const requestParams = {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Authorization": getTokenForHeader(),
|
||||
}
|
||||
};
|
||||
const response = await fetch(host + "/manga", requestParams);
|
||||
const _data = await response.json()
|
||||
console.log(_data);
|
||||
return _data;
|
||||
}
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
getReader().then(_data => {
|
||||
setReader(_data);
|
||||
console.log(_data);
|
||||
});
|
||||
}, []);
|
||||
|
||||
const getReader = async function () {
|
||||
const requestParams = {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": getTokenForHeader(),
|
||||
}
|
||||
};
|
||||
let login = localStorage.getItem("user");
|
||||
console.log(host + `/reader/` + login);
|
||||
const response = await fetch(host + `/reader/` + login, requestParams);
|
||||
const _data = await response.json()
|
||||
return _data;
|
||||
}
|
||||
|
||||
const updateButton = (e) =>{
|
||||
e.preventDefault();
|
||||
update().then((result) => {
|
||||
alert(`Manga[id=${result.id}, mangaName=${result.mangaName}, chapterCount=${result.chapterCount}]`);
|
||||
getReader()
|
||||
.then(_data =>setReader(_data));
|
||||
});
|
||||
console.log(reader);
|
||||
}
|
||||
|
||||
const update = async function (){
|
||||
console.info('Try to update item');
|
||||
if (mangaId === 0) {
|
||||
return;
|
||||
}
|
||||
const requestParams = {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": getTokenForHeader(),
|
||||
}
|
||||
};
|
||||
const response = await fetch(host + `/manga/${mangaId}?chapterCount=${chapterCount}`, requestParams);
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
const setMangaIdButton = (id) =>{
|
||||
setMangaId(id);
|
||||
}
|
||||
|
||||
const removeButton = (id) =>{
|
||||
remove(id).then((result) => {
|
||||
getReader()
|
||||
.then(_data =>setReader(_data));
|
||||
});
|
||||
}
|
||||
|
||||
const remove = async function (id){
|
||||
console.info('Try to remove item');
|
||||
if (!confirm('Do you really want to remove this item?')) {
|
||||
console.info('Canceled');
|
||||
return;
|
||||
}
|
||||
const requestParams = {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": getTokenForHeader(),
|
||||
}
|
||||
};
|
||||
console.log(host + `/reader/${reader.id}/removeManga?mangaId=${id}`, requestParams);
|
||||
const response = await fetch(host + `/reader/${reader.id}/removeManga?mangaId=${id}`, requestParams);
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
|
||||
const addMangaButton = (e) =>{
|
||||
e.preventDefault()
|
||||
addManga().then((result) => {
|
||||
alert(`Manga[id=${result.id}, mangaName=${result.mangaName}, chapterCount=${result.chapterCount}]`);
|
||||
console.log(result);
|
||||
getReader()
|
||||
.then(_data =>setReader(_data));
|
||||
});
|
||||
}
|
||||
|
||||
const addManga = async function (){
|
||||
const requestParams = {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": getTokenForHeader(),
|
||||
}
|
||||
};
|
||||
console.log(host + `/reader/${reader.id}/addManga?mangaId=${mangaId}`, requestParams);
|
||||
const response = await fetch(host + `/reader/${reader.id}/addManga?mangaId=${mangaId}`, requestParams);
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
return (
|
||||
<main>
|
||||
<div className="container" id="root-div">
|
||||
<div className="content">
|
||||
<h1>Reader</h1>
|
||||
<form id="form">
|
||||
<div className="d-flex mt-3">
|
||||
<div className="d-grid col-sm-2">
|
||||
<button type="button" className="btn btn-success" data-bs-toggle="modal" data-bs-target="#exampleModal2">Добавить</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<MangaReaderList
|
||||
reader={reader}
|
||||
removeButton={removeButton}
|
||||
/>
|
||||
<AddMangaReaderModal
|
||||
mangaId={mangaId}
|
||||
setMangaId={setMangaId}
|
||||
mangaData={mangaData}
|
||||
addMangaButton={addMangaButton}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
88
front/src/Main/SingupPage.jsx
Normal file
88
front/src/Main/SingupPage.jsx
Normal file
@@ -0,0 +1,88 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import { Link } from 'react-router-dom';
|
||||
import { useRef } from "react";
|
||||
|
||||
const hostURL = "http://localhost:8080";
|
||||
|
||||
const SingupPage = function () {
|
||||
|
||||
const loginInput = useRef();
|
||||
const emailInput = useRef();
|
||||
const passwordInput = useRef();
|
||||
const passwordConfirmInput = useRef();
|
||||
|
||||
useEffect(() => {
|
||||
}, []);
|
||||
|
||||
const singup = async function (userSinginDto) {
|
||||
const requestParams = {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
body: JSON.stringify(userSinginDto),
|
||||
};
|
||||
console.log(hostURL + "/sing_up");
|
||||
console.log(userSinginDto);
|
||||
const response = await fetch(hostURL + "/sing_up", requestParams);
|
||||
const result = await response.text();
|
||||
alert(result);
|
||||
}
|
||||
|
||||
const singupFormOnSubmit = function (event) {
|
||||
event.preventDefault();
|
||||
const userSinginDto = {
|
||||
login: loginInput.current.value,
|
||||
email: emailInput.current.value,
|
||||
password: passwordInput.current.value,
|
||||
passwordConfirm: passwordConfirmInput.current.value
|
||||
}
|
||||
singup(userSinginDto);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
<form onSubmit={(event) => singupFormOnSubmit(event)}>
|
||||
<div className="mb-3">
|
||||
<p className="mb-1">Login</p>
|
||||
<input className="form-control"
|
||||
type="text" required maxLength="64"
|
||||
ref={loginInput} />
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<p className="mb-1">Email</p>
|
||||
<input className="form-control"
|
||||
type="text" required
|
||||
ref={emailInput} />
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<p className="mb-1">Password</p>
|
||||
<input className="form-control"
|
||||
type="password" required minLength="3" maxLength="64"
|
||||
ref={passwordInput} />
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<p className="mb-1">Confirm Password</p>
|
||||
<input className="form-control"
|
||||
type="password" required minLength="3" maxLength="64"
|
||||
ref={passwordConfirmInput} />
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<button type="submit" className="btn btn-success">
|
||||
Create account
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<p>
|
||||
<span>Already have an account? </span>
|
||||
<Link to="/login">Sing In here</Link>
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default SingupPage;
|
||||
112
front/src/Main/UsersPage.jsx
Normal file
112
front/src/Main/UsersPage.jsx
Normal file
@@ -0,0 +1,112 @@
|
||||
import { useState, useEffect } from "react";
|
||||
|
||||
const hostURL = "http://localhost:8080";
|
||||
const host = hostURL + "/api/1.0";
|
||||
|
||||
const UsersPage = function () {
|
||||
|
||||
const [users, setUsers] = useState([]);
|
||||
const [pageNumbers, setPageNumbers] = useState([]);
|
||||
const [pageNumber, setPageNumber] = useState();
|
||||
|
||||
useEffect(() => {
|
||||
getUsers(1);
|
||||
}, []);
|
||||
|
||||
const getTokenForHeader = function () {
|
||||
return "Bearer " + localStorage.getItem("token");
|
||||
}
|
||||
|
||||
const getUsers = async function (page) {
|
||||
const requestParams = {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Authorization": getTokenForHeader(),
|
||||
}
|
||||
};
|
||||
const requestUrl = host + `/users?page=${page}`;
|
||||
const response = await fetch(requestUrl, requestParams);
|
||||
const data = await response.json();
|
||||
setUsers(data.first.content);
|
||||
setPageNumber(data.first.number);
|
||||
setPageNumbers(data.second);
|
||||
}
|
||||
|
||||
const removeUser = async function (id) {
|
||||
const requestParams = {
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": getTokenForHeader(),
|
||||
}
|
||||
};
|
||||
const requestUrl = host + `/user/${id}`;
|
||||
await fetch(requestUrl, requestParams);
|
||||
}
|
||||
|
||||
const pageButtonOnClick = function (page) {
|
||||
getUsers(page);
|
||||
}
|
||||
|
||||
const removeButtonOnClick = function (id) {
|
||||
const confirmResult = confirm("Are you sure you want to remove " +
|
||||
"the selected user?");
|
||||
if (confirmResult === false) {
|
||||
return;
|
||||
}
|
||||
removeUser(id).then(() => getUsers(pageNumber + 1));
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="table-shell mb-3">
|
||||
<table className="table text-white">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style={{ width: "10%" }} scope="col">#</th>
|
||||
<th style={{ width: "15%" }} scope="col">ID</th>
|
||||
<th style={{ width: "30%" }} scope="col">Login</th>
|
||||
<th style={{ width: "30%" }} scope="col">Email</th>
|
||||
<th style={{ width: "15%" }} scope="col">Role</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{users.map((user, index) => (
|
||||
<tr>
|
||||
<th style={{ width: "10%" }} scope="row">{index}</th>
|
||||
<td style={{ width: "15%" }}>{user.id}</td>
|
||||
<td style={{ width: "30%" }}>{user.login}</td>
|
||||
<td style={{ width: "30%" }}>{user.email}</td>
|
||||
<td style={{ width: "15%" }}>{user.role}</td>
|
||||
{user.login !== localStorage.getItem("user") ?
|
||||
<td style={{ width: "1%" }}>
|
||||
<button className="btn btn-secondary btn-sm"
|
||||
onClick={() => removeButtonOnClick(user.id)}>
|
||||
del
|
||||
</button>
|
||||
</td> : null}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div>
|
||||
<p>
|
||||
Pages:
|
||||
</p>
|
||||
<nav>
|
||||
<ul className="pagination">
|
||||
{pageNumbers.map((number) => (
|
||||
<li className={`page-item ${number === pageNumber + 1 ? "active" : ""}`}
|
||||
onClick={() => pageButtonOnClick(number)}>
|
||||
<a className="page-link" href="#">{number}</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default UsersPage;
|
||||
174
front/src/MainS/Creator.jsx
Normal file
174
front/src/MainS/Creator.jsx
Normal file
@@ -0,0 +1,174 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import TableCreator from '../components/Table/TableCreator';
|
||||
import MangaDto from '../Dto/Manga-Dto';
|
||||
|
||||
export default function Creator() {
|
||||
|
||||
const host = "http://localhost:8080/api/1.0";
|
||||
|
||||
const [creatorId, setCreatorId] = useState(0);
|
||||
|
||||
const [creatorName, setCreatorName] = useState("");
|
||||
|
||||
const [password, setPassword] = useState("");
|
||||
|
||||
const [mangaModel, setMangaModel] = useState(new MangaDto({}));
|
||||
|
||||
const [data, setData] = useState([]);
|
||||
|
||||
const getTokenForHeader = function () {
|
||||
return "Bearer " + localStorage.getItem("token");
|
||||
}
|
||||
|
||||
const table = document.getElementById("tbody");
|
||||
|
||||
useEffect(() => {
|
||||
getData()
|
||||
},[]);
|
||||
|
||||
const getData = async function () {
|
||||
const requestParams = {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Authorization": getTokenForHeader(),
|
||||
}
|
||||
};
|
||||
const requestUrl = host + `/creator`;
|
||||
const response = await fetch(requestUrl, requestParams);
|
||||
setData(await response.json())
|
||||
console.log(data);
|
||||
}
|
||||
|
||||
const create = async function (){
|
||||
const requestParams = {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Authorization": getTokenForHeader(),
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
};
|
||||
console.log((host + `/creator?creatorName=${creatorName}&password=${password}`));
|
||||
const response = await fetch(host + `/creator?creatorName=${creatorName}&password=${password}`, requestParams);
|
||||
getData();
|
||||
}
|
||||
|
||||
const remove = async function (){
|
||||
console.info('Try to remove item');
|
||||
if (creatorId !== 0) {
|
||||
if (!confirm('Do you really want to remove this item?')) {
|
||||
console.info('Canceled');
|
||||
return;
|
||||
}
|
||||
}
|
||||
const requestParams = {
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Authorization": getTokenForHeader(),
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
};
|
||||
const response = await fetch(host + `/creator/` + creatorId, requestParams);
|
||||
console.log("REMOVE");
|
||||
getData();
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
const removeAll = async function (){
|
||||
console.info('Try to remove item');
|
||||
if (!confirm('Do you really want to remove this item?')) {
|
||||
console.info('Canceled');
|
||||
return;
|
||||
}
|
||||
const requestParams = {
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Authorization": getTokenForHeader(),
|
||||
}
|
||||
};
|
||||
await fetch(host + `/creator/`, requestParams);
|
||||
}
|
||||
|
||||
const update = async function (){
|
||||
console.info('Try to update item');
|
||||
if (creatorId === 0 || creatorName == null || password === 0) {
|
||||
return;
|
||||
}
|
||||
const requestParams = {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Authorization": getTokenForHeader(),
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
};
|
||||
const response = await fetch(host + `/creator/${creatorId}?creatorName=${creatorName}&password=${password}`, requestParams);
|
||||
getData();
|
||||
return await response.json();
|
||||
}
|
||||
const createButton = (e) =>{
|
||||
e.preventDefault()
|
||||
create();
|
||||
}
|
||||
|
||||
const removeButton = (e) =>{
|
||||
e.preventDefault()
|
||||
remove();
|
||||
}
|
||||
|
||||
const updateButton = (e) =>{
|
||||
e.preventDefault()
|
||||
update();
|
||||
}
|
||||
|
||||
return (
|
||||
<main>
|
||||
<div className="container" id="root-div">
|
||||
<div className="content">
|
||||
<h1>Creator</h1>
|
||||
<form id="form">
|
||||
<div className="d-flex justify-content-evenly mt-3">
|
||||
<div className="col-sm-2">
|
||||
<label htmlFor="creatorId" className="form-label">creatorId</label>
|
||||
<input type='number' value = {creatorId} onChange={event => setCreatorId(event.target.value)} className="form-control"/>
|
||||
</div>
|
||||
<div className="col-sm-2">
|
||||
<label htmlFor="creatorName" className="form-label">creatorName</label>
|
||||
<input type='text' value = {creatorName} onChange={event => setCreatorName(event.target.value)} className="form-control"/>
|
||||
</div>
|
||||
<div className="col-sm-2">
|
||||
<label htmlFor="password" className="form-label">password</label>
|
||||
<input type='text' value = {password} onChange={event => setPassword(event.target.value)} className="form-control"/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="row mt-3">
|
||||
<div className="d-grid col-sm-3 mx-auto">
|
||||
<button type="submit" onClick={createButton} className="btn btn-success">Добавить</button>
|
||||
</div>
|
||||
<div className="d-grid col-sm-3 mx-auto">
|
||||
<button type="submit" onClick={updateButton} className="btn btn-success" id="btnUpdate" >Обновить</button>
|
||||
</div>
|
||||
<div className="d-grid col-sm-3 mx-auto">
|
||||
<button id="btnRemove" onClick={removeButton} className="btn btn-success">Удалить</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<div className="row table-responsive text-white">
|
||||
|
||||
<table className="table mt-3 text-white">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">#</th>
|
||||
<th scope="col">CreatorName</th>
|
||||
<th scope="col">Password</th>
|
||||
<th scope="col">Mangs</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<TableCreator items = {data}/>
|
||||
{/* <tbody id="tbody">
|
||||
</tbody> */}
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
235
front/src/MainS/Reader.jsx
Normal file
235
front/src/MainS/Reader.jsx
Normal file
@@ -0,0 +1,235 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import TableReader from '../components/Table/TableReader';
|
||||
|
||||
export default function ReaderS() {
|
||||
|
||||
const host = "http://localhost:8080/api/1.0";
|
||||
|
||||
const [readerId, setReaderId] = useState(0);
|
||||
|
||||
const [mangaId, setMangaId] = useState(0);
|
||||
|
||||
const [readerName, setReaderName] = useState("");
|
||||
|
||||
const [password, setPassword] = useState("");
|
||||
|
||||
const [data, setData] = useState([]);
|
||||
|
||||
const getTokenForHeader = function () {
|
||||
return "Bearer " + localStorage.getItem("token");
|
||||
}
|
||||
|
||||
const table = document.getElementById("tbody");
|
||||
|
||||
useEffect(() => {
|
||||
getData();
|
||||
console.log(2);
|
||||
},[]);
|
||||
|
||||
const getData = async function () {
|
||||
const requestParams = {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Authorization": getTokenForHeader(),
|
||||
}
|
||||
};
|
||||
const requestUrl = host + `/reader`;
|
||||
const response = await fetch(requestUrl, requestParams);
|
||||
setData(await response.json())
|
||||
console.log(data);
|
||||
}
|
||||
|
||||
const create = async function (){
|
||||
const requestParams = {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": getTokenForHeader(),
|
||||
}
|
||||
};
|
||||
const response = await fetch(host + `/reader?readerName=${readerName}&password=${password}`, requestParams);
|
||||
alert(response);
|
||||
console.log(response);
|
||||
getData();
|
||||
}
|
||||
|
||||
const remove = async function (id){
|
||||
console.info('Try to remove item');
|
||||
if (id !== 0) {
|
||||
if (!confirm('Do you really want to remove this item?')) {
|
||||
console.info('Canceled');
|
||||
return;
|
||||
}
|
||||
}
|
||||
const requestParams = {
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": getTokenForHeader(),
|
||||
}
|
||||
};
|
||||
const response = await fetch(host + `/reader/` + id, requestParams);
|
||||
getData();
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
const removeAll = async function (){
|
||||
console.info('Try to remove item');
|
||||
if (!confirm('Do you really want to remove this item?')) {
|
||||
console.info('Canceled');
|
||||
return;
|
||||
}
|
||||
const requestParams = {
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Authorization": getTokenForHeader(),
|
||||
}
|
||||
};
|
||||
await fetch(host + `/reader/`, requestParams);
|
||||
getData();
|
||||
}
|
||||
|
||||
const update = async function (){
|
||||
console.info('Try to update item');
|
||||
if (readerId === 0 || readerName == null || password === 0) {
|
||||
return;
|
||||
}
|
||||
const requestParams = {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Authorization": getTokenForHeader(),
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
};
|
||||
const response = await fetch(host + `/reader/${readerId}?readerName=${readerName}&password=${password}`, requestParams);
|
||||
getData();
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
const removeManga = async function (){
|
||||
console.info('Try to remove item');
|
||||
if (!confirm('Do you really want to remove this item?')) {
|
||||
console.info('Canceled');
|
||||
return;
|
||||
}
|
||||
const requestParams = {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Authorization": getTokenForHeader(),
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
};
|
||||
console.log(host + `/reader/${readerId}/removeManga?mangaId=${mangaId}`, requestParams);
|
||||
const response = await fetch(host + `/reader/${readerId}/removeManga?mangaId=${mangaId}`, requestParams);
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
const addManga = async function () {
|
||||
const requestParams = {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Authorization": getTokenForHeader(),
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
};
|
||||
console.log(host + `/reader/${readerId}/addManga?mangaId=${mangaId}`, requestParams);
|
||||
const response = await fetch(host + `/reader/${readerId}/addManga?mangaId=${mangaId}`, requestParams);
|
||||
return await response.json();
|
||||
}
|
||||
const createButton = (e) =>{
|
||||
e.preventDefault()
|
||||
create();
|
||||
}
|
||||
|
||||
const removeButton = (id) =>{
|
||||
remove(id);
|
||||
|
||||
}
|
||||
|
||||
const updateButton = (e) =>{
|
||||
e.preventDefault()
|
||||
update();
|
||||
}
|
||||
|
||||
const removeMangaButton = (e) =>{
|
||||
e.preventDefault()
|
||||
removeManga();
|
||||
}
|
||||
|
||||
const addMangaButton = (e) =>{
|
||||
e.preventDefault()
|
||||
addManga();
|
||||
}
|
||||
|
||||
const setModal_Click = (e) =>{
|
||||
e.preventDefault()
|
||||
setModal(true)
|
||||
//setData({type:'', name:'', description:'' })
|
||||
}
|
||||
|
||||
return (
|
||||
<main>
|
||||
<div className="container" id="root-div">
|
||||
<div className="content">
|
||||
<h1>Readers</h1>
|
||||
<form id="form">
|
||||
<div className="d-flex justify-content-evenly mt-3">
|
||||
<div className="col-sm-2">
|
||||
<label htmlFor="readerId" className="form-label">readerId</label>
|
||||
<input type='number' value = {readerId} onChange={event => setReaderId(event.target.value)} className="form-control"/>
|
||||
</div>
|
||||
<div className="col-sm-2">
|
||||
<label htmlFor="mangaId" className="form-label">mangaId</label>
|
||||
<input type='number' value = {mangaId} onChange={event => setMangaId(event.target.value)} className="form-control"/>
|
||||
</div>
|
||||
<div className="col-sm-2">
|
||||
<label htmlFor="readerName" className="form-label">readerName</label>
|
||||
<input type='text' value = {readerName} onChange={event => setReaderName(event.target.value)} className="form-control"/>
|
||||
</div>
|
||||
<div className="col-sm-2">
|
||||
<label htmlFor="password" className="form-label">password</label>
|
||||
<input type='text' value = {password} onChange={event => setPassword(event.target.value)} className="form-control"/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="row m-3">
|
||||
<div className="d-grid col-sm-3 m-3 mx-auto">
|
||||
<button onClick={createButton} className="btn btn-success">Добавить</button>
|
||||
</div>
|
||||
<div className="d-grid col-sm-3 m-3 mx-auto">
|
||||
<button onClick={updateButton} className="btn btn-success">Обновить</button>
|
||||
</div>
|
||||
<div className="d-grid col-sm-2 m-3 mx-auto">
|
||||
<button onClick={removeMangaButton} className="btn btn-success">Удалить мангу</button>
|
||||
</div>
|
||||
<div className="d-grid col-sm-2 m-3 mx-auto">
|
||||
<button onClick={addMangaButton} className="btn btn-success">Добавить мангу</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<div className="row table-responsive text-white">
|
||||
|
||||
<table className="table mt-3 text-white">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">#</th>
|
||||
<th scope="col">readerName</th>
|
||||
<th scope="col">Password</th>
|
||||
<th scope="col">Mangs</th>
|
||||
<th scope="col"></th>
|
||||
<th scope="col"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<TableReader
|
||||
items = {data}
|
||||
remove ={removeButton}
|
||||
update ={updateButton}
|
||||
/>
|
||||
{/* <tbody id="tbody">
|
||||
</tbody> */}
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
43
front/src/components/Form/EditReaderForm.jsx
Normal file
43
front/src/components/Form/EditReaderForm.jsx
Normal file
@@ -0,0 +1,43 @@
|
||||
import React from 'react';
|
||||
|
||||
const EditReaderForm =(props) => {
|
||||
|
||||
const types = ([
|
||||
{name:'Манга'},
|
||||
{name:'Манхва'},
|
||||
{name:'Маньхуа'},
|
||||
])
|
||||
|
||||
function done(e) {
|
||||
e.preventDefault();
|
||||
// if (formRef.current.checkValidity()) {
|
||||
// props.onDone();
|
||||
// props.setModalEdit();
|
||||
// props.setData({type:'', name:'', description:'' })
|
||||
// }else {
|
||||
// formRef.current.reportValidity();
|
||||
// props.setData({type:'', name:'', description:'' })
|
||||
// }
|
||||
}
|
||||
|
||||
const formRef = React.createRef();
|
||||
|
||||
return (
|
||||
<form id="frm-items" ref={formRef} className="row g-3 text-dark">
|
||||
<div className="col-md-3 ">
|
||||
</div>
|
||||
<div className="col-sm-2">
|
||||
<label htmlFor="readerName" className="form-label">readerName</label>
|
||||
<input type='text' value = {props.readerName} onChange={event => props.setReaderName(event.target.value)} className="form-control"/>
|
||||
</div>
|
||||
<div className="col-sm-2">
|
||||
<label htmlFor="password" className="form-label">password</label>
|
||||
<input type='text' value = {props.password} onChange={event => props.setPassword(event.target.value)} className="form-control"/>
|
||||
</div>
|
||||
<div className="col-md-3">
|
||||
<button onClick={() => props.update(item.id)} id="btn-add-item" className="btn btn-primary" type="submit">Изменить</button>
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
export default EditReaderForm
|
||||
99
front/src/components/Header.jsx
Normal file
99
front/src/components/Header.jsx
Normal file
@@ -0,0 +1,99 @@
|
||||
import {NavLink, useNavigate} from 'react-router-dom';
|
||||
import {useEffect, useState} from "react";
|
||||
|
||||
export default function Header(props) {
|
||||
|
||||
const navigate = useNavigate();
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener("storage", () => {
|
||||
let token = localStorage.getItem("token");
|
||||
if (token) {
|
||||
getRole(token).then((role) => {
|
||||
if (localStorage.getItem("role") != role) {
|
||||
localStorage.removeItem("token");
|
||||
localStorage.removeItem("user");
|
||||
localStorage.removeItem("role");
|
||||
window.dispatchEvent(new Event("storage"));
|
||||
navigate("/catalog");
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}, [])
|
||||
|
||||
const getRole = async function (token) {
|
||||
const requestParams = {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
};
|
||||
const requestUrl = `http://localhost:8080/who_am_i?token=${token}`;
|
||||
const response = await fetch(requestUrl, requestParams);
|
||||
return await response.text();
|
||||
}
|
||||
|
||||
const logoutButtonOnClick = function () {
|
||||
localStorage.removeItem("token");
|
||||
localStorage.removeItem("user");
|
||||
localStorage.removeItem("role");
|
||||
window.dispatchEvent(new Event("storage"));
|
||||
navigate("/login");
|
||||
}
|
||||
|
||||
const [userRole, setUserRole] = useState("NONE");
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener("storage", () => {
|
||||
getUserRole();
|
||||
});
|
||||
getUserRole();
|
||||
}, [])
|
||||
|
||||
const getUserRole = function () {
|
||||
const role = localStorage.getItem("role") || "NONE";
|
||||
setUserRole(role);
|
||||
}
|
||||
|
||||
const validate = function (userGroup) {
|
||||
return (userGroup === "AUTH" && userRole !== "NONE") || (userGroup === userRole);
|
||||
}
|
||||
console.log(userRole);
|
||||
return (
|
||||
<nav className="navbar navbar-expand-lg navbar-dark">
|
||||
<div className="container-fluid">
|
||||
<button className="navbar-toggler" type="button"
|
||||
data-bs-toggle="collapse" data-bs-target="#navbarNav"
|
||||
aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span className="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div className="collapse navbar-collapse" id="navbarNav">
|
||||
<ul className="navbar-nav">
|
||||
{props.links.map(route =>{
|
||||
if (validate(route.userGroup)) {
|
||||
return (
|
||||
<li key={route.path}
|
||||
className="nav-item">
|
||||
<NavLink className="nav-link" to={route.path}>
|
||||
{route.label}
|
||||
</NavLink>
|
||||
</li>
|
||||
);}}
|
||||
)}
|
||||
{userRole === "NONE"?
|
||||
null
|
||||
:
|
||||
<div className="border-bottom pb-3 mb-3">
|
||||
<button className="btn btn-primary"
|
||||
onClick={logoutButtonOnClick}>
|
||||
Log Out
|
||||
</button>
|
||||
</div>
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav >
|
||||
);
|
||||
}
|
||||
36
front/src/components/List/MangaCreatorList.jsx
Normal file
36
front/src/components/List/MangaCreatorList.jsx
Normal file
@@ -0,0 +1,36 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { NavLink } from 'react-router-dom';
|
||||
import TableCreator from '../Table/TableCreator';
|
||||
import MangaDto from "../../Dto/Manga-Dto";
|
||||
|
||||
export default function MangaCreatorList(props) {
|
||||
|
||||
|
||||
return (
|
||||
<div className="row table-responsive text-white">
|
||||
{
|
||||
props.creator.mangas?.map((manga) =>
|
||||
<div key={manga.id} className="d-flex flex-row flex-wrap flex-grow-1 align-items-center mt-3">
|
||||
<div className="me-3">
|
||||
<NavLink to={`/mangapage?id=${manga.id}`} ><img src={manga.image} alt={manga.mangaName} className="slideshow" /></NavLink>
|
||||
</div>
|
||||
<div>
|
||||
<div className="pt-3 description d-flex flex-column justify-content-start mb-3 fs-6 fw-bold">
|
||||
<NavLink className="text-white fs-5 unic_class fw-bold pt-3 mb-3"
|
||||
to={`/mangapage?id=${manga.id}`}>Название: {manga.mangaName}
|
||||
</NavLink>
|
||||
<p>Глав: {manga.chapterCount}</p>
|
||||
</div>
|
||||
<div>
|
||||
<button type="button" className="btn btn-primary" onClick = {() => props.setMangaIdButton(manga.id)} data-bs-toggle="modal" data-bs-target="#exampleModal">
|
||||
<i className="fas fa-edit"></i>
|
||||
</button>
|
||||
<button className="delete bg-danger p-2 px-2 mx-2 border border-0 rounded text-white fw-bold" onClick = {() => props.removeButton(manga.id)} type="button">Удалить</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
33
front/src/components/List/MangaReaderList.jsx
Normal file
33
front/src/components/List/MangaReaderList.jsx
Normal file
@@ -0,0 +1,33 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { NavLink } from 'react-router-dom';
|
||||
import TableCreator from '../Table/TableCreator';
|
||||
import MangaDto from "../../Dto/Manga-Dto";
|
||||
|
||||
export default function MangaReaderList(props) {
|
||||
|
||||
|
||||
return (
|
||||
<div className="row table-responsive text-white">
|
||||
{
|
||||
props.reader.mangas?.map((manga) =>
|
||||
<div key={manga.id} className="d-flex flex-row flex-wrap flex-grow-1 align-items-center mt-3">
|
||||
<div className="me-3">
|
||||
<NavLink to={`/mangapage?id=${manga.id}`} ><img src={manga.image} alt={manga.mangaName} className="slideshow" /></NavLink>
|
||||
</div>
|
||||
<div>
|
||||
<div className="pt-3 description d-flex flex-column justify-content-start mb-3 fs-6 fw-bold">
|
||||
<NavLink className="text-white fs-5 unic_class fw-bold pt-3 mb-3"
|
||||
to={`/mangapage?id=${manga.id}`}>Название: {manga.mangaName}
|
||||
</NavLink>
|
||||
<p>Глав: {manga.chapterCount}</p>
|
||||
</div>
|
||||
<div>
|
||||
<button className="delete bg-danger p-2 px-2 mx-2 border border-0 rounded text-white fw-bold" onClick = {() => props.removeButton(manga.id)} type="button">Удалить</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
22
front/src/components/List/ReaderList.jsx
Normal file
22
front/src/components/List/ReaderList.jsx
Normal file
@@ -0,0 +1,22 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { NavLink } from 'react-router-dom';
|
||||
|
||||
export default function ReaderList(props) {
|
||||
|
||||
|
||||
return (
|
||||
<div className="row table-responsive text-white">
|
||||
{
|
||||
props.readers?.map((reader) =>
|
||||
<div key={reader.id} className="d-flex flex-row flex-wrap flex-grow-1 align-items-center mt-3">
|
||||
<div>
|
||||
<div className="text-white pt-3 description d-flex flex-column justify-content-start mb-3 fs-6 fw-bold">
|
||||
Имя пользователя: {reader.readerName}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
49
front/src/components/Modal/AddMangaModal.jsx
Normal file
49
front/src/components/Modal/AddMangaModal.jsx
Normal file
@@ -0,0 +1,49 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { NavLink } from 'react-router-dom';
|
||||
import TableCreator from '../Table/TableCreator';
|
||||
import MangaDto from "../../Dto/Manga-Dto";
|
||||
|
||||
export default function AddMangaModal(props) {
|
||||
|
||||
|
||||
return (
|
||||
<div className="modal fade text-black" id="exampleModal2" tabIndex="-1" aria-labelledby="exampleModalLabel2" aria-hidden="true">
|
||||
<div className="modal-dialog">
|
||||
<div className="modal-content">
|
||||
<div className="modal-header">
|
||||
<h5 className="modal-title" id="exampleModalLabel2">Создание манги</h5>
|
||||
<button type="button" className="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div className="modal-body">
|
||||
<div>
|
||||
<label htmlFor="chapterCount" className="form-label">chapterCount</label>
|
||||
<input type='number' value = {props.chapterCount} onChange={event => props.setChapterCount(event.target.value)} className="form-control"/>
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="mangaName" className="form-label">mangaName</label>
|
||||
<input type='text' value = {props.mangaName} onChange={event => props.setMangaName(event.target.value)} className="form-control"/>
|
||||
</div>
|
||||
<div>
|
||||
<label
|
||||
htmlFor="file-loader-button"
|
||||
className="form-label">
|
||||
Загрузить файл
|
||||
</label>
|
||||
<input
|
||||
id="file-loader-button"
|
||||
type="file"
|
||||
className="form-control text-white"
|
||||
onChange={props.handleOnChange}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="modal-footer">
|
||||
<button type="button" className="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
||||
<button type="button" onClick={props.createButton} className="btn btn-primary" data-bs-dismiss="modal">Save changes</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
37
front/src/components/Modal/AddMangaReaderModal.jsx
Normal file
37
front/src/components/Modal/AddMangaReaderModal.jsx
Normal file
@@ -0,0 +1,37 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { NavLink } from 'react-router-dom';
|
||||
import TableCreator from '../Table/TableCreator';
|
||||
import MangaDto from "../../Dto/Manga-Dto";
|
||||
|
||||
export default function AddMangaReaderModal(props) {
|
||||
|
||||
|
||||
return (
|
||||
<div className="modal fade text-black" id="exampleModal2" tabIndex="-1" aria-labelledby="exampleModalLabel2" aria-hidden="true">
|
||||
<div className="modal-dialog">
|
||||
<div className="modal-content">
|
||||
<div className="modal-header">
|
||||
<h5 className="modal-title" id="exampleModalLabel2">Добавление манги к читателю</h5>
|
||||
<button type="button" className="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div className="modal-body">
|
||||
<div>
|
||||
<select className="form-select" value={props.mangaId} onChange={event => props.setMangaId(event.target.value)} aria-label="Default select example">
|
||||
<option value={0}>Manga</option>
|
||||
{
|
||||
props.mangaData.map((mangaD) =>
|
||||
<option key={mangaD.id} value={mangaD.id}>{mangaD.mangaName}</option>
|
||||
)
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div className="modal-footer">
|
||||
<button type="button" className="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
||||
<button type="button" onClick={props.addMangaButton} className="btn btn-primary" data-bs-dismiss="modal">Save changes</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
44
front/src/components/Modal/EditMangaModal.jsx
Normal file
44
front/src/components/Modal/EditMangaModal.jsx
Normal file
@@ -0,0 +1,44 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { NavLink } from 'react-router-dom';
|
||||
import TableCreator from '../Table/TableCreator';
|
||||
import MangaDto from "../../Dto/Manga-Dto";
|
||||
|
||||
export default function EditMangaModal(props) {
|
||||
|
||||
return (
|
||||
<div className="modal fade text-black" id="exampleModal" tabIndex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
|
||||
<div className="modal-dialog">
|
||||
<div className="modal-content">
|
||||
<div className="modal-header">
|
||||
<h5 className="modal-title" id="exampleModalLabel">Изменение манги</h5>
|
||||
<button type="button" className="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div className="modal-body">
|
||||
<div>
|
||||
<label htmlFor="chapterCount" className="form-label">chapterCount</label>
|
||||
<input type='number' value={props.chapterCount} onChange={event => props.setChapterCount(event.target.value)} className="form-control"/>
|
||||
</div>
|
||||
<div>
|
||||
<label
|
||||
htmlFor="file-loader-button"
|
||||
className="form-label">
|
||||
Загрузить файл
|
||||
</label>
|
||||
<input
|
||||
id="file-loader-button"
|
||||
type="file"
|
||||
className="form-control text-white"
|
||||
onChange={props.handleOnChange}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="modal-footer">
|
||||
<button type="button" className="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
||||
<button type="button" onClick={props.updateButton} className="btn btn-primary" data-bs-dismiss="modal">Save changes</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
17
front/src/components/PrivateRoutes.jsx
Normal file
17
front/src/components/PrivateRoutes.jsx
Normal file
@@ -0,0 +1,17 @@
|
||||
import {Navigate, Outlet, useNavigate} from 'react-router-dom';
|
||||
import {useEffect} from "react";
|
||||
|
||||
const PrivateRoutes = (props) => {
|
||||
|
||||
|
||||
|
||||
let isAllowed = false;
|
||||
let userRole = localStorage.getItem("role");
|
||||
if ((props.userGroup === "AUTH" && userRole) || (props.userGroup === userRole)) {
|
||||
isAllowed = true;
|
||||
}
|
||||
|
||||
return isAllowed ? <Outlet /> : <Navigate to="/login" />;
|
||||
}
|
||||
|
||||
export default PrivateRoutes;
|
||||
26
front/src/components/Table/TableCreator.jsx
Normal file
26
front/src/components/Table/TableCreator.jsx
Normal file
@@ -0,0 +1,26 @@
|
||||
import { useState } from 'react';
|
||||
|
||||
|
||||
export default function TableCreator(props) {
|
||||
|
||||
return (
|
||||
<tbody>
|
||||
{
|
||||
props.items?.map((item, index) =>
|
||||
<tr key={item.id}>
|
||||
<td>{item.id}</td>
|
||||
<td>{item.creatorName}</td>
|
||||
<td>{item.hashedPassword}</td>
|
||||
<td>
|
||||
<select className="form-select" aria-label="Default select example">{item.mangas?.map(manga =>
|
||||
<option key={manga.mangaName } >{manga.mangaName + " " +manga.chapterCount}</option>)}
|
||||
</select>
|
||||
</td>
|
||||
|
||||
|
||||
</tr>
|
||||
)
|
||||
}
|
||||
</tbody >
|
||||
);
|
||||
}
|
||||
27
front/src/components/Table/TableReader.jsx
Normal file
27
front/src/components/Table/TableReader.jsx
Normal file
@@ -0,0 +1,27 @@
|
||||
import { useState } from 'react';
|
||||
|
||||
|
||||
export default function TableReader(props) {
|
||||
|
||||
return (
|
||||
<tbody>
|
||||
{
|
||||
props.items?.map((item, index) =>
|
||||
<tr key={item.id}>
|
||||
<td>{item.id}</td>
|
||||
<td>{item.readerName}</td>
|
||||
<td>{item.hashedPassword}</td>
|
||||
<td>
|
||||
<select className="form-select" aria-label="Default select example">{item.mangas?.map(manga =>
|
||||
<option key={manga.id}>{manga.mangaName}</option>)}
|
||||
</select>
|
||||
</td>
|
||||
<td>
|
||||
<button onClick={() => props.remove(item.id)} className="btn btn-success">Удалить</button>
|
||||
</td>
|
||||
</tr>
|
||||
)
|
||||
}
|
||||
</tbody >
|
||||
);
|
||||
}
|
||||
8
front/src/main.jsx
Normal file
8
front/src/main.jsx
Normal file
@@ -0,0 +1,8 @@
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom/client'
|
||||
import App from './App'
|
||||
import './style.css'
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('app')).render(
|
||||
<App />
|
||||
)
|
||||
107
front/src/style.css
Normal file
107
front/src/style.css
Normal file
@@ -0,0 +1,107 @@
|
||||
html,
|
||||
body {
|
||||
background-color: #000000;
|
||||
color: #ffffff;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
font-family: sans-serif;
|
||||
line-height: 1.15;
|
||||
height: 100%;
|
||||
}
|
||||
header {
|
||||
background-color: #3c3c3c;
|
||||
color: #ffffff;
|
||||
}
|
||||
header a {
|
||||
color: #ffffff;
|
||||
text-decoration: none;
|
||||
margin: 0 0.5em;
|
||||
}
|
||||
header a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
#logo {
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
article a {
|
||||
color: #ffffff;
|
||||
text-decoration: none;
|
||||
margin: 0.5em 0.5em;
|
||||
}
|
||||
header a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.25em;
|
||||
}
|
||||
h3 {
|
||||
font-size: 1.1em;
|
||||
}
|
||||
footer {
|
||||
background-color: #9c9c9c;
|
||||
color: #ffffff;
|
||||
height: 32px;
|
||||
padding: 0.5em;
|
||||
}
|
||||
.manga_pages{
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
.catalog_wrapper{
|
||||
display: flex;
|
||||
width: 73%;
|
||||
flex-direction: column;
|
||||
}
|
||||
.catalog_article{
|
||||
display: flex;
|
||||
}
|
||||
.poster{
|
||||
width:140px;
|
||||
}
|
||||
th {
|
||||
border: 0px solid rgb(255, 255, 255);
|
||||
}
|
||||
.added_manga{
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
@media (min-width: 992px) {
|
||||
.manga_pages img{
|
||||
max-width: 900px;
|
||||
width: auto;
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
.flex_grow {
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
@media (min-width: 1024px){
|
||||
.article_1 {
|
||||
max-width: -webkit-calc(1600px + (100vw/64*7)*2);
|
||||
max-width: -moz-calc(1600px + (100vw/64*7)*2);
|
||||
max-width: calc(1600px + (100vw/64*7)*2);
|
||||
padding-left: -webkit-calc(100vw/64*7);
|
||||
padding-left: -moz-calc(100vw/64*7);
|
||||
padding-left: calc(100vw/64*7);
|
||||
padding-right: -webkit-calc(100vw/64*7);
|
||||
padding-right: -moz-calc(100vw/64*7);
|
||||
padding-right: calc(100vw/64*7);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.registration_div {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.slideshow{
|
||||
height: 250px;
|
||||
width: auto;/*maintain aspect ratio*/
|
||||
max-width:180px;
|
||||
border-radius: 7%;
|
||||
}
|
||||
42
frontend/.gitignore
vendored
42
frontend/.gitignore
vendored
@@ -1,42 +0,0 @@
|
||||
# See http://help.github.com/ignore-files/ for more about ignoring files.
|
||||
|
||||
# Compiled output
|
||||
/dist
|
||||
/tmp
|
||||
/out-tsc
|
||||
/bazel-out
|
||||
|
||||
# Node
|
||||
/node_modules
|
||||
npm-debug.log
|
||||
yarn-error.log
|
||||
|
||||
# IDEs and editors
|
||||
.idea/
|
||||
.project
|
||||
.classpath
|
||||
.c9/
|
||||
*.launch
|
||||
.settings/
|
||||
*.sublime-workspace
|
||||
|
||||
# Visual Studio Code
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
.history/*
|
||||
|
||||
# Miscellaneous
|
||||
/.angular/cache
|
||||
.sass-cache/
|
||||
/connect.lock
|
||||
/coverage
|
||||
/libpeerconnection.log
|
||||
testem.log
|
||||
/typings
|
||||
|
||||
# System files
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
@@ -1,49 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<link rel="stylesheet" href="style.css" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<script src="node_modules/bootstrap/dist/js/bootstrap.min.js"></script>
|
||||
<link href="node_modules/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet" />
|
||||
<link href="node_modules/@fortawesome/fontawesome-free/css/all.min.css" rel="stylesheet" />
|
||||
<title>Main</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root-div">
|
||||
<header>
|
||||
Calculator
|
||||
</header>
|
||||
<div class="content-div">
|
||||
<form id="calculator-form">
|
||||
<label for="first" class="form-label">num1</label>
|
||||
<input type="number" class="form-control" value="0" id="first" />
|
||||
<label for="second" class="form-label">num2</label>
|
||||
<input type="number" class="form-control" value="0" id="second" />
|
||||
<select class="form-select" id="type">
|
||||
<option value="int">Integer</option>
|
||||
<option value="string">String</option>
|
||||
</select>
|
||||
<label for="operation" class="form-label">Operation</label>
|
||||
<select class="form-select" id="operation">
|
||||
<option value="sum">+</option>
|
||||
<option value="difference">-</option>
|
||||
<option value="multiplication">*</option>
|
||||
<option value="cont">/</option>
|
||||
</select>
|
||||
<button type="button" class="btn btn-primary" id="calculate-button">Calculate</button>
|
||||
<button type="button" class="btn btn-primary" id="binary-button">Binary</button>
|
||||
<div>
|
||||
<label for="result" class="form-label">Result</label>
|
||||
<input type="text" readonly class="form-control" id="result" />
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<footer>
|
||||
Footer
|
||||
</footer>
|
||||
</div>
|
||||
<script src="script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
472
frontend/package-lock.json
generated
472
frontend/package-lock.json
generated
@@ -1,472 +0,0 @@
|
||||
{
|
||||
"name": "int-prog",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "int-prog",
|
||||
"version": "1.0.0",
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-free": "6.2.0",
|
||||
"bootstrap": "5.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"http-server": "14.1.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@fortawesome/fontawesome-free": {
|
||||
"version": "6.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.2.0.tgz",
|
||||
"integrity": "sha512-CNR7qRIfCwWHNN7FnKUniva94edPdyQzil/zCwk3v6k4R6rR2Fr8i4s3PM7n/lyfPA6Zfko9z5WDzFxG9SW1uQ==",
|
||||
"hasInstallScript": true,
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/ansi-styles": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"color-convert": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/async": {
|
||||
"version": "2.6.4",
|
||||
"resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz",
|
||||
"integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"lodash": "^4.17.14"
|
||||
}
|
||||
},
|
||||
"node_modules/basic-auth": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz",
|
||||
"integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"safe-buffer": "5.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/bootstrap": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.2.1.tgz",
|
||||
"integrity": "sha512-UQi3v2NpVPEi1n35dmRRzBJFlgvWHYwyem6yHhuT6afYF+sziEt46McRbT//kVXZ7b1YUYEVGdXEH74Nx3xzGA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/twbs"
|
||||
},
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/bootstrap"
|
||||
}
|
||||
],
|
||||
"peerDependencies": {
|
||||
"@popperjs/core": "^2.11.6"
|
||||
}
|
||||
},
|
||||
"node_modules/call-bind": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
|
||||
"integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"function-bind": "^1.1.1",
|
||||
"get-intrinsic": "^1.0.2"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/chalk": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
|
||||
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"ansi-styles": "^4.1.0",
|
||||
"supports-color": "^7.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/chalk?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"color-name": "~1.1.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/corser": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/corser/-/corser-2.0.1.tgz",
|
||||
"integrity": "sha512-utCYNzRSQIZNPIcGZdQc92UVJYAhtGAteCFg0yRaFm8f0P+CPtyGyHXJcGXnffjCybUCEx3FQ2G7U3/o9eIkVQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "3.2.7",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
|
||||
"integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"ms": "^2.1.1"
|
||||
}
|
||||
},
|
||||
"node_modules/eventemitter3": {
|
||||
"version": "4.0.7",
|
||||
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
|
||||
"integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/follow-redirects": {
|
||||
"version": "1.15.2",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
|
||||
"integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
"url": "https://github.com/sponsors/RubenVerborgh"
|
||||
}
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=4.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"debug": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/function-bind": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
|
||||
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/get-intrinsic": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz",
|
||||
"integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"function-bind": "^1.1.1",
|
||||
"has": "^1.0.3",
|
||||
"has-symbols": "^1.0.3"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/has": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
|
||||
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"function-bind": "^1.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/has-flag": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/has-symbols": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
|
||||
"integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/he": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
|
||||
"integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"he": "bin/he"
|
||||
}
|
||||
},
|
||||
"node_modules/html-encoding-sniffer": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz",
|
||||
"integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"whatwg-encoding": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/http-proxy": {
|
||||
"version": "1.18.1",
|
||||
"resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz",
|
||||
"integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"eventemitter3": "^4.0.0",
|
||||
"follow-redirects": "^1.0.0",
|
||||
"requires-port": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/http-server": {
|
||||
"version": "14.1.1",
|
||||
"resolved": "https://registry.npmjs.org/http-server/-/http-server-14.1.1.tgz",
|
||||
"integrity": "sha512-+cbxadF40UXd9T01zUHgA+rlo2Bg1Srer4+B4NwIHdaGxAGGv59nYRnGGDJ9LBk7alpS0US+J+bLLdQOOkJq4A==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"basic-auth": "^2.0.1",
|
||||
"chalk": "^4.1.2",
|
||||
"corser": "^2.0.1",
|
||||
"he": "^1.2.0",
|
||||
"html-encoding-sniffer": "^3.0.0",
|
||||
"http-proxy": "^1.18.1",
|
||||
"mime": "^1.6.0",
|
||||
"minimist": "^1.2.6",
|
||||
"opener": "^1.5.1",
|
||||
"portfinder": "^1.0.28",
|
||||
"secure-compare": "3.0.1",
|
||||
"union": "~0.5.0",
|
||||
"url-join": "^4.0.1"
|
||||
},
|
||||
"bin": {
|
||||
"http-server": "bin/http-server"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/iconv-lite": {
|
||||
"version": "0.6.3",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
||||
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/lodash": {
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/mime": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
|
||||
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"mime": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/minimist": {
|
||||
"version": "1.2.8",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
|
||||
"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
|
||||
"dev": true,
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/mkdirp": {
|
||||
"version": "0.5.6",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
|
||||
"integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"minimist": "^1.2.6"
|
||||
},
|
||||
"bin": {
|
||||
"mkdirp": "bin/cmd.js"
|
||||
}
|
||||
},
|
||||
"node_modules/ms": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/object-inspect": {
|
||||
"version": "1.12.3",
|
||||
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz",
|
||||
"integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==",
|
||||
"dev": true,
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/opener": {
|
||||
"version": "1.5.2",
|
||||
"resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz",
|
||||
"integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"opener": "bin/opener-bin.js"
|
||||
}
|
||||
},
|
||||
"node_modules/portfinder": {
|
||||
"version": "1.0.32",
|
||||
"resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.32.tgz",
|
||||
"integrity": "sha512-on2ZJVVDXRADWE6jnQaX0ioEylzgBpQk8r55NE4wjXW1ZxO+BgDlY6DXwj20i0V8eB4SenDQ00WEaxfiIQPcxg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"async": "^2.6.4",
|
||||
"debug": "^3.2.7",
|
||||
"mkdirp": "^0.5.6"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/qs": {
|
||||
"version": "6.11.0",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
|
||||
"integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"side-channel": "^1.0.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.6"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/requires-port": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
|
||||
"integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/secure-compare": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/secure-compare/-/secure-compare-3.0.1.tgz",
|
||||
"integrity": "sha512-AckIIV90rPDcBcglUwXPF3kg0P0qmPsPXAj6BBEENQE1p5yA1xfmDJzfi1Tappj37Pv2mVbKpL3Z1T+Nn7k1Qw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/side-channel": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
|
||||
"integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"call-bind": "^1.0.0",
|
||||
"get-intrinsic": "^1.0.2",
|
||||
"object-inspect": "^1.9.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/supports-color": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
||||
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"has-flag": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/union": {
|
||||
"version": "0.5.0",
|
||||
"resolved": "https://registry.npmjs.org/union/-/union-0.5.0.tgz",
|
||||
"integrity": "sha512-N6uOhuW6zO95P3Mel2I2zMsbsanvvtgn6jVqJv4vbVcz/JN0OkL9suomjQGmWtxJQXOCqUJvquc1sMeNz/IwlA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"qs": "^6.4.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/url-join": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz",
|
||||
"integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/whatwg-encoding": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz",
|
||||
"integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"iconv-lite": "0.6.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
{
|
||||
"name": "int-prog",
|
||||
"version": "1.0.0",
|
||||
"main": "index.html",
|
||||
"scripts": {
|
||||
"start": "http-server -p 3000 ./",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"dependencies": {
|
||||
"bootstrap": "5.2.1",
|
||||
"@fortawesome/fontawesome-free": "6.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"http-server": "14.1.1"
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
let calculateButton = document.getElementById("calculate-button");
|
||||
let binaryButton = document.getElementById("binary-button");
|
||||
let numberOneInput = document.getElementById("first");
|
||||
let numberTwoInput = document.getElementById("second");
|
||||
let operationType = document.getElementById("type");
|
||||
let select = document.getElementById("operation");
|
||||
let resultInput = document.getElementById("result");
|
||||
|
||||
calculateButton.onclick = function() {
|
||||
executeRequest(select.value);
|
||||
};
|
||||
|
||||
binaryButton.onclick = function() {
|
||||
let int_result = parseInt(resultInput.value)
|
||||
executeBinaryRequest(int_result);
|
||||
};
|
||||
|
||||
function executeRequest(address) {
|
||||
let first = numberOneInput.value;
|
||||
let second = numberTwoInput.value;
|
||||
let type = operationType.value;
|
||||
fetch(`http://localhost:8080/${address}?first=${first}&second=${second}&type=${type}`)
|
||||
.then(response => response.text())
|
||||
.then(res => resultInput.value = String(res) );
|
||||
}
|
||||
|
||||
function executeBinaryRequest(result) {
|
||||
fetch(`http://localhost:8080/binary?result=${result}`)
|
||||
.then(response => response.text())
|
||||
.then(res => resultInput.value = res);
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
html {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
*, *:before, *:after {
|
||||
box-sizing: inherit;
|
||||
}
|
||||
|
||||
body {
|
||||
height: 100vh;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#root-div {
|
||||
height: 100vh;
|
||||
max-width: 1000px;
|
||||
margin: 0 auto 0 auto;
|
||||
|
||||
display: grid;
|
||||
grid-template-rows: auto 1fr auto;
|
||||
}
|
||||
|
||||
#calculate-button {
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
header {
|
||||
padding: 1em;
|
||||
background-color: gray;
|
||||
color: white;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
footer {
|
||||
padding: 1em;
|
||||
background-color: gray;
|
||||
color: white;
|
||||
font-weight: 700;
|
||||
}
|
||||
@@ -1 +1,2 @@
|
||||
rootProject.name = 'app'
|
||||
include 'front'
|
||||
|
||||
@@ -3,50 +3,9 @@ package com.LabWork.app;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
|
||||
@SpringBootApplication
|
||||
public class AppApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(AppApplication.class, args);
|
||||
}
|
||||
|
||||
/* @GetMapping("/hello")
|
||||
public String hello(@RequestParam(value = "name", defaultValue = "World") String name) {
|
||||
return String.format("Hello %s!", name);
|
||||
}
|
||||
|
||||
@GetMapping("/sum")
|
||||
public String Sum(@RequestParam(required = false, defaultValue = "0") double first,
|
||||
@RequestParam(required = false, defaultValue = "0") double second) {
|
||||
return Double.toString(first + second);
|
||||
}
|
||||
|
||||
@GetMapping("/difference")
|
||||
public String Difference(@RequestParam(required = false, defaultValue = "0") double first,
|
||||
@RequestParam(required = false, defaultValue = "0") double second) {
|
||||
return Double.toString(first - second);
|
||||
}
|
||||
|
||||
@GetMapping("/multiplication")
|
||||
public String Multiplication(@RequestParam(required = false, defaultValue = "1") double first,
|
||||
@RequestParam(required = false, defaultValue = "1") double second) {
|
||||
return Double.toString(first * second);
|
||||
}
|
||||
|
||||
@GetMapping("/division")
|
||||
public String Division(@RequestParam(required = false, defaultValue = "1") double first,
|
||||
@RequestParam(required = false, defaultValue = "1") double second) {
|
||||
if(second == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return Double.toString(first/second);
|
||||
}
|
||||
|
||||
@GetMapping("/binary")
|
||||
public String Binary(@RequestParam int result) {
|
||||
return Integer.toBinaryString(result);
|
||||
}*/
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.LabWork.app.MangaStore.configuration;
|
||||
|
||||
import com.LabWork.app.MangaStore.configuration.jwt.JwtFilter;
|
||||
import io.swagger.v3.oas.models.Components;
|
||||
import io.swagger.v3.oas.models.OpenAPI;
|
||||
import io.swagger.v3.oas.models.security.SecurityRequirement;
|
||||
import io.swagger.v3.oas.models.security.SecurityScheme;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
public class OpenAPI30Configuration {
|
||||
public static final String API_PREFIX = "/api/1.0";
|
||||
|
||||
@Bean
|
||||
public OpenAPI customizeOpenAPI() {
|
||||
final String securitySchemeName = JwtFilter.TOKEN_BEGIN_STR;
|
||||
return new OpenAPI()
|
||||
.addSecurityItem(new SecurityRequirement()
|
||||
.addList(securitySchemeName))
|
||||
.components(new Components()
|
||||
.addSecuritySchemes(securitySchemeName, new SecurityScheme()
|
||||
.name(securitySchemeName)
|
||||
.type(SecurityScheme.Type.HTTP)
|
||||
.scheme("bearer")
|
||||
.bearerFormat("JWT")));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.LabWork.app.MangaStore.configuration;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
|
||||
@Configuration
|
||||
public class PasswordEncoderConfiguration {
|
||||
@Bean
|
||||
public PasswordEncoder passwordEncoder() {
|
||||
return new BCryptPasswordEncoder();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package com.LabWork.app.MangaStore.configuration;
|
||||
|
||||
import com.LabWork.app.MangaStore.configuration.jwt.JwtFilter;
|
||||
import com.LabWork.app.MangaStore.controller.UserController;
|
||||
import com.LabWork.app.MangaStore.model.Default.UserRole;
|
||||
import com.LabWork.app.MangaStore.service.UserService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
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.EnableMethodSecurity;
|
||||
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
|
||||
@EnableMethodSecurity(
|
||||
securedEnabled = true
|
||||
)
|
||||
public class SecurityConfiguration {
|
||||
private final Logger log = LoggerFactory.getLogger(SecurityConfiguration.class);
|
||||
public static final String SPA_URL_MASK = "/{path:[^\\.]*}";
|
||||
private final UserService userService;
|
||||
private final JwtFilter jwtFilter;
|
||||
|
||||
public SecurityConfiguration(UserService userService) {
|
||||
this.userService = userService;
|
||||
this.jwtFilter = new JwtFilter(userService);
|
||||
createAdminOnStartup();
|
||||
}
|
||||
|
||||
private void createAdminOnStartup() {
|
||||
final String admin = "admin";
|
||||
if (userService.findByLogin(admin) == null) {
|
||||
log.info("Admin user successfully created");
|
||||
userService.addUser(admin, "admin@gmail.com", admin, admin, UserRole.ADMIN);
|
||||
}
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||
http.cors()
|
||||
.and()
|
||||
.csrf().disable()
|
||||
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
|
||||
.and()
|
||||
.authorizeHttpRequests()
|
||||
.requestMatchers("/", SPA_URL_MASK).permitAll()
|
||||
.requestMatchers(HttpMethod.POST, UserController.URL_LOGIN).permitAll()
|
||||
.requestMatchers(HttpMethod.POST, UserController.URL_SING_UP).permitAll()
|
||||
.requestMatchers(HttpMethod.POST, UserController.URL_WHO_AM_I).permitAll()
|
||||
.anyRequest()
|
||||
.authenticated()
|
||||
.and()
|
||||
.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class)
|
||||
.anonymous();
|
||||
return http.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AuthenticationManager authenticationManagerBean(HttpSecurity http) throws Exception {
|
||||
AuthenticationManagerBuilder authenticationManagerBuilder = http
|
||||
.getSharedObject(AuthenticationManagerBuilder.class);
|
||||
authenticationManagerBuilder.userDetailsService(userService);
|
||||
return authenticationManagerBuilder.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public WebSecurityCustomizer webSecurityCustomizer() {
|
||||
return (web) -> web.ignoring()
|
||||
.requestMatchers(HttpMethod.OPTIONS, "/**")
|
||||
.requestMatchers("/*.js")
|
||||
.requestMatchers("/*.html")
|
||||
.requestMatchers("/*.css")
|
||||
.requestMatchers("/assets/**")
|
||||
.requestMatchers("/favicon.ico")
|
||||
.requestMatchers("/.js", "/.css")
|
||||
.requestMatchers("/swagger-ui/index.html")
|
||||
.requestMatchers("/webjars/**")
|
||||
.requestMatchers("/swagger-resources/**")
|
||||
.requestMatchers("/v3/api-docs/**")
|
||||
.requestMatchers("/h2-console");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.LabWork.app.MangaStore.configuration;
|
||||
|
||||
import org.springframework.boot.web.server.ErrorPage;
|
||||
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
|
||||
import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory;
|
||||
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.ViewControllerRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
@Configuration
|
||||
public class WebConfiguration implements WebMvcConfigurer {
|
||||
@Override
|
||||
public void addViewControllers(ViewControllerRegistry registry) {
|
||||
registry.addViewController(SecurityConfiguration.SPA_URL_MASK).setViewName("forward:/");
|
||||
registry.addViewController("/notFound").setViewName("forward:/");
|
||||
}
|
||||
|
||||
@Bean
|
||||
public WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> containerCustomizer() {
|
||||
return container -> container.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND, "/notFound"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addCorsMappings(CorsRegistry registry) {
|
||||
registry.addMapping("/**").allowedMethods("*");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.LabWork.app.MangaStore.configuration.jwt;
|
||||
|
||||
public class JwtException extends RuntimeException {
|
||||
public JwtException(Throwable throwable) {
|
||||
super(throwable);
|
||||
}
|
||||
|
||||
public JwtException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package com.LabWork.app.MangaStore.configuration.jwt;
|
||||
|
||||
import com.LabWork.app.MangaStore.service.UserService;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import jakarta.servlet.FilterChain;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.ServletRequest;
|
||||
import jakarta.servlet.ServletResponse;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.filter.GenericFilterBean;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class JwtFilter extends GenericFilterBean {
|
||||
private static final String AUTHORIZATION = "Authorization";
|
||||
public static final String TOKEN_BEGIN_STR = "Bearer ";
|
||||
|
||||
private final UserService userService;
|
||||
|
||||
public JwtFilter(UserService userService) {
|
||||
this.userService = userService;
|
||||
}
|
||||
|
||||
private String getTokenFromRequest(HttpServletRequest request) {
|
||||
String bearer = request.getHeader(AUTHORIZATION);
|
||||
if (StringUtils.hasText(bearer) && bearer.startsWith(TOKEN_BEGIN_STR)) {
|
||||
return bearer.substring(TOKEN_BEGIN_STR.length());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void raiseException(ServletResponse response, int status, String message) throws IOException {
|
||||
if (response instanceof final HttpServletResponse httpResponse) {
|
||||
httpResponse.setContentType(MediaType.APPLICATION_JSON_VALUE);
|
||||
httpResponse.setStatus(status);
|
||||
final byte[] body = new ObjectMapper().writeValueAsBytes(message);
|
||||
response.getOutputStream().write(body);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doFilter(ServletRequest request,
|
||||
ServletResponse response,
|
||||
FilterChain chain) throws IOException, ServletException {
|
||||
if (request instanceof final HttpServletRequest httpRequest) {
|
||||
final String token = getTokenFromRequest(httpRequest);
|
||||
if (StringUtils.hasText(token)) {
|
||||
try {
|
||||
final UserDetails user = userService.loadUserByToken(token);
|
||||
final UsernamePasswordAuthenticationToken auth =
|
||||
new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities());
|
||||
SecurityContextHolder.getContext().setAuthentication(auth);
|
||||
} catch (JwtException e) {
|
||||
raiseException(response, HttpServletResponse.SC_UNAUTHORIZED, e.getMessage());
|
||||
return;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
raiseException(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
|
||||
String.format("Internal error: %s", e.getMessage()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
chain.doFilter(request, response);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.LabWork.app.MangaStore.configuration.jwt;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
@ConfigurationProperties(prefix = "jwt", ignoreInvalidFields = true)
|
||||
public class JwtProperties {
|
||||
private String devToken = "";
|
||||
private Boolean isDev = true;
|
||||
|
||||
public String getDevToken() {
|
||||
return devToken;
|
||||
}
|
||||
|
||||
public void setDevToken(String devToken) {
|
||||
this.devToken = devToken;
|
||||
}
|
||||
|
||||
public Boolean isDev() {
|
||||
return isDev;
|
||||
}
|
||||
|
||||
public void setDev(Boolean dev) {
|
||||
isDev = dev;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
package com.LabWork.app.MangaStore.configuration.jwt;
|
||||
|
||||
import com.auth0.jwt.JWT;
|
||||
import com.auth0.jwt.algorithms.Algorithm;
|
||||
import com.auth0.jwt.exceptions.JWTVerificationException;
|
||||
import com.auth0.jwt.interfaces.DecodedJWT;
|
||||
import com.auth0.jwt.interfaces.JWTVerifier;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.time.LocalDate;
|
||||
import java.time.ZoneId;
|
||||
import java.util.Date;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
@Component
|
||||
public class JwtProvider {
|
||||
private final static Logger LOG = LoggerFactory.getLogger(JwtProvider.class);
|
||||
|
||||
private final static byte[] HEX_ARRAY = "0123456789ABCDEF".getBytes(StandardCharsets.US_ASCII);
|
||||
private final static String ISSUER = "auth0";
|
||||
|
||||
private final Algorithm algorithm;
|
||||
private final JWTVerifier verifier;
|
||||
|
||||
public JwtProvider(JwtProperties jwtProperties) {
|
||||
if (!jwtProperties.isDev()) {
|
||||
LOG.info("Generate new JWT key for prod");
|
||||
try {
|
||||
final MessageDigest salt = MessageDigest.getInstance("SHA-256");
|
||||
salt.update(UUID.randomUUID().toString().getBytes(StandardCharsets.UTF_8));
|
||||
LOG.info("Use generated JWT key for prod \n{}", bytesToHex(salt.digest()));
|
||||
algorithm = Algorithm.HMAC256(bytesToHex(salt.digest()));
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new JwtException(e);
|
||||
}
|
||||
} else {
|
||||
LOG.info("Use default JWT key for dev \n{}", jwtProperties.getDevToken());
|
||||
algorithm = Algorithm.HMAC256(jwtProperties.getDevToken());
|
||||
}
|
||||
verifier = JWT.require(algorithm)
|
||||
.withIssuer(ISSUER)
|
||||
.build();
|
||||
}
|
||||
|
||||
private static String bytesToHex(byte[] bytes) {
|
||||
byte[] hexChars = new byte[bytes.length * 2];
|
||||
for (int j = 0; j < bytes.length; j++) {
|
||||
int v = bytes[j] & 0xFF;
|
||||
hexChars[j * 2] = HEX_ARRAY[v >>> 4];
|
||||
hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
|
||||
}
|
||||
return new String(hexChars, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
public String generateToken(String login) {
|
||||
final Date issueDate = Date.from(LocalDate.now()
|
||||
.atStartOfDay(ZoneId.systemDefault())
|
||||
.toInstant());
|
||||
final Date expireDate = Date.from(LocalDate.now()
|
||||
.plusDays(15)
|
||||
.atStartOfDay(ZoneId.systemDefault())
|
||||
.toInstant());
|
||||
return JWT.create()
|
||||
.withIssuer(ISSUER)
|
||||
.withIssuedAt(issueDate)
|
||||
.withExpiresAt(expireDate)
|
||||
.withSubject(login)
|
||||
.sign(algorithm);
|
||||
}
|
||||
|
||||
private DecodedJWT validateToken(String token) {
|
||||
try {
|
||||
return verifier.verify(token);
|
||||
} catch (JWTVerificationException e) {
|
||||
throw new JwtException(String.format("Token verification error: %s", e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isTokenValid(String token) {
|
||||
if (!StringUtils.hasText(token)) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
validateToken(token);
|
||||
return true;
|
||||
} catch (JwtException e) {
|
||||
LOG.error(e.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public Optional<String> getLoginFromToken(String token) {
|
||||
try {
|
||||
return Optional.ofNullable(validateToken(token).getSubject());
|
||||
} catch (JwtException e) {
|
||||
LOG.error(e.getMessage());
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package com.LabWork.app.MangaStore.controller;
|
||||
|
||||
import com.LabWork.app.MangaStore.model.Dto.CreatorMangaDto;
|
||||
import com.LabWork.app.MangaStore.service.CreatorService;
|
||||
import com.LabWork.app.MangaStore.service.UserService;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import com.LabWork.app.MangaStore.configuration.OpenAPI30Configuration;
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
@RequestMapping(OpenAPI30Configuration.API_PREFIX + "/creator")
|
||||
public class CreatorController {
|
||||
private final CreatorService creatorService;
|
||||
|
||||
private final UserService userService;
|
||||
|
||||
public CreatorController(CreatorService creatorService,
|
||||
UserService userService) {
|
||||
this.creatorService = creatorService;
|
||||
this.userService = userService;
|
||||
}
|
||||
|
||||
@GetMapping("/{login}")
|
||||
public CreatorMangaDto getCreator(@PathVariable String login) {
|
||||
return new CreatorMangaDto(creatorService.findCreator(userService.findByLogin(login).getCreator().getId()));
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public List<CreatorMangaDto> getCreators() {
|
||||
return creatorService.findAllCreators().stream()
|
||||
.map(CreatorMangaDto::new)
|
||||
.toList();
|
||||
}
|
||||
|
||||
/* @PostMapping
|
||||
public CreatorMangaDto createCreator(@RequestParam("creatorName") String creatorName,
|
||||
@RequestParam("password") String password) {
|
||||
return new CreatorMangaDto(creatorService.addCreator());
|
||||
}*/
|
||||
|
||||
/* @PutMapping("/{id}")
|
||||
public CreatorMangaDto updateCreator(@PathVariable Long id,
|
||||
@RequestParam("creatorName") String creatorName,
|
||||
@RequestParam("password") String password) {
|
||||
return new CreatorMangaDto(creatorService.updateCreator(id, creatorName, password));
|
||||
}*/
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
public CreatorMangaDto deleteCreator(@PathVariable Long id) {
|
||||
//creatorService.deleteAllCreators();
|
||||
return new CreatorMangaDto(creatorService.deleteCreator(id));
|
||||
}
|
||||
|
||||
@DeleteMapping
|
||||
public void deleteAllCreator() {
|
||||
creatorService.deleteAllCreators();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package com.LabWork.app.MangaStore.controller;
|
||||
|
||||
|
||||
import com.LabWork.app.MangaStore.model.Dto.MangaReaderDto;
|
||||
import com.LabWork.app.MangaStore.model.Dto.MangaUserDto;
|
||||
import com.LabWork.app.MangaStore.model.Dto.ReaderMangaDto;
|
||||
import com.LabWork.app.MangaStore.model.Dto.SupportDto.MangaDto;
|
||||
import com.LabWork.app.MangaStore.service.MangaService;
|
||||
import com.LabWork.app.MangaStore.configuration.OpenAPI30Configuration;
|
||||
import com.LabWork.app.MangaStore.service.UserService;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
@RequestMapping(OpenAPI30Configuration.API_PREFIX + "/manga")
|
||||
public class MangaController {
|
||||
private final MangaService mangaService;
|
||||
|
||||
private final UserService userService;
|
||||
|
||||
public MangaController(MangaService mangaService,
|
||||
UserService userService) {
|
||||
this.mangaService = mangaService;
|
||||
this.userService = userService;
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
public MangaUserDto getManga(@PathVariable Long id) {
|
||||
return new MangaUserDto(mangaService.findManga(id), mangaService.getReader(id).stream()
|
||||
.map(x -> userService.findUser(x.getId()))
|
||||
.toList());
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public List<MangaReaderDto> getMangas() {
|
||||
return mangaService.findAllMangas().stream()
|
||||
.map(x -> new MangaReaderDto(x, mangaService.getReader(x.getId())))
|
||||
.toList();
|
||||
}
|
||||
|
||||
@GetMapping("/{id}/readers")
|
||||
public List<ReaderMangaDto> getReaders(@PathVariable Long id) {
|
||||
return mangaService.getReader(id).stream()
|
||||
.map(x -> new ReaderMangaDto(x))
|
||||
.toList();
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
public MangaDto deleteManga(@PathVariable Long id) {
|
||||
return new MangaDto(mangaService.deleteManga(id));
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
public MangaDto createManga(@RequestBody @Valid MangaDto mangaDto) {
|
||||
return new MangaDto(mangaService.addManga(mangaDto));
|
||||
}
|
||||
|
||||
@PutMapping("/{id}")
|
||||
public MangaDto updateManga(@RequestBody @Valid MangaDto mangaDto) {
|
||||
return new MangaDto(mangaService.updateManga(mangaDto));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.LabWork.app.MangaStore.controller;
|
||||
|
||||
public class Pair<F, S> {
|
||||
private final F first;
|
||||
private final S second;
|
||||
|
||||
public Pair(F first, S second) {
|
||||
this.first = first;
|
||||
this.second = second;
|
||||
}
|
||||
|
||||
public F getFirst() {
|
||||
return first;
|
||||
}
|
||||
|
||||
public S getSecond() {
|
||||
return second;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package com.LabWork.app.MangaStore.controller;
|
||||
|
||||
|
||||
import com.LabWork.app.MangaStore.configuration.OpenAPI30Configuration;
|
||||
import com.LabWork.app.MangaStore.model.Default.UserRole;
|
||||
import com.LabWork.app.MangaStore.model.Dto.ReaderMangaDto;
|
||||
import com.LabWork.app.MangaStore.model.Dto.SupportDto.MangaDto;
|
||||
import com.LabWork.app.MangaStore.service.ReaderService;
|
||||
import com.LabWork.app.MangaStore.service.UserService;
|
||||
import com.LabWork.app.MangaStore.util.validation.ValidationException;
|
||||
import org.springframework.security.access.annotation.Secured;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
@RequestMapping(OpenAPI30Configuration.API_PREFIX + "/reader")
|
||||
public class ReaderController {
|
||||
private final ReaderService readerService;
|
||||
|
||||
private final UserService userService;
|
||||
|
||||
public ReaderController(ReaderService readerService,
|
||||
UserService userService) {
|
||||
this.readerService = readerService;
|
||||
this.userService = userService;
|
||||
}
|
||||
|
||||
@GetMapping("/{login}")
|
||||
public ReaderMangaDto getReader(@PathVariable String login) {
|
||||
return new ReaderMangaDto(readerService.findReader(userService.findByLogin(login).getReader().getId()));
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public List<ReaderMangaDto> getReaders() {
|
||||
return readerService.findAllReaders().stream()
|
||||
.map(ReaderMangaDto::new)
|
||||
.toList();
|
||||
}
|
||||
|
||||
/* @PostMapping
|
||||
@Secured({UserRole.AsString.ADMIN})
|
||||
public String createReader(@RequestParam("readerName") String readerName,
|
||||
@RequestParam("password") String password) {
|
||||
try {
|
||||
return new ReaderMangaDto(readerService.addReader(readerName, password)).toString();
|
||||
} catch (ValidationException e) {
|
||||
return e.getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
@PutMapping("/{id}")
|
||||
public ReaderMangaDto updateReader(@PathVariable Long id,
|
||||
@RequestParam("readerName") String readerName,
|
||||
@RequestParam("password") String password) {
|
||||
return new ReaderMangaDto(readerService.updateReader(id, readerName, password));
|
||||
}*/
|
||||
|
||||
@PutMapping("/{id}/addManga")
|
||||
public MangaDto addManga(@PathVariable Long id,
|
||||
@RequestParam("mangaId") Long mangaId) {
|
||||
return new MangaDto(readerService.addManga(mangaId, id));
|
||||
}
|
||||
|
||||
@PutMapping("/{id}/removeManga")
|
||||
public MangaDto removeManga(@PathVariable Long id,
|
||||
@RequestParam("mangaId") Long mangaId) {
|
||||
return new MangaDto(readerService.removeManga(mangaId, id));
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
public ReaderMangaDto deleteReader(@PathVariable Long id) {
|
||||
return new ReaderMangaDto(readerService.deleteReader(id));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
package com.LabWork.app.MangaStore.controller;
|
||||
|
||||
|
||||
import com.LabWork.app.MangaStore.configuration.OpenAPI30Configuration;
|
||||
import com.LabWork.app.MangaStore.model.Default.User;
|
||||
import com.LabWork.app.MangaStore.model.Default.UserRole;
|
||||
import com.LabWork.app.MangaStore.model.Dto.UserDto;
|
||||
import com.LabWork.app.MangaStore.model.Dto.UserSignupDto;
|
||||
import com.LabWork.app.MangaStore.service.UserService;
|
||||
import com.LabWork.app.MangaStore.util.validation.ValidationException;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.security.access.annotation.Secured;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
@RestController
|
||||
public class UserController {
|
||||
public static final String URL_LOGIN = "/jwt/login";
|
||||
public static final String URL_SING_UP = "/sing_up";
|
||||
public static final String URL_WHO_AM_I = "/who_am_i";
|
||||
private final UserService userService;
|
||||
|
||||
public UserController(UserService userService) {
|
||||
this.userService = userService;
|
||||
}
|
||||
|
||||
@PostMapping(URL_LOGIN)
|
||||
public String login(@RequestBody @Valid UserDto userDto) {
|
||||
return userService.loginAndGetToken(userDto);
|
||||
}
|
||||
|
||||
@PostMapping(URL_SING_UP)
|
||||
public String singUp(@RequestBody @Valid UserSignupDto userSignupDto) {
|
||||
try {
|
||||
final User user = userService.addUser(userSignupDto.getLogin(), userSignupDto.getEmail(),
|
||||
userSignupDto.getPassword(), userSignupDto.getPasswordConfirm(), UserRole.USER);
|
||||
return "created " + user.getLogin();
|
||||
} catch (ValidationException e) {
|
||||
return e.getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping(OpenAPI30Configuration.API_PREFIX + "/user")
|
||||
public UserDto getUser(@RequestParam("login") String login) {
|
||||
User user = userService.findByLogin(login);
|
||||
return new UserDto(user);
|
||||
}
|
||||
|
||||
@Secured(UserRole.AsString.ADMIN)
|
||||
@PostMapping(OpenAPI30Configuration.API_PREFIX + "/user")
|
||||
public String updateUser(@RequestBody @Valid UserDto userDto) {
|
||||
try {
|
||||
userService.updateUser(userDto);
|
||||
return "Profile updated";
|
||||
} catch (ValidationException e) {
|
||||
return e.getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
@Secured(UserRole.AsString.ADMIN)
|
||||
@DeleteMapping(OpenAPI30Configuration.API_PREFIX + "/user/{id}")
|
||||
public UserDto removeUser(@PathVariable Long id) {
|
||||
User user = userService.deleteUser(id);
|
||||
return new UserDto(user);
|
||||
}
|
||||
|
||||
@Secured(UserRole.AsString.ADMIN)
|
||||
@GetMapping(OpenAPI30Configuration.API_PREFIX + "/users")
|
||||
public Pair<Page<UserDto>, List<Integer>> getUsers(@RequestParam(defaultValue = "1") int page,
|
||||
@RequestParam(defaultValue = "5") int size) {
|
||||
final Page<UserDto> users = userService.findAllPages(page, size)
|
||||
.map(UserDto::new);
|
||||
final int totalPages = users.getTotalPages();
|
||||
final List<Integer> pageNumbers = IntStream.rangeClosed(1, totalPages)
|
||||
.boxed()
|
||||
.toList();
|
||||
return new Pair<>(users, pageNumbers);
|
||||
}
|
||||
|
||||
@GetMapping(URL_WHO_AM_I)
|
||||
public String whoAmI(@RequestParam("token") String token) {
|
||||
UserDetails userDetails = userService.loadUserByToken(token);
|
||||
User user = userService.findByLogin(userDetails.getUsername());
|
||||
return user.getRole().toString();
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
package com.LabWork.app.student.model;
|
||||
package com.LabWork.app.MangaStore.model.Default;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
@@ -11,21 +13,10 @@ public class Creator {
|
||||
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||
private Long id;
|
||||
|
||||
@Column
|
||||
private String creatorName;
|
||||
|
||||
@Column
|
||||
private String hashedPassword;
|
||||
|
||||
@OneToMany(fetch = FetchType.EAGER, mappedBy = "creator", cascade = CascadeType.REMOVE)
|
||||
private List<Manga> mangas;
|
||||
|
||||
public Creator() {
|
||||
}
|
||||
|
||||
public Creator(String creatorName, String hashedPassword) {
|
||||
this.creatorName = creatorName;
|
||||
this.hashedPassword = hashedPassword;
|
||||
this.mangas = new ArrayList<>();
|
||||
}
|
||||
|
||||
@@ -37,26 +28,10 @@ public class Creator {
|
||||
return mangas;
|
||||
}
|
||||
|
||||
public String getCreatorName() {
|
||||
return creatorName;
|
||||
}
|
||||
|
||||
public String getHashedPassword() {
|
||||
return hashedPassword;
|
||||
}
|
||||
|
||||
public void setMangas(List<Manga> mangas) {
|
||||
this.mangas = mangas;
|
||||
}
|
||||
|
||||
public void setCreatorName(String creatorName) {
|
||||
this.creatorName = creatorName;
|
||||
}
|
||||
|
||||
public void setHashedPassword(String hashedPassword) {
|
||||
this.hashedPassword = hashedPassword;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
@@ -74,8 +49,6 @@ public class Creator {
|
||||
public String toString() {
|
||||
return "Creator{" +
|
||||
"id=" + id +
|
||||
", creatorName='" + creatorName + '\'' +
|
||||
", hashedPassword='" + hashedPassword + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,8 @@
|
||||
package com.LabWork.app.student.model;
|
||||
package com.LabWork.app.MangaStore.model.Default;
|
||||
|
||||
import com.LabWork.app.MangaStore.model.Dto.SupportDto.MangaDto;
|
||||
import com.LabWork.app.MangaStore.service.CreatorService;
|
||||
import com.LabWork.app.MangaStore.service.MangaService;
|
||||
import jakarta.persistence.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -22,19 +25,43 @@ public class Manga {
|
||||
@JoinColumn(name="creator_fk")
|
||||
private Creator creator;
|
||||
|
||||
private Long creatorId;
|
||||
|
||||
@Lob
|
||||
private byte[] image;
|
||||
public Manga() {
|
||||
}
|
||||
|
||||
public Manga(Creator creator, String mangaName, Integer chapterCount) {
|
||||
public Manga(Creator creator, String mangaName, Integer chapterCount, String image) {
|
||||
this.creator = creator;
|
||||
this.mangaName = mangaName;
|
||||
this.chapterCount = chapterCount;
|
||||
this.image = image.getBytes();
|
||||
}
|
||||
|
||||
public Manga(Creator creator, MangaDto mangaDto) {
|
||||
this.creator = creator;
|
||||
this.mangaName = mangaDto.getMangaName();
|
||||
this.chapterCount = mangaDto.getChapterCount();
|
||||
this.image = mangaDto.getImage().getBytes();
|
||||
}
|
||||
|
||||
public byte[] getImage() {
|
||||
return image;
|
||||
}
|
||||
|
||||
public void setImage(byte[] image) {
|
||||
this.image = image;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public Long getCreatorId() {
|
||||
return creator.getId();
|
||||
}
|
||||
|
||||
public String getMangaName() {
|
||||
return mangaName;
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.LabWork.app.student.model;
|
||||
package com.LabWork.app.MangaStore.model.Default;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import org.hibernate.annotations.Cascade;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -12,42 +13,32 @@ public class Reader {
|
||||
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||
private Long id;
|
||||
|
||||
@Column
|
||||
private String readerName;
|
||||
|
||||
@Column
|
||||
private String hashedPassword;
|
||||
|
||||
|
||||
@ManyToMany(fetch = FetchType.EAGER)
|
||||
@ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.MERGE)
|
||||
/*@Cascade(value = org.hibernate.annotations.CascadeType.DELETE_ORPHAN)*/
|
||||
/*orphanRemoval=true*/
|
||||
private List<Manga> mangas;
|
||||
|
||||
@OneToOne
|
||||
@MapsId
|
||||
@JoinColumn(name = "user_id")
|
||||
private User user;
|
||||
|
||||
public Reader(User user) {
|
||||
this.mangas = new ArrayList<>();
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
public Reader() {
|
||||
}
|
||||
|
||||
public Reader(String readerName, String hashedPassword) {
|
||||
this.readerName = readerName;
|
||||
this.hashedPassword = hashedPassword;
|
||||
this.mangas = new ArrayList<>();
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public String getReaderName() { return readerName; }
|
||||
|
||||
public void setReaderName(String readerName) { this.readerName = readerName; }
|
||||
|
||||
public String getHashedPassword() { return hashedPassword; }
|
||||
|
||||
public void setHashedPassword(String hashedPassword) { this.hashedPassword = hashedPassword; }
|
||||
|
||||
public List<Manga> getMangas() { return mangas; }
|
||||
|
||||
public void setMangas(List<Manga> mangs) { this.mangas = mangs; }
|
||||
public void setMangas(List<Manga> mangas) { this.mangas = mangas; }
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
@@ -66,8 +57,6 @@ public class Reader {
|
||||
public String toString() {
|
||||
return "Reader{" +
|
||||
"id=" + id +
|
||||
", readerName='" + readerName + '\'' +
|
||||
", hashedPassword='" + hashedPassword + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
144
src/main/java/com/LabWork/app/MangaStore/model/Default/User.java
Normal file
144
src/main/java/com/LabWork/app/MangaStore/model/Default/User.java
Normal file
@@ -0,0 +1,144 @@
|
||||
package com.LabWork.app.MangaStore.model.Default;
|
||||
|
||||
import com.LabWork.app.MangaStore.model.Dto.UserDto;
|
||||
import com.LabWork.app.MangaStore.model.Dto.UserSignupDto;
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.Pattern;
|
||||
import jakarta.validation.constraints.Size;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
@Entity
|
||||
@Table(name = "users")
|
||||
public class User {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||
private Long id;
|
||||
@NotBlank(message = "Login can't be null or empty")
|
||||
@Size(min = 3, max = 64, message = "Incorrect login length")
|
||||
private String login;
|
||||
@NotBlank(message = "Email can't be null or empty")
|
||||
@Size(min = 3, max = 64, message = "Incorrect login length")
|
||||
private String email;
|
||||
@NotBlank(message = "Password can't be null or empty")
|
||||
@Size(min = 3, max = 64, message = "Incorrect password length")
|
||||
private String password;
|
||||
private UserRole role;
|
||||
|
||||
@OneToOne
|
||||
@JoinColumn(name = "creator_id")
|
||||
private Creator creator;
|
||||
|
||||
@OneToOne(mappedBy = "user", cascade = CascadeType.ALL)
|
||||
@PrimaryKeyJoinColumn
|
||||
private Reader reader;
|
||||
|
||||
public User() {
|
||||
}
|
||||
|
||||
public User(String login, String email, String password, UserRole role) {
|
||||
this.login = login;
|
||||
this.email = email;
|
||||
this.password = password;
|
||||
this.role = role;
|
||||
}
|
||||
|
||||
public User(UserDto userDto) {
|
||||
this.login = userDto.getLogin();
|
||||
this.email = userDto.getEmail();
|
||||
this.password = userDto.getPassword();
|
||||
this.role = userDto.getRole();
|
||||
}
|
||||
|
||||
public User(UserSignupDto userSignupDto) {
|
||||
this.login = userSignupDto.getLogin();
|
||||
this.email = userSignupDto.getEmail();
|
||||
this.password = userSignupDto.getPassword();
|
||||
this.role = UserRole.USER;
|
||||
}
|
||||
|
||||
// Properties
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getLogin() {
|
||||
return login;
|
||||
}
|
||||
|
||||
public void setLogin(String login) {
|
||||
this.login = login;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public UserRole getRole() {
|
||||
return role;
|
||||
}
|
||||
|
||||
public void setRole(UserRole role) {
|
||||
this.role = role;
|
||||
}
|
||||
|
||||
public void setCreator(Creator creator) {
|
||||
this.creator = creator;
|
||||
}
|
||||
|
||||
public Creator getCreator() {
|
||||
return creator;
|
||||
}
|
||||
|
||||
public void setReader(Reader reader) {
|
||||
this.reader = reader;
|
||||
}
|
||||
|
||||
public Reader getReader() {
|
||||
return reader;
|
||||
}
|
||||
|
||||
// ![Properties]
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
User marketUser = (User) o;
|
||||
return Objects.equals(id, marketUser.id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "User{" +
|
||||
"id=" + id +
|
||||
", userName='" + login + '\'' +
|
||||
", email='" + email + '\'' +
|
||||
", password='" + password + '\'' +
|
||||
", role='" + role + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.LabWork.app.MangaStore.model.Default;
|
||||
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
|
||||
public enum UserRole implements GrantedAuthority {
|
||||
ADMIN,
|
||||
USER;
|
||||
|
||||
private static final String PREFIX = "ROLE_";
|
||||
|
||||
@Override
|
||||
public String getAuthority() {
|
||||
return PREFIX + this.name();
|
||||
}
|
||||
|
||||
public static final class AsString {
|
||||
public static final String ADMIN = PREFIX + "ADMIN";
|
||||
public static final String USER = PREFIX + "USER";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package com.LabWork.app.MangaStore.model.Dto;
|
||||
|
||||
import com.LabWork.app.MangaStore.model.Default.Creator;
|
||||
import com.LabWork.app.MangaStore.model.Dto.SupportDto.MangaDto;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class CreatorMangaDto {
|
||||
private long id;
|
||||
private String creatorName;
|
||||
private String hashedPassword;
|
||||
private List<MangaDto> mangas;
|
||||
|
||||
public CreatorMangaDto() {
|
||||
}
|
||||
|
||||
public CreatorMangaDto(Creator creator) {
|
||||
this.id = creator.getId();
|
||||
this.mangas = creator.getMangas().stream()
|
||||
.map(x -> new MangaDto(x))
|
||||
.toList();
|
||||
}
|
||||
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getCreatorName() {
|
||||
return creatorName;
|
||||
}
|
||||
|
||||
public String getHashedPassword() {
|
||||
return hashedPassword;
|
||||
}
|
||||
|
||||
public List<MangaDto> getMangas() { return mangas; }
|
||||
|
||||
public void setCreatorName(String creatorName) {
|
||||
this.creatorName = creatorName;
|
||||
}
|
||||
|
||||
public void setHashedPassword(String hashedPassword) {
|
||||
this.hashedPassword = hashedPassword;
|
||||
}
|
||||
|
||||
public void setMangas(List<MangaDto> mangas) {
|
||||
this.mangas = mangas;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package com.LabWork.app.MangaStore.model.Dto;
|
||||
|
||||
import com.LabWork.app.MangaStore.model.Default.Creator;
|
||||
import com.LabWork.app.MangaStore.model.Default.Manga;
|
||||
import com.LabWork.app.MangaStore.model.Default.Reader;
|
||||
import com.LabWork.app.MangaStore.model.Dto.SupportDto.MangaDto;
|
||||
import com.LabWork.app.MangaStore.model.Dto.SupportDto.ReaderDto;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
|
||||
public class MangaReaderDto {
|
||||
private Long id;
|
||||
|
||||
private Long creatorId;
|
||||
private String mangaName;
|
||||
private Integer chapterCount;
|
||||
private List<ReaderDto> readers;
|
||||
private String image;
|
||||
|
||||
public MangaReaderDto() {
|
||||
}
|
||||
|
||||
public MangaReaderDto(Manga manga, List<Reader> listReader) {
|
||||
this.id = manga.getId();
|
||||
this.creatorId = manga.getCreator().getId();
|
||||
this.mangaName = manga.getMangaName();
|
||||
this.chapterCount = manga.getChapterCount();
|
||||
this.image = new String(manga.getImage(), StandardCharsets.UTF_8);
|
||||
this.readers = listReader.stream()
|
||||
.map(y -> new ReaderDto(y))
|
||||
.toList();
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getImage() {
|
||||
return image;
|
||||
}
|
||||
|
||||
public String getMangaName() {
|
||||
return mangaName;
|
||||
}
|
||||
|
||||
public List<ReaderDto> getReaders() {
|
||||
return readers;
|
||||
}
|
||||
|
||||
public Integer getChapterCount() {
|
||||
return chapterCount;
|
||||
}
|
||||
|
||||
public Long getCreatorId() {
|
||||
return creatorId;
|
||||
}
|
||||
|
||||
public void setImage(String image) {
|
||||
this.image = image;
|
||||
}
|
||||
|
||||
public void setMangaName(String mangaName) {
|
||||
this.mangaName = mangaName;
|
||||
}
|
||||
|
||||
public void setReaders(List<ReaderDto> readers) {
|
||||
this.readers = readers;
|
||||
}
|
||||
|
||||
public void setChapterCount(Integer chapterCount) {
|
||||
this.chapterCount = chapterCount;
|
||||
}
|
||||
|
||||
public void setCreatorIdString(Long creatorId) {this.creatorId = creatorId;}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package com.LabWork.app.MangaStore.model.Dto;
|
||||
|
||||
import com.LabWork.app.MangaStore.model.Default.Manga;
|
||||
import com.LabWork.app.MangaStore.model.Default.Reader;
|
||||
import com.LabWork.app.MangaStore.model.Default.User;
|
||||
import com.LabWork.app.MangaStore.model.Dto.SupportDto.ReaderDto;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
|
||||
public class MangaUserDto {
|
||||
private Long id;
|
||||
|
||||
private Long creatorId;
|
||||
private String mangaName;
|
||||
private Integer chapterCount;
|
||||
private List<ReaderDto> readers;
|
||||
private String image;
|
||||
|
||||
public MangaUserDto() {
|
||||
}
|
||||
|
||||
public MangaUserDto(Manga manga, List<User> listUser) {
|
||||
this.id = manga.getId();
|
||||
this.creatorId = manga.getCreator().getId();
|
||||
this.mangaName = manga.getMangaName();
|
||||
this.chapterCount = manga.getChapterCount();
|
||||
this.image = new String(manga.getImage(), StandardCharsets.UTF_8);
|
||||
this.readers = listUser.stream()
|
||||
.map(y -> new ReaderDto(y.getReader(), y.getLogin()))
|
||||
.toList();
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getImage() {
|
||||
return image;
|
||||
}
|
||||
|
||||
public String getMangaName() {
|
||||
return mangaName;
|
||||
}
|
||||
|
||||
public List<ReaderDto> getReaders() {
|
||||
return readers;
|
||||
}
|
||||
|
||||
public Integer getChapterCount() {
|
||||
return chapterCount;
|
||||
}
|
||||
|
||||
public Long getCreatorId() {
|
||||
return creatorId;
|
||||
}
|
||||
|
||||
public void setImage(String image) {
|
||||
this.image = image;
|
||||
}
|
||||
|
||||
public void setMangaName(String mangaName) {
|
||||
this.mangaName = mangaName;
|
||||
}
|
||||
|
||||
public void setReaders(List<ReaderDto> readers) {
|
||||
this.readers = readers;
|
||||
}
|
||||
|
||||
public void setChapterCount(Integer chapterCount) {
|
||||
this.chapterCount = chapterCount;
|
||||
}
|
||||
|
||||
public void setCreatorIdString(Long creatorId) {this.creatorId = creatorId;}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package com.LabWork.app.MangaStore.model.Dto;
|
||||
|
||||
import com.LabWork.app.MangaStore.model.Default.Reader;
|
||||
import com.LabWork.app.MangaStore.model.Dto.SupportDto.MangaDto;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class ReaderMangaDto {
|
||||
private Long id;
|
||||
|
||||
private String readerName;
|
||||
|
||||
private String hashedPassword;
|
||||
|
||||
private List<MangaDto> mangas;
|
||||
|
||||
public ReaderMangaDto() {
|
||||
}
|
||||
|
||||
public ReaderMangaDto(Reader reader) {
|
||||
this.id = reader.getId();
|
||||
this.mangas = reader.getMangas().stream()
|
||||
.map(y -> new MangaDto(y))
|
||||
.toList();
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getReaderName() { return readerName; }
|
||||
|
||||
public String getHashedPassword() { return hashedPassword; }
|
||||
|
||||
public List<MangaDto> getMangas() { return mangas; }
|
||||
|
||||
public void setReaderName(String readerName) {
|
||||
this.readerName = readerName;
|
||||
}
|
||||
|
||||
public void setHashedPassword(String hashedPassword) {
|
||||
this.hashedPassword = hashedPassword;
|
||||
}
|
||||
|
||||
public void setMangas(List<MangaDto> mangas) {
|
||||
this.mangas = mangas;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package com.LabWork.app.MangaStore.model.Dto.SupportDto;
|
||||
|
||||
import com.LabWork.app.MangaStore.model.Default.Creator;
|
||||
import com.LabWork.app.MangaStore.model.Default.Manga;
|
||||
import com.LabWork.app.MangaStore.model.Default.Reader;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
|
||||
public class MangaDto {
|
||||
private Long id;
|
||||
private Long creatorId;
|
||||
private String mangaName;
|
||||
private Integer chapterCount;
|
||||
private String image;
|
||||
|
||||
public MangaDto() {
|
||||
}
|
||||
|
||||
public MangaDto(Manga manga) {
|
||||
this.id = manga.getId();
|
||||
this.creatorId = manga.getCreator().getId();
|
||||
this.mangaName = manga.getMangaName();
|
||||
this.chapterCount = manga.getChapterCount();
|
||||
this.image = new String(manga.getImage(), StandardCharsets.UTF_8);
|
||||
}
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getMangaName() {
|
||||
return mangaName;
|
||||
}
|
||||
|
||||
public Integer getChapterCount() {
|
||||
return chapterCount;
|
||||
}
|
||||
|
||||
public Long getCreatorId() {
|
||||
return creatorId;
|
||||
}
|
||||
|
||||
public String getImage() {
|
||||
return image;
|
||||
}
|
||||
|
||||
public void setMangaName(String mangaName) {
|
||||
this.mangaName = mangaName;
|
||||
}
|
||||
|
||||
public void setChapterCount(Integer chapterCount) {
|
||||
this.chapterCount = chapterCount;
|
||||
}
|
||||
|
||||
public void setCreatorId(Long creatorId) {
|
||||
this.creatorId = creatorId;
|
||||
}
|
||||
|
||||
public void setImage(String image) {this.image = image;}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.LabWork.app.MangaStore.model.Dto.SupportDto;
|
||||
|
||||
import com.LabWork.app.MangaStore.model.Default.Reader;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class ReaderDto {
|
||||
private Long id;
|
||||
|
||||
private String readerName;
|
||||
|
||||
private String hashedPassword;
|
||||
|
||||
public ReaderDto() {
|
||||
}
|
||||
|
||||
public ReaderDto(Reader reader) {
|
||||
this.id = reader.getId();
|
||||
}
|
||||
|
||||
public ReaderDto(Reader reader, String readerName) {
|
||||
this.id = reader.getId();
|
||||
this.readerName = readerName;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getReaderName() { return readerName; }
|
||||
|
||||
public String getHashedPassword() { return hashedPassword; }
|
||||
|
||||
public void setrRaderName(String readerName) {
|
||||
this.readerName = readerName;
|
||||
}
|
||||
|
||||
public void setHashedPassword(String hashedPassword) {
|
||||
this.hashedPassword = hashedPassword;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package com.LabWork.app.MangaStore.model.Dto;
|
||||
|
||||
|
||||
import com.LabWork.app.MangaStore.model.Default.User;
|
||||
import com.LabWork.app.MangaStore.model.Default.UserRole;
|
||||
|
||||
public class UserDto {
|
||||
private Long id;
|
||||
private String login;
|
||||
private String email;
|
||||
private String password;
|
||||
private UserRole role;
|
||||
private Long creatortId;
|
||||
|
||||
private Long readerId;
|
||||
|
||||
public UserDto() {
|
||||
}
|
||||
|
||||
public UserDto(User user) {
|
||||
this.id = user.getId();
|
||||
this.login = user.getLogin();
|
||||
this.email = user.getEmail();
|
||||
this.password = user.getPassword();
|
||||
this.role = user.getRole();
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setLogin(String login) {
|
||||
this.login = login;
|
||||
}
|
||||
|
||||
public String getLogin() {
|
||||
return login;
|
||||
}
|
||||
|
||||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public UserRole getRole() {
|
||||
return role;
|
||||
}
|
||||
|
||||
public Long getReaderId() {
|
||||
return readerId;
|
||||
}
|
||||
public Long getCreatorId() {
|
||||
return creatortId;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.LabWork.app.MangaStore.model.Dto;
|
||||
|
||||
public class UserSignupDto {
|
||||
private String login;
|
||||
private String email;
|
||||
private String password;
|
||||
private String passwordConfirm;
|
||||
|
||||
public String getLogin() {
|
||||
return login;
|
||||
}
|
||||
|
||||
public void setLogin(String login) {
|
||||
this.login = login;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public String getPasswordConfirm() {
|
||||
return passwordConfirm;
|
||||
}
|
||||
|
||||
public void setPasswordConfirm(String passwordConfirm) {
|
||||
this.passwordConfirm = passwordConfirm;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package com.LabWork.app.MangaStore.service;
|
||||
|
||||
import com.LabWork.app.MangaStore.model.Default.Creator;
|
||||
import com.LabWork.app.MangaStore.model.Default.Manga;
|
||||
import com.LabWork.app.MangaStore.model.Default.Reader;
|
||||
import com.LabWork.app.MangaStore.service.Repository.CreatorRepository;
|
||||
import com.LabWork.app.MangaStore.service.Exception.CreatorNotFoundException;
|
||||
import com.LabWork.app.MangaStore.util.validation.ValidatorUtil;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@Service
|
||||
public class CreatorService {
|
||||
private final CreatorRepository creatorRepository;
|
||||
private final MangaService mangaService;
|
||||
private final ValidatorUtil validatorUtil;
|
||||
|
||||
public CreatorService(CreatorRepository creatorRepository,
|
||||
MangaService mangaService,
|
||||
ValidatorUtil validatorUtil) {
|
||||
this.creatorRepository = creatorRepository;
|
||||
this.mangaService = mangaService;
|
||||
this.validatorUtil = validatorUtil;
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public Creator findCreator(Long id) {
|
||||
final Optional<Creator> creator = creatorRepository.findById(id);
|
||||
return creator.orElseThrow(() -> new CreatorNotFoundException(id));
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public List<Creator> findAllCreators() { return creatorRepository.findAll(); }
|
||||
|
||||
@Transactional
|
||||
public Creator addCreator() {
|
||||
final Creator creator = new Creator();
|
||||
validatorUtil.validate(creator);
|
||||
return creatorRepository.save(creator);
|
||||
}
|
||||
|
||||
/* @Transactional
|
||||
public Creator updateCreator(Long id, String creatorName, String password) {
|
||||
final Creator currentCreator = findCreator(id);
|
||||
currentCreator.setCreatorName(creatorName);
|
||||
currentCreator.setHashedPassword(password);
|
||||
validatorUtil.validate(currentCreator);
|
||||
return currentCreator;
|
||||
}*/
|
||||
|
||||
@Transactional
|
||||
public Creator deleteCreator(Long id) {
|
||||
final Creator currentCreator = findCreator(id);
|
||||
List<Manga> listManga = currentCreator.getMangas();
|
||||
for (Manga manga : listManga){
|
||||
for (final Reader reader :mangaService.getReader(manga.getId())){
|
||||
reader.getMangas().remove(manga);
|
||||
}
|
||||
}
|
||||
creatorRepository.delete(currentCreator);
|
||||
return currentCreator;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void deleteAllCreators() {
|
||||
creatorRepository.deleteAll();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.LabWork.app.MangaStore.service.Exception;
|
||||
|
||||
public class CreatorNotFoundException extends RuntimeException {
|
||||
public CreatorNotFoundException(Long id) {
|
||||
super(String.format("Creator with id [%s] is not found", id));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.LabWork.app.MangaStore.service.Exception;
|
||||
|
||||
public class MangaNotFoundException extends RuntimeException {
|
||||
public MangaNotFoundException(Long id) {
|
||||
super(String.format("Manga with id [%s] is not found", id));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.LabWork.app.MangaStore.service.Exception;
|
||||
|
||||
public class ReaderNotFoundException extends RuntimeException {
|
||||
public ReaderNotFoundException(Long id) {
|
||||
super(String.format("Reader with id [%s] is not found", id));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.LabWork.app.MangaStore.service.Exception;
|
||||
|
||||
public class UserNotFoundException extends RuntimeException {
|
||||
public UserNotFoundException(Long id) {
|
||||
super(String.format("User with id [%s] is not found", id));
|
||||
}
|
||||
public UserNotFoundException(String login) {
|
||||
super(String.format("User not found '%s'", login));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
package com.LabWork.app.MangaStore.service;
|
||||
import com.LabWork.app.MangaStore.model.Default.Creator;
|
||||
import com.LabWork.app.MangaStore.model.Default.Manga;
|
||||
import com.LabWork.app.MangaStore.model.Default.Reader;
|
||||
import com.LabWork.app.MangaStore.model.Dto.SupportDto.MangaDto;
|
||||
import com.LabWork.app.MangaStore.service.Repository.CreatorRepository;
|
||||
import com.LabWork.app.MangaStore.service.Repository.MangaRepository;
|
||||
import com.LabWork.app.MangaStore.service.Exception.CreatorNotFoundException;
|
||||
import com.LabWork.app.MangaStore.service.Exception.MangaNotFoundException;
|
||||
import com.LabWork.app.MangaStore.service.Repository.ReaderRepository;
|
||||
import com.LabWork.app.MangaStore.util.validation.ValidatorUtil;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@Service
|
||||
public class MangaService {
|
||||
public final MangaRepository mangaRepository;
|
||||
public final CreatorRepository creatorRepository;
|
||||
public final ReaderService readerService;
|
||||
private final ValidatorUtil validatorUtil;
|
||||
|
||||
public MangaService(MangaRepository mangaRepository,
|
||||
CreatorRepository creatorRepository,
|
||||
ReaderService readerService,
|
||||
ValidatorUtil validatorUtil) {
|
||||
this.mangaRepository = mangaRepository;
|
||||
this.readerService = readerService;
|
||||
this.creatorRepository = creatorRepository;
|
||||
this.validatorUtil = validatorUtil;
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public Manga findManga(Long id) {
|
||||
final Optional<Manga> manga = mangaRepository.findById(id);
|
||||
return manga.orElseThrow(() -> new MangaNotFoundException(id));
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public List<Reader> getReader(Long id) {
|
||||
final Manga currentManga = findManga(id);
|
||||
final List<Reader> listReader = mangaRepository.getReaders(currentManga);
|
||||
return listReader;
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public List<Manga> findAllMangas() {
|
||||
return mangaRepository.findAll();
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public Creator findCreator(Long id) {
|
||||
final Optional<Creator> creator = creatorRepository.findById(id);
|
||||
return creator.orElseThrow(() -> new CreatorNotFoundException(id));
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public Manga addManga(Long creatorId, Integer chapterCount, String mangaName, String Image) {
|
||||
final Creator currentCreator = findCreator(creatorId);
|
||||
final Manga manga = new Manga(currentCreator, mangaName, chapterCount, Image);
|
||||
validatorUtil.validate(manga);
|
||||
return mangaRepository.save(manga);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public Manga addManga(MangaDto mangaDto) {
|
||||
final Creator currentCreator = findCreator(mangaDto.getCreatorId());
|
||||
final Manga manga = new Manga(currentCreator, mangaDto);
|
||||
validatorUtil.validate(manga);
|
||||
return mangaRepository.save(manga);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public Manga updateManga(Long id, Integer chapterCount, String Image) {
|
||||
final Manga currentManga = findManga(id);
|
||||
currentManga.setChapterCount(chapterCount);
|
||||
currentManga.setImage(Image.getBytes());
|
||||
validatorUtil.validate(currentManga);
|
||||
return currentManga;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public Manga updateManga(MangaDto mangaDto) {
|
||||
final Manga currentManga = findManga(mangaDto.getId());
|
||||
currentManga.setChapterCount(mangaDto.getChapterCount());
|
||||
currentManga.setImage(mangaDto.getImage().getBytes());
|
||||
validatorUtil.validate(currentManga);
|
||||
return currentManga;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public Manga deleteManga(Long id) {
|
||||
final Manga currentManga = findManga(id);
|
||||
final List<Reader> listReader = readerService.findAllReaders();
|
||||
for (Reader reader : listReader){
|
||||
reader.getMangas().remove(currentManga);
|
||||
}
|
||||
mangaRepository.delete(currentManga);
|
||||
return currentManga;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void deleteAllMangas() {
|
||||
mangaRepository.deleteAll();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
package com.LabWork.app.MangaStore.service;
|
||||
|
||||
import com.LabWork.app.MangaStore.model.Default.Manga;
|
||||
import com.LabWork.app.MangaStore.model.Default.Reader;
|
||||
import com.LabWork.app.MangaStore.model.Default.User;
|
||||
import com.LabWork.app.MangaStore.service.Repository.MangaRepository;
|
||||
import com.LabWork.app.MangaStore.service.Repository.ReaderRepository;
|
||||
import com.LabWork.app.MangaStore.service.Exception.MangaNotFoundException;
|
||||
import com.LabWork.app.MangaStore.service.Exception.ReaderNotFoundException;
|
||||
import com.LabWork.app.MangaStore.util.validation.ValidatorUtil;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@Service
|
||||
public class ReaderService {
|
||||
private final ReaderRepository readerRepository;
|
||||
private final MangaRepository mangaRepository;
|
||||
private final ValidatorUtil validatorUtil;
|
||||
|
||||
public ReaderService(ReaderRepository readerRepository,
|
||||
ValidatorUtil validatorUtil,
|
||||
MangaRepository mangaRepository) {
|
||||
this.readerRepository = readerRepository;
|
||||
this.mangaRepository = mangaRepository;
|
||||
this.validatorUtil = validatorUtil;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public Reader findReader(Long id) {
|
||||
final Optional<Reader> reader = readerRepository.findById(id);
|
||||
return reader.orElseThrow(() -> new ReaderNotFoundException(id));
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public List<Reader> findAllReaders() {
|
||||
return readerRepository.findAll();
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public Reader addReader(User user) {
|
||||
final Reader reader = new Reader(user);
|
||||
validatorUtil.validate(reader);
|
||||
return readerRepository.save(reader);
|
||||
}
|
||||
|
||||
/* @Transactional
|
||||
public Reader updateReader(Long id, String readername, String password) {
|
||||
final Reader currentReader = findReader(id);
|
||||
currentReader.setReaderName(readername);
|
||||
currentReader.setHashedPassword(password);
|
||||
validatorUtil.validate(currentReader);
|
||||
return currentReader;
|
||||
}*/
|
||||
|
||||
@Transactional
|
||||
public Reader deleteReader(Long id) {
|
||||
final Reader currentReader = findReader(id);
|
||||
currentReader.getMangas().clear();
|
||||
readerRepository.delete(currentReader);
|
||||
return currentReader;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void deleteAllReaders() {
|
||||
readerRepository.deleteAll();
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public Manga findManga(Long id) {
|
||||
final Optional<Manga> manga = mangaRepository.findById(id);
|
||||
return manga.orElseThrow(() -> new MangaNotFoundException(id));
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public Manga addManga(Long mangaId, Long readerId) {
|
||||
final Manga manga = findManga(mangaId);
|
||||
final Reader reader = findReader(readerId);
|
||||
validatorUtil.validate(reader);
|
||||
if (reader.getMangas().contains(manga))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
reader.getMangas().add(manga);
|
||||
return manga;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public Manga removeManga(Long mangaId, Long readerId) {
|
||||
final Reader currentReader = findReader(readerId);
|
||||
final Manga currentManga = findManga(mangaId);
|
||||
currentReader.getMangas().remove(currentManga);
|
||||
return currentManga;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.LabWork.app.MangaStore.service.Repository;
|
||||
|
||||
import com.LabWork.app.MangaStore.model.Default.Creator;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
public interface CreatorRepository extends JpaRepository<Creator, Long> {
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.LabWork.app.MangaStore.service.Repository;
|
||||
|
||||
import com.LabWork.app.MangaStore.model.Default.Manga;
|
||||
import com.LabWork.app.MangaStore.model.Default.Reader;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
public interface MangaRepository extends JpaRepository<Manga, Long> {
|
||||
@Query("select r from Reader r where :manga MEMBER OF r.mangas")
|
||||
List<Reader> getReaders(Manga manga);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.LabWork.app.MangaStore.service.Repository;
|
||||
|
||||
import com.LabWork.app.MangaStore.model.Default.Reader;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface ReaderRepository extends JpaRepository<Reader, Long> {
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.LabWork.app.MangaStore.service.Repository;
|
||||
|
||||
import com.LabWork.app.MangaStore.model.Default.User;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
public interface UserRepository extends JpaRepository<User, Long> {
|
||||
User findOneByLoginIgnoreCase(String login);
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
package com.LabWork.app.MangaStore.service;
|
||||
|
||||
import com.LabWork.app.MangaStore.configuration.jwt.JwtException;
|
||||
import com.LabWork.app.MangaStore.configuration.jwt.JwtProvider;
|
||||
import com.LabWork.app.MangaStore.model.Default.User;
|
||||
import com.LabWork.app.MangaStore.model.Default.UserRole;
|
||||
import com.LabWork.app.MangaStore.model.Dto.UserDto;
|
||||
import com.LabWork.app.MangaStore.service.Exception.UserNotFoundException;
|
||||
import com.LabWork.app.MangaStore.service.Repository.UserRepository;
|
||||
import com.LabWork.app.MangaStore.util.validation.ValidationException;
|
||||
import com.LabWork.app.MangaStore.util.validation.ValidatorUtil;
|
||||
import com.LabWork.app.MangaStore.model.Default.Creator;
|
||||
import com.LabWork.app.MangaStore.model.Default.Reader;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
@Service
|
||||
public class UserService implements UserDetailsService {
|
||||
private final UserRepository userRepository;
|
||||
private final ValidatorUtil validatorUtil;
|
||||
private final PasswordEncoder passwordEncoder;
|
||||
private final JwtProvider jwtProvider;
|
||||
private final CreatorService creatorService;
|
||||
private final ReaderService readerService;
|
||||
|
||||
public UserService(UserRepository userRepository,
|
||||
ValidatorUtil validatorUtil,
|
||||
PasswordEncoder passwordEncoder,
|
||||
CreatorService creatorService,
|
||||
ReaderService readerService,
|
||||
JwtProvider jwtProvider) {
|
||||
this.userRepository = userRepository;
|
||||
this.validatorUtil = validatorUtil;
|
||||
this.passwordEncoder = passwordEncoder;
|
||||
this.jwtProvider = jwtProvider;
|
||||
this.creatorService = creatorService;
|
||||
this.readerService = readerService;
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public User findUser(Long id) {
|
||||
final Optional<User> user = userRepository.findById(id);
|
||||
return user.orElseThrow(() -> new UserNotFoundException(id));
|
||||
}
|
||||
|
||||
public User findByLogin(String login) {
|
||||
return userRepository.findOneByLoginIgnoreCase(login);
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public List<User> findAllUsers() {
|
||||
return userRepository.findAll();
|
||||
}
|
||||
|
||||
public Page<User> findAllPages(int page, int size) {
|
||||
return userRepository.findAll(PageRequest.of(page - 1, size, Sort.by("id").ascending()));
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public User addUser(String login, String email, String password, String passwordConfirm, UserRole role) {
|
||||
if (findByLogin(login) != null) {
|
||||
throw new ValidationException(String.format("User '%s' already exists", login));
|
||||
}
|
||||
if (!Objects.equals(password, passwordConfirm)) {
|
||||
throw new ValidationException("Passwords not equals");
|
||||
}
|
||||
final User user = new User(login, email, passwordEncoder.encode(password), role);
|
||||
//validatorUtil.validate(user);
|
||||
userRepository.save(user);
|
||||
if (role.toString().equals("ADMIN")){
|
||||
final Creator creator = creatorService.addCreator();
|
||||
bindCreator(user.getId(), creator.getId());
|
||||
}
|
||||
if (role.toString().equals("USER")){
|
||||
final Reader reader = readerService.addReader(user);
|
||||
bindReader(user.getId(), reader.getId());
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public User updateUser(UserDto userDto) {
|
||||
final User currentUser = findUser(userDto.getId());
|
||||
final User sameUser = findByLogin(userDto.getLogin());
|
||||
if (sameUser != null && !Objects.equals(sameUser.getId(), currentUser.getId())) {
|
||||
throw new ValidationException(String.format("User '%s' already exists", userDto.getLogin()));
|
||||
}
|
||||
if (!passwordEncoder.matches(userDto.getPassword(), currentUser.getPassword())) {
|
||||
throw new ValidationException("Incorrect password");
|
||||
}
|
||||
currentUser.setLogin(userDto.getLogin());
|
||||
currentUser.setEmail(userDto.getEmail());
|
||||
validatorUtil.validate(currentUser);
|
||||
return userRepository.save(currentUser);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public User deleteUser(Long id) {
|
||||
final User currentUser = findUser(id);
|
||||
userRepository.delete(currentUser);
|
||||
return currentUser;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void deleteAllUsers() {
|
||||
userRepository.deleteAll();
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public User bindCreator(Long id, Long creatorId) {
|
||||
final User user = findUser(id);
|
||||
final Creator creator = creatorService.findCreator(creatorId);
|
||||
user.setCreator(creator);
|
||||
return userRepository.save(user);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public User bindReader(Long id, Long readerId) {
|
||||
final User user = findUser(id);
|
||||
final Reader reader = readerService.findReader(readerId);
|
||||
user.setReader(reader);
|
||||
return userRepository.save(user);
|
||||
}
|
||||
|
||||
public String loginAndGetToken(UserDto userDto) {
|
||||
final User user = findByLogin(userDto.getLogin());
|
||||
if (user == null) {
|
||||
throw new UserNotFoundException(userDto.getLogin());
|
||||
}
|
||||
if (!passwordEncoder.matches(userDto.getPassword(), user.getPassword())) {
|
||||
throw new ValidationException("Incorrect password");
|
||||
}
|
||||
return jwtProvider.generateToken(user.getLogin());
|
||||
}
|
||||
|
||||
public UserDetails loadUserByToken(String token) throws UsernameNotFoundException {
|
||||
if (!jwtProvider.isTokenValid(token)) {
|
||||
throw new JwtException("Bad token");
|
||||
}
|
||||
final String userLogin = jwtProvider.getLoginFromToken(token)
|
||||
.orElseThrow(() -> new JwtException("Token is not contain Login"));
|
||||
return loadUserByUsername(userLogin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
|
||||
final User userEntity = findByLogin(username);
|
||||
if (userEntity == null) {
|
||||
throw new UsernameNotFoundException(username);
|
||||
}
|
||||
return new org.springframework.security.core.userdetails.User(
|
||||
userEntity.getLogin(), userEntity.getPassword(), Collections.singleton(userEntity.getRole()));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.LabWork.app.MangaStore.util.error;
|
||||
|
||||
import com.LabWork.app.MangaStore.service.Exception.CreatorNotFoundException;
|
||||
import com.LabWork.app.MangaStore.service.Exception.MangaNotFoundException;
|
||||
import com.LabWork.app.MangaStore.service.Exception.ReaderNotFoundException;
|
||||
import com.LabWork.app.MangaStore.util.validation.ValidationException;
|
||||
import org.springframework.context.support.DefaultMessageSourceResolvable;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.MethodArgumentNotValidException;
|
||||
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@ControllerAdvice
|
||||
public class AdviceController {
|
||||
@ExceptionHandler({
|
||||
CreatorNotFoundException.class,
|
||||
MangaNotFoundException.class,
|
||||
ReaderNotFoundException.class,
|
||||
ValidationException.class
|
||||
})
|
||||
public ResponseEntity<Object> handleException(Throwable e) {
|
||||
return new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
|
||||
@ExceptionHandler(MethodArgumentNotValidException.class)
|
||||
public ResponseEntity<Object> handleBindException(MethodArgumentNotValidException e) {
|
||||
final ValidationException validationException = new ValidationException(
|
||||
e.getBindingResult().getAllErrors().stream()
|
||||
.map(DefaultMessageSourceResolvable::getDefaultMessage)
|
||||
.collect(Collectors.toSet()));
|
||||
return handleException(validationException);
|
||||
}
|
||||
|
||||
@ExceptionHandler(Exception.class)
|
||||
public ResponseEntity<Object> handleUnknownException(Throwable e) {
|
||||
e.printStackTrace();
|
||||
return new ResponseEntity<>(e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.LabWork.app.MangaStore.util.validation;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public class ValidationException extends RuntimeException {
|
||||
public <T> ValidationException(Set<String> errors) {
|
||||
super(String.join("\n", errors));
|
||||
}
|
||||
|
||||
public <T> ValidationException(String error) {
|
||||
super(error);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.LabWork.app.MangaStore.util.validation;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import jakarta.validation.ConstraintViolation;
|
||||
import jakarta.validation.Validation;
|
||||
import jakarta.validation.Validator;
|
||||
import jakarta.validation.ValidatorFactory;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Component
|
||||
public class ValidatorUtil {
|
||||
private final Validator validator;
|
||||
|
||||
public ValidatorUtil() {
|
||||
try (ValidatorFactory factory = Validation.buildDefaultValidatorFactory()) {
|
||||
this.validator = factory.getValidator();
|
||||
}
|
||||
}
|
||||
|
||||
public <T> void validate(T object) {
|
||||
final Set<ConstraintViolation<T>> errors = validator.validate(object);
|
||||
if (!errors.isEmpty()) {
|
||||
throw new ValidationException(errors.stream()
|
||||
.map(ConstraintViolation::getMessage)
|
||||
.collect(Collectors.toSet()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
package com.LabWork.app;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.config.annotation.CorsRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
@Configuration
|
||||
public class WebConfiguration implements WebMvcConfigurer {
|
||||
@Override
|
||||
public void addCorsMappings(CorsRegistry registry) {
|
||||
registry.addMapping("/**").allowedMethods("*");
|
||||
}
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
package com.LabWork.app.student.service;
|
||||
|
||||
import com.LabWork.app.student.model.Creator;
|
||||
import com.LabWork.app.student.model.Manga;
|
||||
import jakarta.persistence.EntityManager;
|
||||
import jakarta.persistence.EntityNotFoundException;
|
||||
import jakarta.persistence.PersistenceContext;
|
||||
import jakarta.transaction.Transactional;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
public class CreatorService {
|
||||
@PersistenceContext
|
||||
private EntityManager em;
|
||||
|
||||
@Transactional
|
||||
public Creator findCreator(Long id) {
|
||||
final Creator creator = em.find(Creator.class, id);
|
||||
if (creator == null) {
|
||||
throw new EntityNotFoundException(String.format("Creator with id [%s] is not found", id));
|
||||
}
|
||||
return creator;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public List<Creator> findAllCreators() {
|
||||
return em.createQuery("select c from Creator c", Creator.class).getResultList();
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public Creator addCreator(String creatorName, String password) {
|
||||
if (!StringUtils.hasText(creatorName) || !StringUtils.hasText(password)) {
|
||||
throw new IllegalArgumentException("Creator's creatorName or password is empty");
|
||||
}
|
||||
final Creator creator = new Creator(creatorName, password);
|
||||
em.persist(creator);
|
||||
return creator;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public Creator updateCreator(Long id, String creatorName, String password) {
|
||||
if (!StringUtils.hasText(creatorName) || !StringUtils.hasText(password)) {
|
||||
throw new IllegalArgumentException("Creator's creatorName or password is empty");
|
||||
}
|
||||
final Creator customer = findCreator(id);
|
||||
customer.setCreatorName(creatorName);
|
||||
customer.setHashedPassword(password);
|
||||
return em.merge(customer);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public Creator deleteCreator(Long id) {
|
||||
final Creator currentCustomer = findCreator(id);
|
||||
em.remove(currentCustomer);
|
||||
return currentCustomer;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void deleteAllCreators() { em.createQuery("delete from Creator").executeUpdate(); }
|
||||
|
||||
/*
|
||||
//бесполезная штука
|
||||
@Transactional
|
||||
public Creator addManga(Long creatorId, Manga manga) {
|
||||
final Creator creator = findCreator(creatorId);
|
||||
creator.getMangas().add(manga);
|
||||
em.merge(creator);
|
||||
return creator;
|
||||
}*/
|
||||
|
||||
/* //бесполезная штука
|
||||
@Transactional
|
||||
public Manga deleteManga(Long creatorId, Manga manga) {
|
||||
Creator creator = findCreator(creatorId);
|
||||
if (creator.getMangas().contains(manga)){
|
||||
final Manga currentManga = em.createQuery("select m from Manga m where m.id = " + manga.getId(), Manga.class).getSingleResult();
|
||||
em.remove(currentManga);
|
||||
return currentManga;
|
||||
}
|
||||
return null;
|
||||
}*/
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
package com.LabWork.app.student.service;
|
||||
|
||||
import com.LabWork.app.student.model.Creator;
|
||||
import com.LabWork.app.student.model.Manga;
|
||||
import com.LabWork.app.student.model.Reader;
|
||||
import jakarta.persistence.EntityManager;
|
||||
import jakarta.persistence.EntityNotFoundException;
|
||||
import jakarta.persistence.PersistenceContext;
|
||||
import jakarta.transaction.Transactional;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
public class MangaService {
|
||||
@PersistenceContext
|
||||
private EntityManager em;
|
||||
|
||||
@Transactional
|
||||
public Manga findManga(Long id) {
|
||||
final Manga manga = em.find(Manga.class, id);
|
||||
if (manga == null) {
|
||||
throw new EntityNotFoundException(String.format("Manga with id [%s] is not found", id));
|
||||
}
|
||||
return manga;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public List<Manga> findAllMangas() {
|
||||
return em.createQuery("select c from Manga c", Manga.class).getResultList();
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public Manga addManga(Creator creator, Integer chapterCount, String mangaName) {
|
||||
if (creator == null) {
|
||||
throw new IllegalArgumentException("Invalid creator");
|
||||
}
|
||||
if (chapterCount < 0 || chapterCount == null) {
|
||||
throw new IllegalArgumentException("Invalid chapterCount");
|
||||
}
|
||||
if (!StringUtils.hasText(mangaName)) {
|
||||
throw new IllegalArgumentException("Invalid mangaName");
|
||||
}
|
||||
final Manga manga = new Manga(creator, mangaName, chapterCount);
|
||||
manga.getCreator().getMangas().add(manga);
|
||||
em.persist(manga);
|
||||
return manga;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public Manga updateManga(Long id, Integer chapterCount) {
|
||||
if (chapterCount < 0 || chapterCount == null) {
|
||||
throw new IllegalArgumentException("Invalid chapterCount");
|
||||
}
|
||||
final Manga manga = findManga(id);
|
||||
manga.setChapterCount(chapterCount);
|
||||
em.merge(manga);
|
||||
return manga;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public List<Reader> getReader(Long id) {
|
||||
//em.createNativeQuery("delete from Mangas_Readers where MANGA_FK = " + manga.getId() + " AND READER_FK = "+ readerId).executeUpdate();
|
||||
//SELECT b FROM Book b WHERE ?1 MEMBER OF b.genres
|
||||
final Manga currentManga = em.find(Manga.class, id);
|
||||
final List<Reader> listReader = em.createQuery("select r from Reader r where :currentManga MEMBER OF r.mangas", Reader.class)
|
||||
.setParameter("currentManga", currentManga)
|
||||
.getResultList();
|
||||
return listReader;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public Manga deleteManga(Long id) {
|
||||
final Manga currentManga = findManga(id);
|
||||
final List<Reader> listReader = em.createQuery("select c from Reader c", Reader.class).getResultList();
|
||||
for (Reader reader : listReader){
|
||||
reader.getMangas().remove(currentManga);
|
||||
em.merge(reader);
|
||||
}
|
||||
em.remove(currentManga);
|
||||
return currentManga;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void deleteAllMangas() {
|
||||
em.createQuery("delete from Manga").executeUpdate();
|
||||
}
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
package com.LabWork.app.student.service;
|
||||
|
||||
import com.LabWork.app.student.model.Manga;
|
||||
import com.LabWork.app.student.model.Reader;
|
||||
import jakarta.persistence.EntityManager;
|
||||
import jakarta.persistence.EntityNotFoundException;
|
||||
import jakarta.persistence.PersistenceContext;
|
||||
import jakarta.transaction.Transactional;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
public class ReaderService {
|
||||
@PersistenceContext
|
||||
private EntityManager em;
|
||||
@Autowired
|
||||
private MangaService mangaService;
|
||||
|
||||
@Transactional
|
||||
public Reader findReader(Long id) {
|
||||
final Reader reader = em.find(Reader.class, id);
|
||||
if (reader == null) {
|
||||
throw new EntityNotFoundException(String.format("Reader with id [%s] is not found", id));
|
||||
}
|
||||
return reader;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public List<Reader> findAllReaders() {
|
||||
return em.createQuery("select c from Reader c", Reader.class).getResultList();
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public Reader addReader(String readername, String password) {
|
||||
if (!StringUtils.hasText(readername) || !StringUtils.hasText(password)) {
|
||||
throw new IllegalArgumentException("Customer's readername or password is empty");
|
||||
}
|
||||
final Reader reader = new Reader(readername, password);
|
||||
em.persist(reader);
|
||||
return reader;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void addManga(Manga manga, Long readerId) {
|
||||
final Reader reader = findReader(readerId);
|
||||
reader.getMangas().add(manga);
|
||||
//manga.getReaders().add(reader);
|
||||
em.merge(reader);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void removeManga(Manga manga, Long readerId) {
|
||||
//em.createNativeQuery("delete from Mangas_Readers where MANGA_FK = " + manga.getId() + " AND READER_FK = "+ readerId).executeUpdate();
|
||||
final Reader currentReader = findReader(readerId);
|
||||
final Manga currentManga = em.find(Manga.class, manga.getId());
|
||||
currentReader.getMangas().remove(currentManga);
|
||||
//currentManga.getReaders().remove(currentReader);
|
||||
em.merge(currentReader);
|
||||
em.merge(currentManga);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public Reader updateReader(Long id, String readername, String password) {
|
||||
if (!StringUtils.hasText(readername) || !StringUtils.hasText(password)) {
|
||||
throw new IllegalArgumentException("Customer's readername or password is empty");
|
||||
}
|
||||
final Reader reader = findReader(id);
|
||||
reader.setReaderName(readername);
|
||||
reader.setHashedPassword(password);
|
||||
return em.merge(reader);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public Reader deleteReader(Long id) {
|
||||
final Reader currentReader = findReader(id);
|
||||
/* for (Manga manga : currentReader.getMangas()){
|
||||
manga.getReaders().remove(currentReader);
|
||||
}*/
|
||||
em.merge(currentReader);
|
||||
em.remove(currentReader);
|
||||
return currentReader;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void deleteAllReaders() {
|
||||
em.createQuery("delete from Reader").executeUpdate();
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
spring.main.banner-mode=off
|
||||
#server.port=8080
|
||||
server.port=8080
|
||||
spring.datasource.url=jdbc:h2:file:./data
|
||||
spring.datasource.driverClassName=org.h2.Driver
|
||||
spring.datasource.username=sa
|
||||
@@ -9,3 +9,5 @@ spring.jpa.hibernate.ddl-auto=update
|
||||
spring.h2.console.enabled=true
|
||||
spring.h2.console.settings.trace=false
|
||||
spring.h2.console.settings.web-allow-others=false
|
||||
jwt.dev-token=my-secret-jwt
|
||||
jwt.dev=true
|
||||
@@ -1,62 +1,12 @@
|
||||
package com.LabWork.app;
|
||||
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
@SpringBootTest
|
||||
class AppApplicationTests {
|
||||
|
||||
/* @Autowired
|
||||
MethodService methodService;
|
||||
|
||||
@Test
|
||||
void testMethodSumInt() {
|
||||
final String res = methodService.Sum(1, 2, "int");
|
||||
Assertions.assertEquals(3, Integer.parseInt(res));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMethodSumString() {
|
||||
final String res = methodService.Sum("1", "2", "string");
|
||||
Assertions.assertEquals("12", res);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMethodMinusInt() {
|
||||
final String res = methodService.Difference(1, 2, "int");
|
||||
Assertions.assertEquals(-1, Integer.parseInt(res));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMethodMinusString() {
|
||||
final String res = methodService.Difference("214324", "4", "string");
|
||||
Assertions.assertEquals("2132", res);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMethodMultInt() {
|
||||
final String res = methodService.Multiplication(1, 2, "int");
|
||||
Assertions.assertEquals(2, Integer.parseInt(res));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMethodMultString() {
|
||||
final String res = methodService.Multiplication("1", "2", "string");
|
||||
Assertions.assertEquals("11", res);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMethodContainsInt() {
|
||||
final String res = methodService.Contains(123, 2, "int");
|
||||
Assertions.assertEquals(61, Integer.parseInt(res));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMethodContainsString() {
|
||||
final String res = methodService.Contains("1", "2", "string");
|
||||
Assertions.assertEquals("false", res);
|
||||
}*/
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(AppApplicationTests.class, args);
|
||||
}
|
||||
}
|
||||
|
||||
77
src/test/java/com/LabWork/app/JpaUserTests.java
Normal file
77
src/test/java/com/LabWork/app/JpaUserTests.java
Normal file
@@ -0,0 +1,77 @@
|
||||
package com.LabWork.app;
|
||||
|
||||
import com.LabWork.app.MangaStore.model.Default.User;
|
||||
import com.LabWork.app.MangaStore.model.Default.UserRole;
|
||||
import com.LabWork.app.MangaStore.service.Exception.UserNotFoundException;
|
||||
import com.LabWork.app.MangaStore.service.UserService;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@SpringBootTest
|
||||
public class JpaUserTests {
|
||||
private static final Logger log = LoggerFactory.getLogger(JpaUserTests.class);
|
||||
@Autowired
|
||||
private UserService userService;
|
||||
|
||||
|
||||
@Test
|
||||
void testUserCreate() {
|
||||
userService.deleteAllUsers();
|
||||
final User user = userService.addUser("User 1", "email@gmail.com",
|
||||
"123456", "123456", UserRole.USER);
|
||||
log.info("testUserCreate: " + user.toString());
|
||||
Assertions.assertNotNull(user.getId());
|
||||
|
||||
userService.deleteAllUsers();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUserRead() {
|
||||
userService.deleteAllUsers();
|
||||
final User user = userService.addUser("User 2", "email@gmail.com",
|
||||
"123456", "123456", UserRole.USER);
|
||||
log.info("testUserRead[0]: " + user.toString());
|
||||
final User findUser = userService.findUser(user.getId());
|
||||
log.info("testUserRead[1]: " + findUser.toString());
|
||||
Assertions.assertEquals(user, findUser);
|
||||
|
||||
userService.deleteAllUsers();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUserReadNotFound() {
|
||||
userService.deleteAllUsers();
|
||||
Assertions.assertThrows(UserNotFoundException.class, () -> userService.findUser(-1L));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUserReadAll() {
|
||||
userService.deleteAllUsers();
|
||||
userService.addUser("User 3", "email@gmail.com", "123456",
|
||||
"123456", UserRole.USER);
|
||||
userService.addUser("User 4", "email@gmail.com", "123456",
|
||||
"123456", UserRole.USER);
|
||||
final List<User> users = userService.findAllUsers();
|
||||
log.info("testUserReadAll: " + users.toString());
|
||||
Assertions.assertEquals(users.size(), 2);
|
||||
|
||||
userService.deleteAllUsers();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUserReadAllEmpty() {
|
||||
userService.deleteAllUsers();
|
||||
final List<User> users = userService.findAllUsers();
|
||||
log.info("testUserReadAllEmpty: " + users.toString());
|
||||
Assertions.assertEquals(users.size(), 0);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
/*
|
||||
package com.LabWork.app;
|
||||
|
||||
import com.LabWork.app.student.model.Creator;
|
||||
import com.LabWork.app.student.model.Manga;
|
||||
import com.LabWork.app.student.model.Reader;
|
||||
import com.LabWork.app.student.service.CreatorService;
|
||||
import com.LabWork.app.student.service.MangaService;
|
||||
import com.LabWork.app.student.service.ReaderService;
|
||||
import com.LabWork.app.MangaStore.model.Default.Creator;
|
||||
import com.LabWork.app.MangaStore.model.Default.Manga;
|
||||
import com.LabWork.app.MangaStore.model.Default.Reader;
|
||||
import com.LabWork.app.MangaStore.service.CreatorService;
|
||||
import com.LabWork.app.MangaStore.service.MangaService;
|
||||
import com.LabWork.app.MangaStore.service.ReaderService;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -13,8 +14,6 @@ import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@SpringBootTest
|
||||
public class ReMangaTest {
|
||||
@Autowired
|
||||
@@ -28,33 +27,52 @@ public class ReMangaTest {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(ReMangaTest.class);
|
||||
|
||||
@Test
|
||||
void testReaderRemove2() {
|
||||
readerService.deleteAllReaders();
|
||||
mangaService.deleteAllMangas();
|
||||
creatorService.deleteAllCreators();
|
||||
Creator c1 = creatorService.addCreator("first_C", "1");
|
||||
Manga m1 = mangaService.addManga(c1.getId(), 0, "Vagabond", "1");
|
||||
Manga m2 = mangaService.addManga(c1.getId(), 10, "Berserk", "1");
|
||||
Reader r1 = readerService.addReader("first_R", "1");
|
||||
|
||||
readerService.addManga(m1.getId(), r1.getId());
|
||||
readerService.addManga(m2.getId(), r1.getId());
|
||||
|
||||
Reader r11 = readerService.findReader(r1.getId());
|
||||
readerService.deleteReader(r11.getId());
|
||||
|
||||
log.info(readerService.findAllReaders().toString());
|
||||
mangaService.deleteAllMangas();
|
||||
readerService.deleteAllReaders();
|
||||
creatorService.deleteAllCreators();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAddToMangaReader2() {
|
||||
readerService.deleteAllReaders();
|
||||
mangaService.deleteAllMangas();
|
||||
creatorService.deleteAllCreators();
|
||||
Creator c1 = creatorService.addCreator("first_C", "1");
|
||||
Manga m1 = mangaService.addManga(c1, 0, "vagabond");
|
||||
Manga m1 = mangaService.addManga(c1.getId(), 0, "vagabond", "1");
|
||||
Reader r1 = readerService.addReader("first_R", "1");
|
||||
Reader r2 = readerService.addReader("2", "2");
|
||||
Reader r3 = readerService.addReader("3", "3");
|
||||
|
||||
readerService.addManga(m1, r1.getId());
|
||||
readerService.addManga(m1, r2.getId());
|
||||
readerService.addManga(m1.getId(), r1.getId());
|
||||
readerService.addManga(m1.getId(), r2.getId());
|
||||
|
||||
log.info(r1.getMangas().toString());
|
||||
|
||||
Reader r4 = readerService.findReader(r1.getId());
|
||||
log.info(r4.getMangas().toString());
|
||||
List<Reader> listReader = mangaService.getReader(m1.getId());
|
||||
log.info(listReader.toString());
|
||||
Assertions.assertEquals(true, mangaService.getReader(m1.getId()).contains(r4));
|
||||
Assertions.assertEquals(true, mangaService.getReader(m1.getId()).contains(r2));
|
||||
Assertions.assertEquals(2, mangaService.getReader(m1.getId()).size());
|
||||
Assertions.assertEquals(1, mangaService.findAllMangas().size());
|
||||
readerService.deleteAllReaders();
|
||||
mangaService.deleteAllMangas();
|
||||
creatorService.deleteAllCreators();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCreatorAddAndFind() {
|
||||
readerService.deleteAllReaders();
|
||||
@@ -65,8 +83,28 @@ public class ReMangaTest {
|
||||
Assertions.assertEquals(c1.getCreatorName(), creatorService.findCreator(c1.getId()).getCreatorName());
|
||||
Assertions.assertEquals(c1, creatorService.findCreator(c1.getId()));
|
||||
Assertions.assertEquals(2, creatorService.findAllCreators().size());
|
||||
readerService.deleteAllReaders();
|
||||
mangaService.deleteAllMangas();
|
||||
creatorService.deleteAllCreators();
|
||||
}
|
||||
|
||||
@Test
|
||||
void test() {
|
||||
readerService.deleteAllReaders();
|
||||
mangaService.deleteAllMangas();
|
||||
creatorService.deleteAllCreators();
|
||||
Creator c1 = creatorService.addCreator("first", "1");
|
||||
Manga m1 = mangaService.addManga(c1.getId(), 0, "Vagabond", "1");
|
||||
Manga m2 = mangaService.addManga(c1.getId(), 10, "Berserk", "1");
|
||||
Manga m3 = mangaService.addManga(c1.getId(), 0, "Manga_3", "1");
|
||||
Creator c2 = creatorService.findCreator(c1.getId());
|
||||
Assertions.assertEquals(3, c2.getMangas().size());
|
||||
readerService.deleteAllReaders();
|
||||
mangaService.deleteAllMangas();
|
||||
creatorService.deleteAllCreators();
|
||||
}
|
||||
|
||||
*/
|
||||
/* @Test
|
||||
void testCreatorAddManga() {
|
||||
readerService.deleteAllReaders();
|
||||
@@ -83,8 +121,10 @@ public class ReMangaTest {
|
||||
log.info(m1.getCreator().toString());
|
||||
log.info(c1.toString());
|
||||
Assertions.assertEquals(c1.getCreatorName(), m1.getCreator().getCreatorName());
|
||||
}*/
|
||||
}*//*
|
||||
|
||||
|
||||
*/
|
||||
/* //бесполезня штука
|
||||
@Test
|
||||
void testCreatorDeleteManga() {
|
||||
@@ -110,7 +150,11 @@ public class ReMangaTest {
|
||||
log.info(mangaService.findAllMangas().toString());
|
||||
|
||||
Assertions.assertEquals(1, c1.getMangas().size());
|
||||
}*/
|
||||
|
||||
}*//*
|
||||
|
||||
*/
|
||||
/*тестстим работоспособность гита*//*
|
||||
|
||||
@Test
|
||||
void testCreatorUpdated() {
|
||||
@@ -120,9 +164,13 @@ public class ReMangaTest {
|
||||
Creator c1 = creatorService.addCreator("first", "1");
|
||||
Creator c2 = creatorService.updateCreator(c1.getId(),"second", "1");
|
||||
Creator c3 = creatorService.findCreator(c1.getId());
|
||||
log.info(c3.toString());
|
||||
Assertions.assertNotEquals(c1.getCreatorName(), c2.getCreatorName());
|
||||
Assertions.assertEquals(c1.getHashedPassword(), c2.getHashedPassword());
|
||||
Assertions.assertEquals(c3.getCreatorName(), c2.getCreatorName());
|
||||
readerService.deleteAllReaders();
|
||||
mangaService.deleteAllMangas();
|
||||
creatorService.deleteAllCreators();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -135,6 +183,25 @@ public class ReMangaTest {
|
||||
creatorService.deleteCreator(c2.getId());
|
||||
log.info(creatorService.findAllCreators().toString());
|
||||
Assertions.assertEquals(1, creatorService.findAllCreators().size());
|
||||
readerService.deleteAllReaders();
|
||||
mangaService.deleteAllMangas();
|
||||
creatorService.deleteAllCreators();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCreatorMangaDelete() {
|
||||
readerService.deleteAllReaders();
|
||||
mangaService.deleteAllMangas();
|
||||
creatorService.deleteAllCreators();
|
||||
Creator c1 = creatorService.addCreator("first", "1");
|
||||
Manga m1 = mangaService.addManga(c1.getId(), 0, "Vagabond", "1");
|
||||
Manga m2 = mangaService.addManga(c1.getId(), 10, "Berserk", "1");
|
||||
creatorService.deleteCreator(c1.getId());
|
||||
log.info(mangaService.findAllMangas().toString());
|
||||
Assertions.assertEquals(0, mangaService.findAllMangas().size());
|
||||
readerService.deleteAllReaders();
|
||||
mangaService.deleteAllMangas();
|
||||
creatorService.deleteAllCreators();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -146,6 +213,28 @@ public class ReMangaTest {
|
||||
Creator c2 = creatorService.addCreator("second", "2");
|
||||
creatorService.deleteAllCreators();
|
||||
Assertions.assertEquals(0, creatorService.findAllCreators().size());
|
||||
readerService.deleteAllReaders();
|
||||
mangaService.deleteAllMangas();
|
||||
creatorService.deleteAllCreators();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCreatorDeleteMangaReader() {
|
||||
readerService.deleteAllReaders();
|
||||
mangaService.deleteAllMangas();
|
||||
creatorService.deleteAllCreators();
|
||||
Creator c1 = creatorService.addCreator("first", "1");
|
||||
Manga m1 = mangaService.addManga(c1.getId(), 0, "Vagabond", "1");
|
||||
Manga m2 = mangaService.addManga(c1.getId(), 10, "Berserk", "1");
|
||||
Reader r1 = readerService.addReader("1", "1");
|
||||
readerService.addManga(m1.getId(), r1.getId());
|
||||
readerService.addManga(m2.getId(), r1.getId());
|
||||
creatorService.deleteCreator(c1.getId());
|
||||
log.info(mangaService.findAllMangas().toString());
|
||||
Assertions.assertEquals(0, mangaService.findAllMangas().size());
|
||||
readerService.deleteAllReaders();
|
||||
mangaService.deleteAllMangas();
|
||||
creatorService.deleteAllCreators();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -154,13 +243,16 @@ public class ReMangaTest {
|
||||
mangaService.deleteAllMangas();
|
||||
creatorService.deleteAllCreators();
|
||||
Creator c1 = creatorService.addCreator("first", "1");
|
||||
Manga m1 = mangaService.addManga(c1, 0, "Vagabond");
|
||||
Manga m2 = mangaService.addManga(c1, 10, "Berserk");
|
||||
Manga m1 = mangaService.addManga(c1.getId(), 0, "Vagabond", "1");
|
||||
Manga m2 = mangaService.addManga(c1.getId(), 10, "Berserk", "1");
|
||||
c1 = creatorService.findCreator(c1.getId());
|
||||
m1 = mangaService.findManga(m1.getId());
|
||||
log.info(c1.getMangas().toString());
|
||||
Assertions.assertEquals(2, c1.getMangas().size());
|
||||
Assertions.assertEquals(c1.getCreatorName(), m1.getCreator().getCreatorName());
|
||||
readerService.deleteAllReaders();
|
||||
mangaService.deleteAllMangas();
|
||||
creatorService.deleteAllCreators();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -169,13 +261,18 @@ public class ReMangaTest {
|
||||
mangaService.deleteAllMangas();
|
||||
creatorService.deleteAllCreators();
|
||||
Creator c1 = creatorService.addCreator("first", "1");
|
||||
Manga m1 = mangaService.addManga(c1, 0, "Vagabond");
|
||||
Manga m2 = mangaService.updateManga(m1.getId(), 10);
|
||||
Manga m1 = mangaService.addManga(c1.getId(), 0, "Vagabond", "1");
|
||||
Manga m2 = mangaService.updateManga(m1.getId(), 10, "1");
|
||||
m2 = mangaService.findManga(m2.getId());
|
||||
c1 = creatorService.findCreator(c1.getId());
|
||||
log.info(m2.toString());
|
||||
Assertions.assertEquals(m1.getMangaName(), m2.getMangaName());
|
||||
Assertions.assertNotEquals(m1.getChapterCount(), m2.getChapterCount());
|
||||
Assertions.assertEquals(c1.getMangas().get(0).getChapterCount(), m2.getChapterCount());
|
||||
Assertions.assertEquals(10, m2.getChapterCount());
|
||||
readerService.deleteAllReaders();
|
||||
mangaService.deleteAllMangas();
|
||||
creatorService.deleteAllCreators();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -184,12 +281,12 @@ public class ReMangaTest {
|
||||
mangaService.deleteAllMangas();
|
||||
creatorService.deleteAllCreators();
|
||||
Creator c1 = creatorService.addCreator("first", "1");
|
||||
Manga m1 = mangaService.addManga(c1, 0, "Vagabond");
|
||||
Manga m2 = mangaService.addManga(c1, 10, "Berserk");
|
||||
Manga m1 = mangaService.addManga(c1.getId(), 0, "Vagabond", "1");
|
||||
Manga m2 = mangaService.addManga(c1.getId(), 10, "Berserk", "1");
|
||||
Reader r1 = readerService.addReader("reader1", "password1");
|
||||
|
||||
readerService.addManga(m1, r1.getId());
|
||||
readerService.addManga(m2, r1.getId());
|
||||
readerService.addManga(m1.getId(), r1.getId());
|
||||
readerService.addManga(m2.getId(), r1.getId());
|
||||
mangaService.deleteManga(m1.getId());
|
||||
r1 = readerService.findReader(r1.getId());
|
||||
|
||||
@@ -198,6 +295,9 @@ public class ReMangaTest {
|
||||
|
||||
Assertions.assertEquals(1, mangaService.findAllMangas().size());
|
||||
Assertions.assertEquals(1, r1.getMangas().size());
|
||||
readerService.deleteAllReaders();
|
||||
mangaService.deleteAllMangas();
|
||||
creatorService.deleteAllCreators();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -206,10 +306,13 @@ public class ReMangaTest {
|
||||
mangaService.deleteAllMangas();
|
||||
creatorService.deleteAllCreators();
|
||||
Creator c1 = creatorService.addCreator("first", "1");
|
||||
Manga m1 = mangaService.addManga(c1, 0, "Vagabond");
|
||||
Manga m2 = mangaService.addManga(c1, 10, "Berserk");
|
||||
Manga m1 = mangaService.addManga(c1.getId(), 0, "Vagabond", "1");
|
||||
Manga m2 = mangaService.addManga(c1.getId(), 10, "Berserk", "1");
|
||||
mangaService.deleteAllMangas();
|
||||
Assertions.assertEquals(0, mangaService.findAllMangas().size());
|
||||
readerService.deleteAllReaders();
|
||||
mangaService.deleteAllMangas();
|
||||
creatorService.deleteAllCreators();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -218,19 +321,23 @@ public class ReMangaTest {
|
||||
mangaService.deleteAllMangas();
|
||||
creatorService.deleteAllCreators();
|
||||
Creator c1 = creatorService.addCreator("first_C", "1");
|
||||
Manga m1 = mangaService.addManga(c1, 0, "vagabond");
|
||||
Manga m2 = mangaService.addManga(c1, 10, "Berserk");
|
||||
Manga m1 = mangaService.addManga(c1.getId(), 0, "vagabond", "1");
|
||||
Manga m2 = mangaService.addManga(c1.getId(), 10, "Berserk", "1");
|
||||
Reader r1 = readerService.addReader("first_R", "1");
|
||||
|
||||
readerService.addManga(m1, r1.getId());
|
||||
readerService.addManga(m2, r1.getId());
|
||||
readerService.addManga(m1.getId(), r1.getId());
|
||||
readerService.addManga(m2.getId(), r1.getId());
|
||||
|
||||
log.info(r1.getMangas().toString());
|
||||
|
||||
Reader r2 = readerService.findReader(r1.getId());
|
||||
Manga m3 = mangaService.findManga(m1.getId());
|
||||
log.info(r2.getMangas().toString());
|
||||
Assertions.assertEquals(2, r2.getMangas().size());
|
||||
Assertions.assertEquals(1, mangaService.getReader(m1.getId()).size());
|
||||
//Assertions.assertEquals(1, m3.getReaders().size());
|
||||
readerService.deleteAllReaders();
|
||||
mangaService.deleteAllMangas();
|
||||
creatorService.deleteAllCreators();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -247,6 +354,9 @@ public class ReMangaTest {
|
||||
|
||||
Reader r3 = readerService.findReader(r2.getId());
|
||||
Assertions.assertEquals(r2.getReaderName(), r3.getReaderName());
|
||||
readerService.deleteAllReaders();
|
||||
mangaService.deleteAllMangas();
|
||||
creatorService.deleteAllCreators();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -255,21 +365,24 @@ public class ReMangaTest {
|
||||
mangaService.deleteAllMangas();
|
||||
creatorService.deleteAllCreators();
|
||||
Creator c1 = creatorService.addCreator("first_C", "1");
|
||||
Manga m1 = mangaService.addManga(c1, 0, "Vagabond");
|
||||
Manga m2 = mangaService.addManga(c1, 10, "Berserk");
|
||||
Manga m1 = mangaService.addManga(c1.getId(), 0, "Vagabond", "1");
|
||||
Manga m2 = mangaService.addManga(c1.getId(), 10, "Berserk", "1");
|
||||
Reader r1 = readerService.addReader("first_R", "1");
|
||||
|
||||
readerService.addManga(m1, r1.getId());
|
||||
readerService.addManga(m2, r1.getId());
|
||||
readerService.addManga(m1.getId(), r1.getId());
|
||||
readerService.addManga(m2.getId(), r1.getId());
|
||||
|
||||
Reader r11 = readerService.findReader(r1.getId());
|
||||
readerService.removeManga(m1, r11.getId());
|
||||
readerService.removeManga(m1.getId(), r11.getId());
|
||||
Reader r12 = readerService.findReader(r11.getId());
|
||||
|
||||
Manga m11 = mangaService.findManga(m1.getId());
|
||||
Assertions.assertEquals(1, r12.getMangas().size());
|
||||
log.info(mangaService.findAllMangas().toString());
|
||||
log.info(r12.getMangas().toString());
|
||||
readerService.deleteAllReaders();
|
||||
mangaService.deleteAllMangas();
|
||||
creatorService.deleteAllCreators();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -278,12 +391,13 @@ public class ReMangaTest {
|
||||
mangaService.deleteAllMangas();
|
||||
creatorService.deleteAllCreators();
|
||||
Creator c1 = creatorService.addCreator("first_C", "1");
|
||||
Manga m1 = mangaService.addManga(c1, 0, "Vagabond");
|
||||
Manga m2 = mangaService.addManga(c1, 10, "Berserk");
|
||||
Manga m1 = mangaService.addManga(c1.getId(), 0, "Vagabond", "1");
|
||||
Manga m2 = mangaService.addManga(c1.getId(), 10, "Berserk", "1");
|
||||
Reader r1 = readerService.addReader("first_R", "1");
|
||||
Reader r2 = readerService.addReader("2_R", "2");
|
||||
|
||||
readerService.addManga(m1, r1.getId());
|
||||
readerService.addManga(m2, r1.getId());
|
||||
readerService.addManga(m1.getId(), r1.getId());
|
||||
readerService.addManga(m2.getId(), r1.getId());
|
||||
|
||||
Reader r11 = readerService.findReader(r1.getId());
|
||||
readerService.deleteReader(r11.getId());
|
||||
@@ -292,6 +406,9 @@ public class ReMangaTest {
|
||||
log.info(readerService.findAllReaders().toString());
|
||||
log.info(mangaService.getReader(m11.getId()).toString());
|
||||
Assertions.assertEquals(0, mangaService.getReader(m11.getId()).size());
|
||||
mangaService.deleteAllMangas();
|
||||
readerService.deleteAllReaders();
|
||||
creatorService.deleteAllCreators();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -300,17 +417,23 @@ public class ReMangaTest {
|
||||
mangaService.deleteAllMangas();
|
||||
creatorService.deleteAllCreators();
|
||||
Creator c1 = creatorService.addCreator("first_C", "1");
|
||||
Manga m1 = mangaService.addManga(c1, 0, "Vagabond");
|
||||
Manga m2 = mangaService.addManga(c1, 10, "Berserk");
|
||||
Manga m1 = mangaService.addManga(c1.getId(), 0, "Vagabond", "1");
|
||||
Manga m2 = mangaService.addManga(c1.getId(), 10, "Berserk", "1");
|
||||
Reader r1 = readerService.addReader("first_R", "1");
|
||||
|
||||
readerService.addManga(m1, r1.getId());
|
||||
readerService.addManga(m2, r1.getId());
|
||||
readerService.addManga(m1.getId(), r1.getId());
|
||||
readerService.addManga(m2.getId(), r1.getId());
|
||||
|
||||
Reader r11 = readerService.updateReader(r1.getId(), "reader", "password");
|
||||
r11 = readerService.findReader(r11.getId());
|
||||
m1 = mangaService.findManga(m1.getId());
|
||||
log.info(r1.getReaderName());
|
||||
log.info(r11.getReaderName());
|
||||
Assertions.assertNotEquals(r11.getReaderName(), r1.getReaderName());
|
||||
Assertions.assertEquals(r11.getReaderName(), mangaService.getReader(m1.getId()).get(0).getReaderName());
|
||||
//Assertions.assertEquals(r11.getReaderName(), m1.getReaders().get(0).getReaderName());
|
||||
readerService.deleteAllReaders();
|
||||
mangaService.deleteAllMangas();
|
||||
creatorService.deleteAllCreators();
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user