Compare commits
12 Commits
0f30ec0e19
...
LabWork06
| Author | SHA1 | Date | |
|---|---|---|---|
| 09861b425c | |||
| 02268b2b1b | |||
| cbda0f455f | |||
| 60c2564229 | |||
| 8348a9bf31 | |||
| 2918fee687 | |||
| 5ec74ef311 | |||
| a243920d42 | |||
| 809e593a86 | |||
| eada9f3c88 | |||
| d84000b5b4 | |||
| 382e4729fb |
38
.gitignore
vendored
Normal file
38
.gitignore
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
HELP.md
|
||||
.gradle
|
||||
build/
|
||||
!gradle/wrapper/gradle-wrapper.jar
|
||||
!**/src/main/**/build/
|
||||
!**/src/test/**/build/
|
||||
lab4-vue-front/node_modules
|
||||
|
||||
### STS ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
.sts4-cache
|
||||
bin/
|
||||
!**/src/main/**/bin/
|
||||
!**/src/test/**/bin/
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
out/
|
||||
!**/src/main/**/out/
|
||||
!**/src/test/**/out/
|
||||
|
||||
### NetBeans ###
|
||||
/nbproject/private/
|
||||
/nbbuild/
|
||||
/dist/
|
||||
/nbdist/
|
||||
/.nb-gradle/
|
||||
|
||||
### VS Code ###
|
||||
.vscode/
|
||||
15
build.gradle
15
build.gradle
@@ -1,6 +1,6 @@
|
||||
plugins {
|
||||
id 'java'
|
||||
id 'org.springframework.boot' version '3.0.2'
|
||||
id 'org.springframework.boot' version '2.6.3'
|
||||
id 'io.spring.dependency-management' version '1.1.0'
|
||||
}
|
||||
|
||||
@@ -14,7 +14,20 @@ repositories {
|
||||
|
||||
dependencies {
|
||||
implementation 'org.springframework.boot:spring-boot-starter-web'
|
||||
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
|
||||
implementation 'com.h2database:h2:2.1.210'
|
||||
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
|
||||
implementation 'org.springframework.boot:spring-boot-devtools'
|
||||
implementation 'nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect'
|
||||
implementation 'org.webjars:bootstrap:5.1.3'
|
||||
implementation 'org.webjars:jquery:3.6.0'
|
||||
implementation 'org.webjars:font-awesome:6.1.0'
|
||||
implementation 'org.springframework.boot:spring-boot-starter-security'
|
||||
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5'
|
||||
implementation 'com.auth0:java-jwt:4.4.0'
|
||||
implementation group: 'org.springdoc', name: 'springdoc-openapi-ui', version: '1.6.5'
|
||||
testImplementation 'org.springframework.boot:spring-boot-starter-test'
|
||||
implementation 'org.hibernate.validator:hibernate-validator'
|
||||
}
|
||||
|
||||
tasks.named('test') {
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<title>Calc</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-GLhlTQ8iRABdZLl6O3oVMWSktQOp6b7In1Zl3/Jr59b6EGGoI1aFkw7cmDA6j6gD" crossorigin="anonymous">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js" integrity="sha384-w76AqPfDkMBDXo30jS1Sgez6pr3x5MlQ1ZAGC+nuZB+EYdgRZgiwxhTBTkF7CXvN" crossorigin="anonymous"></script>
|
||||
<input type="number" class="form-control" id="num1">
|
||||
<select class="form-control" id="operation">
|
||||
<option value="sum">+</option>
|
||||
<option value="sub">-</option>
|
||||
<option value="mul">*</option>
|
||||
<option value="div">/</option>
|
||||
</select>
|
||||
<input type="number" class="form-control" id="num2">
|
||||
<button type="button" class="form-control btn btn-light" id="get-result" onclick="calc()">=</button>
|
||||
<p class="h5" id="result"></p>
|
||||
|
||||
<script>
|
||||
async function calc() {
|
||||
const num1 = document.getElementById("num1").value
|
||||
const num2 = document.getElementById("num2").value
|
||||
document.getElementById("result").innerHTML = await (await fetch(`http://127.0.0.1:8080/${document.getElementById("operation").value}?num1=${num1}&num2=${num2}`)).text()
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,12 +0,0 @@
|
||||
const fs = require('fs')
|
||||
const http = require('http')
|
||||
|
||||
const requestListener = async function (req, res) {
|
||||
res.writeHead(200);
|
||||
fs.readFile('index.html', (err, data) => {
|
||||
res.end(data);
|
||||
});
|
||||
};
|
||||
|
||||
const server = http.createServer(requestListener)
|
||||
server.listen(5050)
|
||||
9
frontend/node_modules/fs/README.md
generated
vendored
9
frontend/node_modules/fs/README.md
generated
vendored
@@ -1,9 +0,0 @@
|
||||
# Security holding package
|
||||
|
||||
This package name is not currently in use, but was formerly occupied
|
||||
by another package. To avoid malicious use, npm is hanging on to the
|
||||
package name, but loosely, and we'll probably give it to you if you
|
||||
want it.
|
||||
|
||||
You may adopt this package by contacting support@npmjs.com and
|
||||
requesting the name.
|
||||
46
frontend/node_modules/fs/package.json
generated
vendored
46
frontend/node_modules/fs/package.json
generated
vendored
@@ -1,46 +0,0 @@
|
||||
{
|
||||
"_from": "fs",
|
||||
"_id": "fs@0.0.1-security",
|
||||
"_inBundle": false,
|
||||
"_integrity": "sha512-3XY9e1pP0CVEUCdj5BmfIZxRBTSDycnbqhIOGec9QYtmVH2fbLpj86CFWkrNOkt/Fvty4KZG5lTglL9j/gJ87w==",
|
||||
"_location": "/fs",
|
||||
"_phantomChildren": {},
|
||||
"_requested": {
|
||||
"type": "tag",
|
||||
"registry": true,
|
||||
"raw": "fs",
|
||||
"name": "fs",
|
||||
"escapedName": "fs",
|
||||
"rawSpec": "",
|
||||
"saveSpec": null,
|
||||
"fetchSpec": "latest"
|
||||
},
|
||||
"_requiredBy": [
|
||||
"#USER",
|
||||
"/"
|
||||
],
|
||||
"_resolved": "https://registry.npmjs.org/fs/-/fs-0.0.1-security.tgz",
|
||||
"_shasum": "8a7bd37186b6dddf3813f23858b57ecaaf5e41d4",
|
||||
"_spec": "fs",
|
||||
"_where": "C:\\Users\\user\\Desktop\\something\\frontend",
|
||||
"author": "",
|
||||
"bugs": {
|
||||
"url": "https://github.com/npm/security-holder/issues"
|
||||
},
|
||||
"bundleDependencies": false,
|
||||
"deprecated": false,
|
||||
"description": "This package name is not currently in use, but was formerly occupied by another package. To avoid malicious use, npm is hanging on to the package name, but loosely, and we'll probably give it to you if you want it.",
|
||||
"homepage": "https://github.com/npm/security-holder#readme",
|
||||
"keywords": [],
|
||||
"license": "ISC",
|
||||
"main": "index.js",
|
||||
"name": "fs",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/npm/security-holder.git"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"version": "0.0.1-security"
|
||||
}
|
||||
9
frontend/node_modules/http/README.md
generated
vendored
9
frontend/node_modules/http/README.md
generated
vendored
@@ -1,9 +0,0 @@
|
||||
# Security holding package
|
||||
|
||||
This package name is not currently in use, but was formerly occupied
|
||||
by another package. To avoid malicious use, npm is hanging on to the
|
||||
package name, but loosely, and we'll probably give it to you if you
|
||||
want it.
|
||||
|
||||
You may adopt this package by contacting support@npmjs.com and
|
||||
requesting the name.
|
||||
39
frontend/node_modules/http/package.json
generated
vendored
39
frontend/node_modules/http/package.json
generated
vendored
@@ -1,39 +0,0 @@
|
||||
{
|
||||
"_from": "http",
|
||||
"_id": "http@0.0.1-security",
|
||||
"_inBundle": false,
|
||||
"_integrity": "sha512-RnDvP10Ty9FxqOtPZuxtebw1j4L/WiqNMDtuc1YMH1XQm5TgDRaR1G9u8upL6KD1bXHSp9eSXo/ED+8Q7FAr+g==",
|
||||
"_location": "/http",
|
||||
"_phantomChildren": {},
|
||||
"_requested": {
|
||||
"type": "tag",
|
||||
"registry": true,
|
||||
"raw": "http",
|
||||
"name": "http",
|
||||
"escapedName": "http",
|
||||
"rawSpec": "",
|
||||
"saveSpec": null,
|
||||
"fetchSpec": "latest"
|
||||
},
|
||||
"_requiredBy": [
|
||||
"#USER",
|
||||
"/"
|
||||
],
|
||||
"_resolved": "https://registry.npmjs.org/http/-/http-0.0.1-security.tgz",
|
||||
"_shasum": "3aac09129d12dc2747bbce4157afde20ad1f7995",
|
||||
"_spec": "http",
|
||||
"_where": "C:\\Users\\user\\Desktop\\something\\frontend",
|
||||
"bugs": {
|
||||
"url": "https://github.com/npm/security-holder/issues"
|
||||
},
|
||||
"bundleDependencies": false,
|
||||
"deprecated": false,
|
||||
"description": "security holding package",
|
||||
"homepage": "https://github.com/npm/security-holder#readme",
|
||||
"name": "http",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/npm/security-holder.git"
|
||||
},
|
||||
"version": "0.0.1-security"
|
||||
}
|
||||
18
frontend/package-lock.json
generated
18
frontend/package-lock.json
generated
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"name": "frontend",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
"fs": {
|
||||
"version": "0.0.1-security",
|
||||
"resolved": "https://registry.npmjs.org/fs/-/fs-0.0.1-security.tgz",
|
||||
"integrity": "sha512-3XY9e1pP0CVEUCdj5BmfIZxRBTSDycnbqhIOGec9QYtmVH2fbLpj86CFWkrNOkt/Fvty4KZG5lTglL9j/gJ87w=="
|
||||
},
|
||||
"http": {
|
||||
"version": "0.0.1-security",
|
||||
"resolved": "https://registry.npmjs.org/http/-/http-0.0.1-security.tgz",
|
||||
"integrity": "sha512-RnDvP10Ty9FxqOtPZuxtebw1j4L/WiqNMDtuc1YMH1XQm5TgDRaR1G9u8upL6KD1bXHSp9eSXo/ED+8Q7FAr+g=="
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
{
|
||||
"name": "frontend",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"dev": "node index.js"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"fs": "0.0.1-security",
|
||||
"http": "0.0.1-security"
|
||||
}
|
||||
}
|
||||
19
lab4-vue-front/README.md
Normal file
19
lab4-vue-front/README.md
Normal file
@@ -0,0 +1,19 @@
|
||||
# lab4-vue-front
|
||||
|
||||
## Project setup
|
||||
```
|
||||
npm install
|
||||
```
|
||||
|
||||
### Compiles and hot-reloads for development
|
||||
```
|
||||
npm run serve
|
||||
```
|
||||
|
||||
### Compiles and minifies for production
|
||||
```
|
||||
npm run build
|
||||
```
|
||||
|
||||
### Customize configuration
|
||||
See [Configuration Reference](https://cli.vuejs.org/config/).
|
||||
5
lab4-vue-front/babel.config.js
Normal file
5
lab4-vue-front/babel.config.js
Normal file
@@ -0,0 +1,5 @@
|
||||
module.exports = {
|
||||
presets: [
|
||||
'@vue/cli-plugin-babel/preset'
|
||||
]
|
||||
}
|
||||
19
lab4-vue-front/jsconfig.json
Normal file
19
lab4-vue-front/jsconfig.json
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"module": "esnext",
|
||||
"baseUrl": "./",
|
||||
"moduleResolution": "node",
|
||||
"paths": {
|
||||
"@/*": [
|
||||
"src/*"
|
||||
]
|
||||
},
|
||||
"lib": [
|
||||
"esnext",
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"scripthost"
|
||||
]
|
||||
}
|
||||
}
|
||||
17576
lab4-vue-front/package-lock.json
generated
Normal file
17576
lab4-vue-front/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
29
lab4-vue-front/package.json
Normal file
29
lab4-vue-front/package.json
Normal file
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"name": "lab4_vue_front",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"serve": "vue-cli-service serve",
|
||||
"build": "vue-cli-service build"
|
||||
},
|
||||
"dependencies": {
|
||||
"@popperjs/core": "^2.11.7",
|
||||
"axios": "^1.3.4",
|
||||
"core-js": "^3.8.3",
|
||||
"vue": "^3.2.13",
|
||||
"vue-router": "^4.1.6",
|
||||
"vuex": "^4.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vue/cli-plugin-babel": "~5.0.0",
|
||||
"@vue/cli-plugin-router": "~5.0.0",
|
||||
"@vue/cli-plugin-vuex": "~5.0.0",
|
||||
"@vue/cli-service": "~5.0.0"
|
||||
},
|
||||
"browserslist": [
|
||||
"> 1%",
|
||||
"last 2 versions",
|
||||
"not dead",
|
||||
"not ie 11"
|
||||
]
|
||||
}
|
||||
18
lab4-vue-front/public/index.html
Normal file
18
lab4-vue-front/public/index.html
Normal file
@@ -0,0 +1,18 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
|
||||
<link rel="stylesheet" href="https://unpkg.com/chota@latest">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<script src="https://code.jquery.com/jquery-3.7.0.slim.min.js" integrity="sha256-tG5mcZUtJsZvyKAxYLVXrmjKBVLd6VpVccqz/r4ypFE=" crossorigin="anonymous"></script>
|
||||
<title>LabWork04 - Social Network</title>
|
||||
</head>
|
||||
<body>
|
||||
<script src="https://unpkg.com/@popperjs/core@2"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-OERcA2EqjJCMA+/3y+gxIOqMEjwtxJY7qPCqsdltbNJuaOe923+mo//f6V8Qbsw3" crossorigin="anonymous"></script>
|
||||
<div id="app"></div>
|
||||
</body>
|
||||
</html>
|
||||
75
lab4-vue-front/src/App.vue
Normal file
75
lab4-vue-front/src/App.vue
Normal file
@@ -0,0 +1,75 @@
|
||||
<template>
|
||||
<div class="nav row">
|
||||
<div class="nav-left">
|
||||
<router-link to="/customers" v-bind:class="'button primary ' + this.$route.path.startsWith('/customers')? 'clear' : ''">Профили</router-link>
|
||||
<router-link to="/feed" v-bind:class="'button primary ' + this.$route.path.startsWith('/feed')? 'clear' : ''">Посты</router-link>
|
||||
<router-link v-if="role === 'ADMIN'" to="/admin" v-bind:class="'button primary ' + this.$route.path.startsWith('/admin')? 'clear' : ''">Администрирование</router-link>
|
||||
<router-link v-if="this.token_value == null" to="/login" v-bind:class="'button primary ' + this.$route.path.startsWith('/login')? 'clear' : ''">Вход</router-link>
|
||||
<button v-if="this.token_value != null" v-on:click="logout()" v-bind:class="'button primary ' + this.$route.path.startsWith('/signup')? 'clear' : ''">Выход</button>
|
||||
</div>
|
||||
</div>
|
||||
<router-view></router-view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
token_value: localStorage.getItem("token"),
|
||||
role: ''
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
logout() {
|
||||
this.token = null
|
||||
this.role = ''
|
||||
localStorage.clear()
|
||||
this.$router.push('login')
|
||||
},
|
||||
async actualRole() {
|
||||
let response = await fetch(
|
||||
"http://localhost:8080/api/1.0/customer/role/" + localStorage.getItem("token"),
|
||||
{
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Authorization": "Bearer " + localStorage.getItem("token")
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
this.role = await response.text()
|
||||
localStorage.setItem("role", this.role)
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
token: {
|
||||
get: function() {
|
||||
return this.token_value
|
||||
},
|
||||
set: function(value) {
|
||||
this.token_value = value
|
||||
localStorage.setItem("token", value)
|
||||
localStorage.setItem("role", this.role)
|
||||
}
|
||||
}
|
||||
},
|
||||
async beforeMount() {
|
||||
if (localStorage.getItem("token") === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
await this.actualRole()
|
||||
|
||||
const component = this
|
||||
|
||||
document.addEventListener('token_changed', async function() {
|
||||
component.token = localStorage.getItem("token")
|
||||
await component.actualRole()
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="">
|
||||
|
||||
</style>
|
||||
223
lab4-vue-front/src/components/Admin.vue
Normal file
223
lab4-vue-front/src/components/Admin.vue
Normal file
@@ -0,0 +1,223 @@
|
||||
<template>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="row mb-5">
|
||||
<p class='is-center h2'>Профили</p>
|
||||
</div>
|
||||
<div class="row mb-5">
|
||||
<div class="col"></div>
|
||||
<div class="col-10 is-center">
|
||||
<button class="button primary" data-bs-toggle="modal" data-bs-target="#customerCreate">
|
||||
Добавить нового пользователя
|
||||
</button>
|
||||
</div>
|
||||
<div class="col"></div>
|
||||
</div>
|
||||
|
||||
<p class='h3 is-center row mb-5'>Список профилей</p>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="row card mb-3">
|
||||
<div class="row">
|
||||
<div class="col-3 is-left h3 fw-bold">ID</div>
|
||||
<div class="col-3 is-center h3 fw-bold">Никнейм</div>
|
||||
<div class="col-3 is-right h3 fw-bold">Пароль</div>
|
||||
<div class="col-2"></div>
|
||||
<div class="col-1"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-for="customer in customers" class="row card mb-3">
|
||||
<div class="row">
|
||||
<div class="col-3 is-left h3">{{ customer.id }}</div>
|
||||
<router-link :to="{name: 'Customers', params: {'id': customer.id}}" class="col-3 is-center h3">{{ customer.username }}</router-link>
|
||||
<div class="col-3 is-right h3">
|
||||
<span style="text-overflow: ellipsis; overflow: hidden; max-width: 10ch; white-space: nowrap">
|
||||
{{ customer.password }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<button style="max-width: 66px; max-height: 38px;" v-on:click="prepareEditModal(customer)" class="button primary outline is-right" data-bs-toggle="modal" data-bs-target="#customerEdit">
|
||||
<i class="fa fa-pencil" aria-hidden="true">
|
||||
</i>
|
||||
</button>
|
||||
|
||||
<div class="col-1 is-right">
|
||||
<button class="button dark outline is-right" v-on:click="deleteCustomer(customer)" style="max-width: 66px; max-height: 38px;">
|
||||
<i class="fa fa-trash" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal fade" id="customerCreate" tabindex="-1" role="dialog" aria-labelledby="customerCreateLabel" aria-hidden="true">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="customerCreateLabel">Создать профиль</h5>
|
||||
</div>
|
||||
<div class="modal-body text-center">
|
||||
<p>Логин</p>
|
||||
<textarea name="username" v-model="newCustomer.username" cols="30" rows="1"></textarea>
|
||||
<p>Пароль</p>
|
||||
<textarea name="password" v-model="newCustomer.password" id="passwordTextC" cols="30" rows="1"></textarea>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Закрыть</button>
|
||||
<button type="button" v-on:click="createCustomer()" class="btn btn-primary" data-bs-dismiss="modal">Сохранить</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal fade" id="customerEdit" tabindex="-1" role="dialog" aria-labelledby="customerEditLabel" aria-hidden="true">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content" id="edit-customer-form">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="customerEditLabel">Редактировать профиль</h5>
|
||||
</div>
|
||||
<div class="modal-body text-center">
|
||||
<p>Логин</p>
|
||||
<textarea name="username" v-model="editedCustomer.username" cols="30" rows="1"></textarea>
|
||||
<p>Пароль</p>
|
||||
<textarea name="password" v-model="editedCustomer.password" cols="30" rows="1"></textarea>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Закрыть</button>
|
||||
<button type="button" v-on:click="editCustomer()" class="btn btn-primary" data-bs-dismiss="modal">Изменить</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
editedCustomer: {
|
||||
id: -1,
|
||||
username: '',
|
||||
password: ''
|
||||
},
|
||||
newCustomer: {
|
||||
id: -1,
|
||||
username: '',
|
||||
password: ''
|
||||
},
|
||||
customers: [],
|
||||
currentCustomerId: -1
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async updateCustomers() {
|
||||
const response = await fetch(
|
||||
"http://localhost:8080/api/1.0/customer",
|
||||
{
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Authorization": "Bearer " + localStorage.getItem("token")
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
this.customers = await response.json()
|
||||
},
|
||||
async createCustomer() {
|
||||
const response = await fetch(
|
||||
"http://localhost:8080/api/1.0/customer",
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
"Authorization": "Bearer " + localStorage.getItem("token")
|
||||
},
|
||||
body: JSON.stringify({
|
||||
"username": this.newCustomer.username,
|
||||
"password": this.newCustomer.password
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
await this.updateCustomers()
|
||||
},
|
||||
async editCustomer() {
|
||||
const response = await fetch(
|
||||
"http://localhost:8080/api/1.0/customer/" + this.editedCustomer.id,
|
||||
{
|
||||
method: "PUT",
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
"Authorization": "Bearer " + localStorage.getItem("token")
|
||||
},
|
||||
body: JSON.stringify({
|
||||
"username": this.editedCustomer.username,
|
||||
"password": this.editedCustomer.password
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
if (this.currentCustomerId == this.editedCustomer.id) {
|
||||
localStorage.clear()
|
||||
document.dispatchEvent(new Event("token_changed"))
|
||||
this.$router.replace("login")
|
||||
}
|
||||
|
||||
await this.updateCustomers()
|
||||
},
|
||||
async deleteCustomer(customer) {
|
||||
const response = await fetch(
|
||||
"http://localhost:8080/api/1.0/customer/" + customer.id,
|
||||
{
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Authorization": "Bearer " + localStorage.getItem("token")
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
if (this.currentCustomerId == this.editedCustomer.id) {
|
||||
localStorage.clear()
|
||||
document.dispatchEvent(new Event("token_changed"))
|
||||
this.$router.replace("login")
|
||||
}
|
||||
|
||||
await this.updateCustomers()
|
||||
},
|
||||
async prepareEditModal(customer) {
|
||||
this.editedCustomer.username = customer.username
|
||||
this.editedCustomer.id = customer.id
|
||||
},
|
||||
async getCurrentCustomer() {
|
||||
const response = await fetch(
|
||||
"http://localhost:8080/api/1.0/customer/me",
|
||||
{
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Authorization": "Bearer " + localStorage.getItem("token")
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
this.currentCustomerId = (await response.json())['id']
|
||||
}
|
||||
},
|
||||
async beforeMount() {
|
||||
if (localStorage.getItem("role") !== "ADMIN") {
|
||||
this.$router.replace("login")
|
||||
}
|
||||
if (!localStorage.getItem("token") === null) {
|
||||
this.$router.replace("login")
|
||||
}
|
||||
|
||||
await this.getCurrentCustomer()
|
||||
|
||||
await this.updateCustomers()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
163
lab4-vue-front/src/components/Customers.vue
Normal file
163
lab4-vue-front/src/components/Customers.vue
Normal file
@@ -0,0 +1,163 @@
|
||||
<template>
|
||||
<div class="ms-5">
|
||||
<p class='h4 m-3'>Пользователи</p>
|
||||
|
||||
<p class='h4 ms-3'>Список</p>
|
||||
<div class="row">
|
||||
<div class="col-5">
|
||||
<div v-for="customer in customers" class="row card is-left mb-3">
|
||||
<div class="row is-left">
|
||||
<div class="row is-left h3">{{ customer['username'] }}</div>
|
||||
</div>
|
||||
<p class="row is-left"></p>
|
||||
|
||||
<div class="row" v-if="customer['posts'].length !== 0">
|
||||
<p class="h4">Посты:</p>
|
||||
<div class="col">
|
||||
<div v-for="post in customer['posts']" class="row is-left card mb-3">
|
||||
<div class="row is-left h4">{{ post['title'] }}</div>
|
||||
<div class="row is-left h5">{{ post['content'] }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="row is-left" v-if="customer['comments'].length !== 0">
|
||||
<p class="h4">Комментарии:</p>
|
||||
<div class="col">
|
||||
<div v-for="comment in customer['comments']" class="row is-left card mb-3">
|
||||
<div class="row is-left h5">"{{ comment['content'] }}" - к посту '{{ comment['postTitle'] }}' от пользователя {{ comment['postAuthor'] }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row" v-if="currentCustomerId === customer['id']">
|
||||
<button v-on:click="deleteCustomer(customer)" class="col button dark outline">Удалить</button>
|
||||
<button v-on:click="prepareEditModal(customer)" class="col button primary outline" data-bs-toggle="modal" data-bs-target="#customerEdit">Редактировать</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal -->
|
||||
<div class="modal fade" id="customerEdit" tabindex="-1" role="dialog" aria-labelledby="customerEditLabel" aria-hidden="true">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content" id="edit-customer-form">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="customerEditLabel">Изменение пользователя</h5>
|
||||
</div>
|
||||
<div class="modal-body text-center">
|
||||
<p>Имя пользователя</p>
|
||||
<textarea name="username" id="usernameTextE" cols="30" rows="1"></textarea>
|
||||
<p>Пароль</p>
|
||||
<textarea name="password" id="passwordTextE" cols="30" rows="1"></textarea>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Отмена</button>
|
||||
<button v-on:click="editCustomer()" type="button" class="btn btn-primary">Применить</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
customers: [],
|
||||
currentCustomerId: -1
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async updateCustomers() {
|
||||
if (!this.$route.params.id) {
|
||||
const response = await fetch(
|
||||
"http://localhost:8080/api/1.0/customer",
|
||||
{
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Authorization": "Bearer " + localStorage.getItem("token")
|
||||
}
|
||||
}
|
||||
)
|
||||
this.customers = await response.json()
|
||||
}
|
||||
else {
|
||||
const response = await fetch(
|
||||
"http://localhost:8080/api/1.0/customer/" + this.$route.params.id,
|
||||
{
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Authorization": "Bearer " + localStorage.getItem("token")
|
||||
}
|
||||
}
|
||||
)
|
||||
this.customers = [await response.json()]
|
||||
}
|
||||
},
|
||||
async getCurrentCustomer() {
|
||||
const response = await fetch(
|
||||
"http://localhost:8080/api/1.0/customer/me",
|
||||
{
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Authorization": "Bearer " + localStorage.getItem("token")
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
this.currentCustomerId = (await response.json())['id']
|
||||
},
|
||||
prepareEditModal(customer) {
|
||||
$("#usernameTextE").val(customer['username'])
|
||||
},
|
||||
async deleteCustomer() {
|
||||
await fetch(
|
||||
"http://localhost:8080/api/1.0/customer/" + this.currentCustomerId,
|
||||
{
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Authorization": "Bearer " + localStorage.getItem("token")
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
localStorage.clear()
|
||||
this.$router.push("login")
|
||||
},
|
||||
async editCustomer() {
|
||||
await fetch(
|
||||
"http://localhost:8080/api/1.0/customer/" + this.currentCustomerId,
|
||||
{
|
||||
method: "PUT",
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
"Authorization": "Bearer " + localStorage.getItem("token")
|
||||
},
|
||||
body: JSON.stringify({
|
||||
"username": $("#usernameTextE").val(),
|
||||
"password": $("#passwordTextE").val()
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
localStorage.clear()
|
||||
|
||||
this.$router.replace("login")
|
||||
}
|
||||
},
|
||||
async beforeMount() {
|
||||
if (localStorage.getItem("token") == null) {
|
||||
this.$router.push("login")
|
||||
}
|
||||
|
||||
await Promise.all([this.updateCustomers(), this.getCurrentCustomer()])
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="">
|
||||
|
||||
</style>
|
||||
324
lab4-vue-front/src/components/Feed.vue
Normal file
324
lab4-vue-front/src/components/Feed.vue
Normal file
@@ -0,0 +1,324 @@
|
||||
<template>
|
||||
<div class="ms-5">
|
||||
<p class='is-left h2'>Посты</p>
|
||||
|
||||
<div class="col-5">
|
||||
<form class="row is-left mb-5" @submit.prevent="searchPosts">
|
||||
<input type="search" v-model="searchQuery" class="col-7">
|
||||
<button class="button primary col-3" type="submit">Поиск</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="row is-left mb-5" v-if="currentCustomerId > 0">
|
||||
<form class="col-7" @submit.prevent="createPost">
|
||||
<input name="customerId" type="text" style="display: none;" :value="currentCustomerId">
|
||||
<div class="row is-left">
|
||||
<p class="col-2 is-left mb-2">Заголовок:</p>
|
||||
<input name="title" type="text" class="col-5" v-model="newPost.title" />
|
||||
</div>
|
||||
<div class="row is-left">
|
||||
<p class="col-2 is-left mb-2">Текст:</p>
|
||||
<textarea name="content" class="col-5" v-model="newPost.content"></textarea>
|
||||
</div>
|
||||
<div class="row is-left">
|
||||
<button type='submit' class="button primary col-7">
|
||||
Опубликовать
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div v-if="posts.length > 0" class="row">
|
||||
<div class="col-5">
|
||||
<div class="row mb-5 card" v-for="post in posts" :key="post.id">
|
||||
<div class="col">
|
||||
<div class="row h3">
|
||||
<div class="col is-left">
|
||||
<p>Автор: <a :href="'/customers/' + post.customerId" class="text-primary">{{ post.customerName }}</a></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row is-left text-left">
|
||||
<span class="h2">{{ post.title }}</span>
|
||||
<span class="h3">{{ post.content }}</span>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<p class="row h3 is-left my-2">Комментарии</p>
|
||||
<div v-if="post.comments.length > 0" class="row text-left mb-5 card" v-for="comment in post.comments" :key="comment.id">
|
||||
<div class="row is-left">
|
||||
<span class="h2 text-primary">{{ comment.customerName }}</span>
|
||||
<span class="h3">{{ comment.content }}</span>
|
||||
</div>
|
||||
|
||||
<div v-if="currentCustomerId === comment.customerId" class="row">
|
||||
<button @click="prepareCommentEditModal(comment)" class="button primary outline col" data-bs-toggle="modal" data-bs-target="#commentEdit">Изменить</button>
|
||||
<form @submit.prevent="deleteComment(comment.id)" class="col">
|
||||
<button type="submit" class="button error is-full-width">Удалить</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<p v-else class="h3 row is-center mb-5">Пусто</p>
|
||||
<form class="row" v-if="currentCustomerId !== -1" @submit.prevent="addComment(post.id)">
|
||||
<input name="content" type="text" class="col-7" v-model="newComment.content"/>
|
||||
<input name="customerId" type="text" style="display: none;" :value="currentCustomerId">
|
||||
<input name="postId" type="text" style="display: none;" :value="post.id">
|
||||
<button type="submit" class="button col-5 secondary outline">Комментировать</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row" v-if="currentCustomerId === post.customerId">
|
||||
<form class="col" @submit.prevent="deletePost(post.id)">
|
||||
<button type="submit" class="is-full-width button dark outline">Удалить</button>
|
||||
</form>
|
||||
<button @click="preparePostEditModal(post)" class="col button primary outline" data-bs-toggle="modal" data-bs-target="#postEdit">Изменить</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Modal -->
|
||||
<div class="modal fade" id="postEdit" tabindex="-1" role="dialog" aria-labelledby="postEditLabel" aria-hidden="true">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="postEditLabel">Редактировать пост</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body text-center">
|
||||
<form @submit.prevent="updatePost">
|
||||
<input type="hidden" name="postId" v-model="editedPost.id">
|
||||
<div class="form-group">
|
||||
<label for="postEditTitle">Заголовок</label>
|
||||
<input type="text" class="form-control" id="postEditTitle" v-model="editedPost.title">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="postEditContent">Содержание</label>
|
||||
<textarea class="form-control" id="postEditContent" rows="5" v-model="editedPost.content"></textarea>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Измененить</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal -->
|
||||
<div class="modal fade" id="commentEdit" tabindex="-1" role="dialog" aria-labelledby="commentEditLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="commentEditLabel">Изменить комментарий</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body text-center">
|
||||
<form @submit.prevent="updateComment">
|
||||
<input type="hidden" name="commentId" v-model="editedComment.id">
|
||||
<div class="form-group">
|
||||
<label for="commentEditContent">Содержание</label>
|
||||
<textarea class="form-control" id="commentEditContent" rows="5" v-model="editedComment.content"></textarea>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Сохранить</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
posts: [],
|
||||
currentCustomerId: -1,
|
||||
searchQuery: '',
|
||||
newPost: {
|
||||
title: '',
|
||||
content: ''
|
||||
},
|
||||
newComment: {
|
||||
content: ''
|
||||
},
|
||||
editedPost: {
|
||||
id: -1,
|
||||
title: '',
|
||||
content: ''
|
||||
},
|
||||
editedComment: {
|
||||
id: -1,
|
||||
content: ''
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async updatePosts() {
|
||||
const response = await fetch(
|
||||
"http://localhost:8080/api/1.0/post",
|
||||
{
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Authorization": "Bearer " + localStorage.getItem("token")
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
this.posts = await response.json()
|
||||
},
|
||||
async searchPosts() {
|
||||
if (this.searchQuery) {
|
||||
const response = await fetch(
|
||||
"http://localhost:8080/api/1.0/post/search?query=" + this.searchQuery,
|
||||
{
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Authorization": "Bearer " + localStorage.getItem("token")
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
this.posts = await response.json()
|
||||
} else {
|
||||
await this.updatePosts()
|
||||
}
|
||||
},
|
||||
async createPost() {
|
||||
const response = await fetch(
|
||||
"http://localhost:8080/api/1.0/post",
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
"Authorization": "Bearer " + localStorage.getItem("token")
|
||||
},
|
||||
body: JSON.stringify({
|
||||
"customerId": this.currentCustomerId,
|
||||
"title": this.newPost.title,
|
||||
"content": this.newPost.content
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
await this.updatePosts()
|
||||
},
|
||||
async updatePost() {
|
||||
const response = await fetch(
|
||||
"http://localhost:8080/api/1.0/post/" + this.editedPost.id,
|
||||
{
|
||||
method: "PUT",
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
"Authorization": "Bearer " + localStorage.getItem("token")
|
||||
},
|
||||
body: JSON.stringify({
|
||||
"title": this.editedPost.title,
|
||||
"content": this.editedPost.content,
|
||||
"customerId": this.currentCustomerId
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
await this.updatePosts()
|
||||
},
|
||||
async deletePost(postId) {
|
||||
const response = await fetch(
|
||||
"http://localhost:8080/api/1.0/post/" + postId,
|
||||
{
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Authorization": "Bearer " + localStorage.getItem("token")
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
await this.updatePosts()
|
||||
},
|
||||
async addComment(postId) {
|
||||
const response = await fetch(
|
||||
"http://localhost:8080/api/1.0/comment",
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
"Authorization": "Bearer " + localStorage.getItem("token")
|
||||
},
|
||||
body: JSON.stringify({
|
||||
"postId": postId,
|
||||
"content": this.newComment.content,
|
||||
"customerId": this.currentCustomerId
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
await this.updatePosts()
|
||||
},
|
||||
async updateComment() {
|
||||
const response = await fetch(
|
||||
"http://localhost:8080/api/1.0/comment/" + this.editedComment.id,
|
||||
{
|
||||
method: "PUT",
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
"Authorization": "Bearer " + localStorage.getItem("token")
|
||||
},
|
||||
body: JSON.stringify({
|
||||
"content": this.editedComment.content
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
await this.updatePosts()
|
||||
},
|
||||
async deleteComment(commentId) {
|
||||
const response = await fetch(
|
||||
"http://localhost:8080/api/1.0/comment/" + commentId,
|
||||
{
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Authorization": "Bearer " + localStorage.getItem("token")
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
await this.updatePosts()
|
||||
},
|
||||
prepareCommentEditModal(comment) {
|
||||
this.editedComment.id = comment.id
|
||||
this.editedComment.content = comment.content
|
||||
},
|
||||
preparePostEditModal(post) {
|
||||
this.editedPost.id = post.id
|
||||
this.editedPost.title = post.title
|
||||
this.editedPost.content = post.content
|
||||
},
|
||||
async getCurrentCustomer() {
|
||||
const response = await fetch(
|
||||
"http://localhost:8080/api/1.0/customer/me",
|
||||
{
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Authorization": "Bearer " + localStorage.getItem("token")
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
this.currentCustomerId = (await response.json()).id
|
||||
}
|
||||
},
|
||||
async beforeMount() {
|
||||
if (localStorage.getItem("token") == null) {
|
||||
this.$router.push("login")
|
||||
}
|
||||
|
||||
await Promise.all([this.updatePosts(), this.getCurrentCustomer()])
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
64
lab4-vue-front/src/components/Login.vue
Normal file
64
lab4-vue-front/src/components/Login.vue
Normal file
@@ -0,0 +1,64 @@
|
||||
<template>
|
||||
<div class="row">
|
||||
<div class="col"></div>
|
||||
<div class="col-6">
|
||||
<div class="row mb-3">
|
||||
<label>Имя пользователя: <input type="text" id="username" required /></label>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<label>Пароль: <input type="password" id="password" required /></label>
|
||||
</div>
|
||||
<div class="row mb-3 alert alert-danger" v-if="error !== null" role="alert">
|
||||
{{ error }}
|
||||
</div>
|
||||
<div class="row mt-3">
|
||||
<div class="col-4"></div>
|
||||
<button v-on:click="login()" type="button" class="button primary col" id="enter">Войти</button>
|
||||
<div class="col-4"></div>
|
||||
</div>
|
||||
<div class="row mt-5">
|
||||
<router-link to="/signup" class="button primary outline">Регистрация</router-link>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
error: null
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async login() {
|
||||
let response = await fetch(
|
||||
"http://localhost:8080/api/1.0/customer/jwt/login",
|
||||
{
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
"username": $("#username").val(),
|
||||
"password": $("#password").val()
|
||||
}),
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
if (response.status !== 200) {
|
||||
this.error = await response.text()
|
||||
} else {
|
||||
localStorage.setItem("token", await response.text())
|
||||
document.dispatchEvent(new Event("token_changed"))
|
||||
this.$router.push("feed")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
69
lab4-vue-front/src/components/Signup.vue
Normal file
69
lab4-vue-front/src/components/Signup.vue
Normal file
@@ -0,0 +1,69 @@
|
||||
<template>
|
||||
<div class="row">
|
||||
<div class="col"></div>
|
||||
<div class="col-6">
|
||||
<div class="row">
|
||||
<label>Имя пользователя: <input type="text" id="username" required /></label>
|
||||
</div>
|
||||
<div class="row">
|
||||
<label>Пароль: <input v-on:change="check()" type="password" id="password" required /></label>
|
||||
</div>
|
||||
<div class="row">
|
||||
<label>Пароль повторно: <input v-on:change="check()" type="password" id="confirm-password" required /></label>
|
||||
</div>
|
||||
<div class="row mb-3 alert alert-danger" v-if="error !== null" role="alert">
|
||||
{{ error }}
|
||||
</div>
|
||||
<div class="row mt-3">
|
||||
<div class="col-4"></div>
|
||||
<button v-on:click="register()" class="button primary col" id="enter" disabled>Регистрация</button>
|
||||
<div class="col-4"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
error: null
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
check() {
|
||||
if ($("#password").val() === $("#confirm-password").val()) {
|
||||
$("#enter").removeAttr("disabled")
|
||||
} else {
|
||||
$("#enter").attr("disabled", "disabled")
|
||||
}
|
||||
},
|
||||
async register() {
|
||||
const response = await fetch(
|
||||
"http://localhost:8080/api/1.0/customer",
|
||||
{
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
"username": $("#username").val(),
|
||||
"password": $("#password").val()
|
||||
}),
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
if (response.status !== 200) {
|
||||
this.error = await response.text()
|
||||
} else {
|
||||
this.$router.push("login")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
45
lab4-vue-front/src/main.js
Normal file
45
lab4-vue-front/src/main.js
Normal file
@@ -0,0 +1,45 @@
|
||||
import {createApp} from 'vue'
|
||||
import App from './App'
|
||||
import { createRouter, createWebHistory } from "vue-router"
|
||||
|
||||
import Customers from './components/Customers'
|
||||
import Feed from './components/Feed'
|
||||
import Login from "@/components/Login.vue";
|
||||
import Signup from "@/components/Signup.vue";
|
||||
import Admin from "@/components/Admin.vue";
|
||||
|
||||
const routes = [
|
||||
{
|
||||
path: '/customers/:id?',
|
||||
name: "Customers",
|
||||
component: Customers
|
||||
},
|
||||
{
|
||||
path: '/feed',
|
||||
name: "Feed",
|
||||
component: Feed
|
||||
},
|
||||
{
|
||||
path: '/login',
|
||||
name: "Login",
|
||||
component: Login
|
||||
},
|
||||
{
|
||||
path: '/signup',
|
||||
name: "Signup",
|
||||
component: Signup
|
||||
},
|
||||
{
|
||||
path: '/admin',
|
||||
name: "Admin",
|
||||
component: Admin
|
||||
}
|
||||
]
|
||||
|
||||
const router = createRouter({
|
||||
routes,
|
||||
history: createWebHistory()
|
||||
})
|
||||
|
||||
|
||||
createApp(App).use(router).mount('#app')
|
||||
4
lab4-vue-front/vue.config.js
Normal file
4
lab4-vue-front/vue.config.js
Normal file
@@ -0,0 +1,4 @@
|
||||
const { defineConfig } = require('@vue/cli-service')
|
||||
module.exports = defineConfig({
|
||||
transpileDependencies: true
|
||||
})
|
||||
94
src/main/java/ru/ulstu/is/labwork/Lab4/DTO/CommentDto.java
Normal file
94
src/main/java/ru/ulstu/is/labwork/Lab4/DTO/CommentDto.java
Normal file
@@ -0,0 +1,94 @@
|
||||
package ru.ulstu.is.labwork.Lab4.DTO;
|
||||
|
||||
import ru.ulstu.is.labwork.Lab4.model.Comment;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
public class CommentDto {
|
||||
public long id;
|
||||
public String content;
|
||||
public long customerId;
|
||||
public String customerName;
|
||||
public long postId;
|
||||
public String postTitle;
|
||||
public String postAuthor;
|
||||
public long postAuthorId;
|
||||
|
||||
public CommentDto() {
|
||||
|
||||
}
|
||||
|
||||
public CommentDto(Comment comment) {
|
||||
this.id = comment.getId();
|
||||
this.content = comment.getContent();
|
||||
this.customerId = comment.getCustomer().getId();
|
||||
this.customerName = comment.getCustomer().getUsername();
|
||||
this.postId = comment.getPost().getId();
|
||||
this.postTitle = comment.getPost().getTitle();
|
||||
this.postAuthor = comment.getPost().getCustomer().getUsername();
|
||||
this.postAuthorId = comment.getPost().getCustomer().getId();
|
||||
}
|
||||
|
||||
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
public void setContent(String content) {
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
public long getCustomerId() {
|
||||
return customerId;
|
||||
}
|
||||
|
||||
public void setCustomerId(long customerId) {
|
||||
this.customerId = customerId;
|
||||
}
|
||||
|
||||
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
|
||||
public String getCustomerName() {
|
||||
return customerName;
|
||||
}
|
||||
|
||||
public long getPostId() {
|
||||
return postId;
|
||||
}
|
||||
|
||||
public void setPostId(long postId) {
|
||||
this.postId = postId;
|
||||
}
|
||||
|
||||
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
|
||||
public String getPostTitle() {
|
||||
return postTitle;
|
||||
}
|
||||
|
||||
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
|
||||
public String getPostAuthor() {
|
||||
return postAuthor;
|
||||
}
|
||||
|
||||
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
|
||||
public long getPostAuthorId() {
|
||||
return postAuthorId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
CommentDto that = (CommentDto) o;
|
||||
|
||||
return id == that.id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return (int) (id ^ (id >>> 32));
|
||||
}
|
||||
}
|
||||
56
src/main/java/ru/ulstu/is/labwork/Lab4/DTO/CustomerDto.java
Normal file
56
src/main/java/ru/ulstu/is/labwork/Lab4/DTO/CustomerDto.java
Normal file
@@ -0,0 +1,56 @@
|
||||
package ru.ulstu.is.labwork.Lab4.DTO;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import ru.ulstu.is.labwork.Lab4.model.Customer;
|
||||
import java.util.List;
|
||||
|
||||
public class CustomerDto {
|
||||
public long id;
|
||||
public String username;
|
||||
public String password;
|
||||
public List<CommentDto> comments;
|
||||
public List<PostDto> posts;
|
||||
|
||||
public CustomerDto() {
|
||||
|
||||
}
|
||||
|
||||
public CustomerDto(Customer customer){
|
||||
this.id = customer.getId();
|
||||
this.username = customer.getUsername();
|
||||
this.password = customer.getPassword();
|
||||
this.comments = customer.getComments().stream().map(CommentDto::new).toList();
|
||||
this.posts = customer.getPosts().stream().map(PostDto::new).toList();
|
||||
}
|
||||
|
||||
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
|
||||
public List<CommentDto> getComments() {
|
||||
return comments;
|
||||
}
|
||||
|
||||
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
|
||||
public List<PostDto> getPosts() {
|
||||
return posts;
|
||||
}
|
||||
}
|
||||
67
src/main/java/ru/ulstu/is/labwork/Lab4/DTO/PostDto.java
Normal file
67
src/main/java/ru/ulstu/is/labwork/Lab4/DTO/PostDto.java
Normal file
@@ -0,0 +1,67 @@
|
||||
package ru.ulstu.is.labwork.Lab4.DTO;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import ru.ulstu.is.labwork.Lab4.model.Post;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class PostDto {
|
||||
public long id;
|
||||
public String title;
|
||||
public String content;
|
||||
public String customerName;
|
||||
public long customerId;
|
||||
public ArrayList<CommentDto> comments;
|
||||
|
||||
public PostDto() {
|
||||
|
||||
}
|
||||
|
||||
public PostDto(Post post){
|
||||
this.id = post.getId();
|
||||
this.title = post.getTitle();
|
||||
this.content = post.getContent();
|
||||
this.customerName = post.getCustomer().getUsername();
|
||||
this.customerId = post.getCustomer().getId();
|
||||
this.comments = new ArrayList<>(post.getComments().stream().map(CommentDto::new).toList());
|
||||
}
|
||||
|
||||
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public String getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
public void setContent(String content) {
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
|
||||
public String getCustomerName() {
|
||||
return customerName;
|
||||
}
|
||||
|
||||
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
|
||||
public List<CommentDto> getComments() {
|
||||
return comments;
|
||||
}
|
||||
|
||||
public long getCustomerId() {
|
||||
return customerId;
|
||||
}
|
||||
|
||||
public void setCustomerId(long customerId) {
|
||||
this.customerId = customerId;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package ru.ulstu.is.labwork.Lab4.DTO;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.Size;
|
||||
|
||||
public class UserSignupDto {
|
||||
@NotBlank
|
||||
@Size(min = 3, max = 64)
|
||||
private String username;
|
||||
@NotBlank
|
||||
@Size(min = 6, max = 64)
|
||||
private String password;
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package ru.ulstu.is.labwork.Lab4.Exceptions;
|
||||
|
||||
public class CommentNotFoundException extends RuntimeException {
|
||||
public CommentNotFoundException(Long id) {
|
||||
super(String.format("Comment with id [%s] is not found", id));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package ru.ulstu.is.labwork.Lab4.Exceptions;
|
||||
|
||||
public class CustomerNotFoundException extends RuntimeException {
|
||||
public CustomerNotFoundException(Long id) {
|
||||
super(String.format("Customer with id [%s] is not found", id));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package ru.ulstu.is.labwork.Lab4.Exceptions;
|
||||
|
||||
public class JwtException extends RuntimeException {
|
||||
public JwtException(Throwable throwable) {
|
||||
super(throwable);
|
||||
}
|
||||
|
||||
public JwtException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package ru.ulstu.is.labwork.Lab4.Exceptions;
|
||||
|
||||
public class PostNotFoundException extends RuntimeException {
|
||||
public PostNotFoundException(Long id) {
|
||||
super(String.format("Post with id [%s] is not found", id));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package ru.ulstu.is.labwork.Lab4.controller;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import ru.ulstu.is.labwork.Lab4.DTO.CommentDto;
|
||||
import ru.ulstu.is.labwork.Lab4.model.Comment;
|
||||
import ru.ulstu.is.labwork.Lab4.services.CommentService;
|
||||
import ru.ulstu.is.labwork.Lab4.services.CustomerService;
|
||||
import ru.ulstu.is.labwork.Lab4.services.PostService;
|
||||
import ru.ulstu.is.labwork.WebConfiguration;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
@RequestMapping(WebConfiguration.REST_API + "/comment")
|
||||
public class CommentController {
|
||||
private final CommentService commentService;
|
||||
private final CustomerService customerService;
|
||||
private final PostService postService;
|
||||
|
||||
public CommentController(CommentService commentService, CustomerService customerService, PostService postService) {
|
||||
this.commentService = commentService;
|
||||
this.customerService = customerService;
|
||||
this.postService = postService;
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
public CommentDto getComment(@PathVariable Long id) {
|
||||
return new CommentDto(commentService.findComment(id));
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public List<CommentDto> getComments() {
|
||||
return commentService.findAllComments().stream()
|
||||
.map(CommentDto::new)
|
||||
.toList();
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
public CommentDto createComment(@RequestBody @Valid CommentDto commentDto){
|
||||
final Comment comment = commentService.addComment(
|
||||
customerService.findCustomer(commentDto.getCustomerId()),
|
||||
postService.findPost(commentDto.getPostId()),
|
||||
commentDto.getContent()
|
||||
);
|
||||
return new CommentDto(comment);
|
||||
}
|
||||
|
||||
@PutMapping("/{id}")
|
||||
public CommentDto updateComment(@RequestBody @Valid CommentDto commentDto, @PathVariable Long id) {
|
||||
return new CommentDto(commentService.updateComment(id, commentDto.getContent()));
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
public CommentDto deleteComment(@PathVariable Long id) {
|
||||
return new CommentDto(commentService.deleteComment(id));
|
||||
}
|
||||
|
||||
@DeleteMapping
|
||||
public void deleteAllComments(){
|
||||
commentService.deleteAllComments();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package ru.ulstu.is.labwork.Lab4.controller;
|
||||
|
||||
import javax.validation.Valid;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import ru.ulstu.is.labwork.Lab4.DTO.CustomerDto;
|
||||
import ru.ulstu.is.labwork.Lab4.model.Customer;
|
||||
import ru.ulstu.is.labwork.Lab4.services.CustomerService;
|
||||
import ru.ulstu.is.labwork.WebConfiguration;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
@RequestMapping(WebConfiguration.REST_API + "/customer")
|
||||
public class CustomerController {
|
||||
public static final String URL_LOGIN = "/jwt/login";
|
||||
private final CustomerService customerService;
|
||||
|
||||
public CustomerController(CustomerService customerService) {
|
||||
this.customerService = customerService;
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
public CustomerDto getCustomer(@PathVariable Long id) {
|
||||
return new CustomerDto(customerService.findCustomer(id));
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public List<CustomerDto> getCustomers() {
|
||||
return customerService.findAllCustomers().stream()
|
||||
.map(CustomerDto::new)
|
||||
.toList();
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
public CustomerDto createCustomer(@RequestBody @Valid CustomerDto customerDto){
|
||||
final Customer customer = customerService.addCustomer(customerDto.getUsername(), customerDto.getPassword());
|
||||
return new CustomerDto(customer);
|
||||
}
|
||||
|
||||
@PutMapping("/{id}")
|
||||
public CustomerDto updateCustomer(@RequestBody @Valid CustomerDto customerDto, @PathVariable Long id) {
|
||||
return new CustomerDto(customerService.updateCustomer(id, customerDto.getUsername(), customerDto.getPassword()));
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
public CustomerDto deleteCustomer(@PathVariable Long id) {
|
||||
return new CustomerDto(customerService.deleteCustomer(id));
|
||||
}
|
||||
|
||||
@DeleteMapping
|
||||
public void deleteAllCustomers(){
|
||||
customerService.deleteAllCustomers();
|
||||
}
|
||||
|
||||
@GetMapping("/find/{username}")
|
||||
public CustomerDto getCustomerByUsername(@PathVariable String username) {
|
||||
return new CustomerDto(customerService.findByUsername(username));
|
||||
}
|
||||
|
||||
@PostMapping(URL_LOGIN)
|
||||
public String login(@RequestBody @Valid CustomerDto customerDto) {
|
||||
return customerService.loginAndGetToken(customerDto);
|
||||
}
|
||||
|
||||
@GetMapping ("/me")
|
||||
CustomerDto getCurrentCustomer(@RequestHeader(HttpHeaders.AUTHORIZATION) String token) {
|
||||
return new CustomerDto(customerService.findByUsername(customerService.loadUserByToken(token.substring(7)).getUsername()));
|
||||
}
|
||||
|
||||
@GetMapping("role/{token}")
|
||||
public String getRoleByToken(@PathVariable String token) {
|
||||
var userDetails = customerService.loadUserByToken(token);
|
||||
Customer customer = customerService.findByUsername(userDetails.getUsername());
|
||||
|
||||
if (customer != null) {
|
||||
return customer.getRole().toString();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package ru.ulstu.is.labwork.Lab4.controller;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import ru.ulstu.is.labwork.Lab4.DTO.CommentDto;
|
||||
import ru.ulstu.is.labwork.Lab4.DTO.PostDto;
|
||||
import ru.ulstu.is.labwork.Lab4.model.Comment;
|
||||
import ru.ulstu.is.labwork.Lab4.model.Post;
|
||||
import ru.ulstu.is.labwork.Lab4.services.CommentService;
|
||||
import ru.ulstu.is.labwork.Lab4.services.CustomerService;
|
||||
import ru.ulstu.is.labwork.Lab4.services.PostService;
|
||||
import ru.ulstu.is.labwork.WebConfiguration;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@RestController
|
||||
@RequestMapping(WebConfiguration.REST_API + "/post")
|
||||
public class PostController {
|
||||
private final PostService postService;
|
||||
private final CustomerService customerService;
|
||||
|
||||
public PostController(PostService postService, CustomerService customerService) {
|
||||
this.postService = postService;
|
||||
this.customerService = customerService;
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
public PostDto getPost(@PathVariable Long id) {
|
||||
return new PostDto(postService.findPost(id));
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public List<PostDto> getPosts() {
|
||||
return postService.findAllPosts().stream()
|
||||
.map(PostDto::new)
|
||||
.toList();
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
public PostDto createPost(@RequestBody @Valid PostDto postDto) {
|
||||
return new PostDto(postService.addPost(customerService.findCustomer(postDto.getCustomerId()), postDto.getTitle(), postDto.getContent()));
|
||||
}
|
||||
|
||||
@PutMapping("/{id}")
|
||||
public PostDto updatePost(@RequestBody @Valid PostDto postDto, @PathVariable Long id) {
|
||||
return new PostDto(postService.updatePost(id, postDto.title, postDto.content));
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
public PostDto deletePost (@PathVariable Long id) {
|
||||
return new PostDto(postService.deletePost(id));
|
||||
}
|
||||
|
||||
@DeleteMapping
|
||||
public void deleteAllPosts() {
|
||||
postService.deleteAllPosts();
|
||||
}
|
||||
|
||||
@GetMapping("/search")
|
||||
public List<PostDto> searchPosts(@RequestParam(required = false) String query) {
|
||||
if (query == null || query.isBlank()) {
|
||||
return postService.findAllPosts().stream()
|
||||
.map(PostDto::new)
|
||||
.toList();
|
||||
} else {
|
||||
return postService.searchPosts(query);
|
||||
}
|
||||
}
|
||||
}
|
||||
68
src/main/java/ru/ulstu/is/labwork/Lab4/model/Comment.java
Normal file
68
src/main/java/ru/ulstu/is/labwork/Lab4/model/Comment.java
Normal file
@@ -0,0 +1,68 @@
|
||||
package ru.ulstu.is.labwork.Lab4.model;
|
||||
|
||||
import javax.persistence.*;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import java.util.Objects;
|
||||
|
||||
@Entity
|
||||
public class Comment {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||
private Long id;
|
||||
|
||||
@Column
|
||||
private String content;
|
||||
|
||||
@ManyToOne(fetch = FetchType.EAGER)
|
||||
@JoinColumn(name="customer_fk")
|
||||
private Customer customer;
|
||||
|
||||
@ManyToOne(fetch = FetchType.EAGER)
|
||||
@JoinColumn(name="post_fk")
|
||||
private Post post;
|
||||
|
||||
public Comment() {
|
||||
|
||||
}
|
||||
|
||||
public Comment(Customer customer, Post post, String content) {
|
||||
this.customer = customer;
|
||||
this.post = post;
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public Post getPost() {
|
||||
return post;
|
||||
}
|
||||
|
||||
public Customer getCustomer() {
|
||||
return customer;
|
||||
}
|
||||
|
||||
public String getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
public void setContent(String content) {
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
Comment comment = (Comment) o;
|
||||
|
||||
return Objects.equals(id, comment.id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(id);
|
||||
}
|
||||
}
|
||||
90
src/main/java/ru/ulstu/is/labwork/Lab4/model/Customer.java
Normal file
90
src/main/java/ru/ulstu/is/labwork/Lab4/model/Customer.java
Normal file
@@ -0,0 +1,90 @@
|
||||
package ru.ulstu.is.labwork.Lab4.model;
|
||||
|
||||
import javax.persistence.*;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
|
||||
import org.h2.engine.User;
|
||||
import java.util.*;
|
||||
|
||||
@Entity
|
||||
public class Customer {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||
private Long id;
|
||||
@Column
|
||||
private String username;
|
||||
@Column
|
||||
private String password;
|
||||
@OneToMany(fetch = FetchType.EAGER, mappedBy = "customer", cascade = CascadeType.ALL)
|
||||
private List<Comment> comments;
|
||||
@OneToMany(fetch = FetchType.EAGER, mappedBy = "customer", cascade = CascadeType.ALL)
|
||||
private List<Post> posts;
|
||||
|
||||
private UserRole role;
|
||||
|
||||
public Customer() {
|
||||
|
||||
}
|
||||
|
||||
public Customer(String username, String password) {
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
this.role = UserRole.USER;
|
||||
this.comments = new ArrayList<>();
|
||||
this.posts = new ArrayList<>();
|
||||
}
|
||||
|
||||
public Customer(String username, String password, UserRole role) {
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
this.role = role;
|
||||
this.comments = new ArrayList<>();
|
||||
this.posts = new ArrayList<>();
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public List<Comment> getComments() {
|
||||
return comments;
|
||||
}
|
||||
|
||||
public List<Post> getPosts() {
|
||||
return posts;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public UserRole getRole() {
|
||||
return role;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) return true;
|
||||
if (obj == null || getClass() != obj.getClass()) return false;
|
||||
Customer customer = (Customer) obj;
|
||||
return Objects.equals(id, customer.id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(id);
|
||||
}
|
||||
}
|
||||
|
||||
80
src/main/java/ru/ulstu/is/labwork/Lab4/model/Post.java
Normal file
80
src/main/java/ru/ulstu/is/labwork/Lab4/model/Post.java
Normal file
@@ -0,0 +1,80 @@
|
||||
package ru.ulstu.is.labwork.Lab4.model;
|
||||
|
||||
import javax.persistence.*;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Entity
|
||||
public class Post {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||
private Long id;
|
||||
|
||||
@Column
|
||||
private String title;
|
||||
|
||||
@Column
|
||||
private String content;
|
||||
|
||||
@ManyToOne(fetch = FetchType.EAGER)
|
||||
@JoinColumn(name = "customer_fk")
|
||||
private Customer customer;
|
||||
|
||||
@OneToMany(fetch = FetchType.EAGER, mappedBy = "post", cascade = CascadeType.ALL)
|
||||
private List<Comment> comments;
|
||||
|
||||
public Post() {
|
||||
|
||||
}
|
||||
|
||||
public Post(Customer customer, String title, String content) {
|
||||
this.customer = customer;
|
||||
this.title = title;
|
||||
this.content = content;
|
||||
this.comments = new ArrayList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
Post post = (Post) o;
|
||||
|
||||
return id.equals(post.id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return id.hashCode();
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public String getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
public void setContent(String content) {
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
public List<Comment> getComments() {
|
||||
return comments;
|
||||
}
|
||||
|
||||
public Customer getCustomer() {
|
||||
return customer;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
20
src/main/java/ru/ulstu/is/labwork/Lab4/model/UserRole.java
Normal file
20
src/main/java/ru/ulstu/is/labwork/Lab4/model/UserRole.java
Normal file
@@ -0,0 +1,20 @@
|
||||
package ru.ulstu.is.labwork.Lab4.model;
|
||||
|
||||
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";
|
||||
}
|
||||
}
|
||||
70
src/main/java/ru/ulstu/is/labwork/Lab4/mvc/Admin.java
Normal file
70
src/main/java/ru/ulstu/is/labwork/Lab4/mvc/Admin.java
Normal file
@@ -0,0 +1,70 @@
|
||||
package ru.ulstu.is.labwork.Lab4.mvc;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpSession;
|
||||
import javax.validation.Valid;
|
||||
import ru.ulstu.is.labwork.Lab4.DTO.CustomerDto;
|
||||
import ru.ulstu.is.labwork.Lab4.model.UserRole;
|
||||
import ru.ulstu.is.labwork.Lab4.services.CustomerService;
|
||||
import org.springframework.security.access.annotation.Secured;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
@Controller
|
||||
@RequestMapping("/admin")
|
||||
@Secured({UserRole.AsString.ADMIN})
|
||||
public class Admin {
|
||||
private final CustomerService customerService;
|
||||
|
||||
public Admin(CustomerService customerService) {
|
||||
this.customerService = customerService;
|
||||
}
|
||||
|
||||
@GetMapping(value = { "/", "/{id}" })
|
||||
@Secured({UserRole.AsString.ADMIN})
|
||||
public String getCustomers(@PathVariable(required = false) Long id, HttpServletRequest request, Model model) {
|
||||
model.addAttribute("request", request);
|
||||
model.addAttribute("currentCustomerId", customerService.findByUsername(
|
||||
((UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getUsername()
|
||||
).getId());
|
||||
if (id == null || id <= 0) {
|
||||
model.addAttribute("customers", customerService.findAllCustomers().stream().map(CustomerDto::new).toList());
|
||||
return "admin";
|
||||
} else {
|
||||
return "redirect:/customers/" + id;
|
||||
}
|
||||
}
|
||||
|
||||
@PostMapping("/delete/{id}")
|
||||
public String deleteCustomer(@PathVariable Long id) {
|
||||
customerService.deleteCustomer(id);
|
||||
return "redirect:/admin/";
|
||||
}
|
||||
|
||||
@PostMapping(value = { "/", "/{id}"})
|
||||
public String manipulateCustomer(@PathVariable(required = false) Long id, @ModelAttribute @Valid CustomerDto customerDto,
|
||||
HttpServletRequest request,
|
||||
BindingResult bindingResult,
|
||||
Model model) {
|
||||
model.addAttribute("request", request);
|
||||
model.addAttribute("currentCustomerId", customerService.findByUsername(
|
||||
((UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getUsername()
|
||||
).getId());
|
||||
if (bindingResult.hasErrors()) {
|
||||
model.addAttribute("errors", bindingResult.getAllErrors());
|
||||
return "/admin";
|
||||
}
|
||||
|
||||
if (id == null || id <= 0) {
|
||||
customerService.addCustomer(customerDto.username, customerDto.password);
|
||||
} else {
|
||||
customerService.updateCustomer(id, customerDto.username, customerDto.password);
|
||||
}
|
||||
|
||||
return "redirect:/admin/";
|
||||
}
|
||||
}
|
||||
59
src/main/java/ru/ulstu/is/labwork/Lab4/mvc/Comments.java
Normal file
59
src/main/java/ru/ulstu/is/labwork/Lab4/mvc/Comments.java
Normal file
@@ -0,0 +1,59 @@
|
||||
package ru.ulstu.is.labwork.Lab4.mvc;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpSession;
|
||||
import javax.validation.Valid;
|
||||
import ru.ulstu.is.labwork.Lab4.DTO.CommentDto;
|
||||
import ru.ulstu.is.labwork.Lab4.DTO.PostDto;
|
||||
import ru.ulstu.is.labwork.Lab4.services.CommentService;
|
||||
import ru.ulstu.is.labwork.Lab4.services.CustomerService;
|
||||
import ru.ulstu.is.labwork.Lab4.services.PostService;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
@Controller
|
||||
@RequestMapping("/comments")
|
||||
public class Comments {
|
||||
private final CustomerService customerService;
|
||||
private final CommentService commentService;
|
||||
private final PostService postService;
|
||||
|
||||
public Comments(CustomerService customerService, CommentService commentService, PostService postService) {
|
||||
this.customerService = customerService;
|
||||
this.commentService = commentService;
|
||||
this.postService = postService;
|
||||
}
|
||||
|
||||
@PostMapping(value = { "/", "/{id}"})
|
||||
public String manipulateComment(@PathVariable(required = false) Long id, @ModelAttribute @Valid CommentDto commentDto,
|
||||
HttpServletRequest request, BindingResult bindingResult, Model model) {
|
||||
model.addAttribute("request", request);
|
||||
model.addAttribute("currentCustomerId", customerService.findByUsername(
|
||||
((UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getUsername()
|
||||
).getId());
|
||||
model.addAttribute("posts", postService.findAllPosts().stream().map(PostDto::new).toList());
|
||||
|
||||
if (bindingResult.hasErrors()) {
|
||||
model.addAttribute("errors", bindingResult.getAllErrors());
|
||||
return "/feed";
|
||||
}
|
||||
|
||||
if (id == null || id <= 0) {
|
||||
commentService.addComment(customerService.findCustomer(commentDto.customerId), postService.findPost(commentDto.postId), commentDto.content);
|
||||
} else {
|
||||
commentService.updateComment(id, commentDto.content);
|
||||
}
|
||||
|
||||
return "redirect:/feed";
|
||||
}
|
||||
|
||||
@PostMapping("/delete/{id}")
|
||||
public String deleteComment(@PathVariable Long id) {
|
||||
commentService.deleteComment(id);
|
||||
return "redirect:/feed";
|
||||
}
|
||||
}
|
||||
67
src/main/java/ru/ulstu/is/labwork/Lab4/mvc/Customers.java
Normal file
67
src/main/java/ru/ulstu/is/labwork/Lab4/mvc/Customers.java
Normal file
@@ -0,0 +1,67 @@
|
||||
package ru.ulstu.is.labwork.Lab4.mvc;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpSession;
|
||||
import javax.validation.Valid;
|
||||
import ru.ulstu.is.labwork.Lab4.DTO.CustomerDto;
|
||||
import ru.ulstu.is.labwork.Lab4.services.CustomerService;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
@Controller
|
||||
@RequestMapping("/customers")
|
||||
public class Customers {
|
||||
private final CustomerService customerService;
|
||||
|
||||
public Customers(CustomerService customerService) {
|
||||
this.customerService = customerService;
|
||||
}
|
||||
|
||||
@GetMapping(value = {"", "/", "/{id}" })
|
||||
public String getCustomers(@PathVariable(required = false) Long id, HttpServletRequest request, HttpSession session, Model model) {
|
||||
model.addAttribute("request", request);
|
||||
model.addAttribute("currentCustomerId", customerService.findByUsername(
|
||||
((UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getUsername()
|
||||
).getId());
|
||||
if (id == null || id <= 0) {
|
||||
model.addAttribute("customers", customerService.findAllCustomers().stream().map(CustomerDto::new).toList());
|
||||
} else {
|
||||
model.addAttribute("customers", new CustomerDto[] { new CustomerDto(customerService.findCustomer(id)) });
|
||||
}
|
||||
|
||||
return "customers";
|
||||
}
|
||||
|
||||
@PostMapping("/delete/{id}")
|
||||
public String deleteCustomer(@PathVariable Long id, HttpSession session) {
|
||||
customerService.deleteCustomer(id);
|
||||
return "redirect:/customers/";
|
||||
}
|
||||
|
||||
@PostMapping(value = { "/", "/{id}"})
|
||||
public String manipulateCustomer(@PathVariable(required = false) Long id, @ModelAttribute @Valid CustomerDto customerDto,
|
||||
HttpServletRequest request, HttpSession session,
|
||||
BindingResult bindingResult,
|
||||
Model model) {
|
||||
model.addAttribute("request", request);
|
||||
model.addAttribute("currentCustomerId", customerService.findByUsername(
|
||||
((UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getUsername()
|
||||
).getId());
|
||||
if (bindingResult.hasErrors()) {
|
||||
model.addAttribute("errors", bindingResult.getAllErrors());
|
||||
return "/customers";
|
||||
}
|
||||
|
||||
if (id == null || id <= 0) {
|
||||
customerService.addCustomer(customerDto.username, customerDto.password);
|
||||
} else {
|
||||
customerService.updateCustomer(id, customerDto.username, customerDto.password);
|
||||
}
|
||||
|
||||
return "redirect:/customers/";
|
||||
}
|
||||
}
|
||||
51
src/main/java/ru/ulstu/is/labwork/Lab4/mvc/Feed.java
Normal file
51
src/main/java/ru/ulstu/is/labwork/Lab4/mvc/Feed.java
Normal file
@@ -0,0 +1,51 @@
|
||||
package ru.ulstu.is.labwork.Lab4.mvc;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpSession;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import ru.ulstu.is.labwork.Lab4.DTO.CustomerDto;
|
||||
import ru.ulstu.is.labwork.Lab4.DTO.PostDto;
|
||||
import ru.ulstu.is.labwork.Lab4.services.CommentService;
|
||||
import ru.ulstu.is.labwork.Lab4.services.CustomerService;
|
||||
import ru.ulstu.is.labwork.Lab4.services.PostService;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Objects;
|
||||
|
||||
@Controller
|
||||
@RequestMapping(value = { "", "/feed" })
|
||||
public class Feed {
|
||||
private final PostService postService;
|
||||
private final CustomerService customerService;
|
||||
private final CommentService commentService;
|
||||
|
||||
public Feed(PostService postService, CustomerService customerService, CommentService commentService) {
|
||||
this.postService = postService;
|
||||
this.customerService = customerService;
|
||||
this.commentService = commentService;
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public String getPosts(@RequestParam(required = false) String search,
|
||||
HttpServletRequest request,
|
||||
HttpSession session,
|
||||
Model model) {
|
||||
model.addAttribute("request", request);
|
||||
model.addAttribute("currentCustomerId", customerService.findByUsername(
|
||||
((UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getUsername()
|
||||
).getId());
|
||||
if (search == null) {
|
||||
model.addAttribute("posts", postService.findAllPosts().stream().map(PostDto::new).toList());
|
||||
} else {
|
||||
model.addAttribute("posts", postService.searchPosts(search));
|
||||
}
|
||||
model.addAttribute("customers", customerService.findAllCustomers().stream().map(CustomerDto::new).toList());
|
||||
|
||||
return "/feed";
|
||||
}
|
||||
}
|
||||
60
src/main/java/ru/ulstu/is/labwork/Lab4/mvc/Posts.java
Normal file
60
src/main/java/ru/ulstu/is/labwork/Lab4/mvc/Posts.java
Normal file
@@ -0,0 +1,60 @@
|
||||
package ru.ulstu.is.labwork.Lab4.mvc;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpSession;
|
||||
import ru.ulstu.is.labwork.Lab4.services.CommentService;
|
||||
import ru.ulstu.is.labwork.Lab4.services.CustomerService;
|
||||
import ru.ulstu.is.labwork.Lab4.services.PostService;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import javax.validation.Valid;
|
||||
import ru.ulstu.is.labwork.Lab4.DTO.PostDto;
|
||||
|
||||
@Controller
|
||||
@RequestMapping("/posts")
|
||||
public class Posts {
|
||||
private final CustomerService customerService;
|
||||
private final CommentService commentService;
|
||||
private final PostService postService;
|
||||
|
||||
public Posts(CustomerService customerService, CommentService commentService, PostService postService) {
|
||||
this.customerService = customerService;
|
||||
this.commentService = commentService;
|
||||
this.postService = postService;
|
||||
}
|
||||
|
||||
@PostMapping("/delete/{id}")
|
||||
public String deletePost(@PathVariable Long id) {
|
||||
postService.deletePost(id);
|
||||
return "redirect:/feed";
|
||||
}
|
||||
|
||||
@PostMapping(value = { "/", "/{id}"})
|
||||
public String manipulatePost(@PathVariable(required = false) Long id, @ModelAttribute @Valid PostDto postDto,
|
||||
HttpServletRequest request, HttpSession session,
|
||||
BindingResult bindingResult,
|
||||
Model model) {
|
||||
model.addAttribute("request", request);
|
||||
model.addAttribute("currentCustomerId", customerService.findByUsername(
|
||||
((UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getUsername()
|
||||
).getId());
|
||||
model.addAttribute("posts", postService.findAllPosts().stream().map(PostDto::new).toList());
|
||||
|
||||
if (bindingResult.hasErrors()) {
|
||||
model.addAttribute("errors", bindingResult.getAllErrors());
|
||||
return "/feed";
|
||||
}
|
||||
|
||||
if (id == null || id <= 0) {
|
||||
postService.addPost(customerService.findCustomer(postDto.customerId), postDto.title, postDto.content);
|
||||
} else {
|
||||
postService.updatePost(id, postDto.title, postDto.content);
|
||||
}
|
||||
|
||||
return "redirect:/feed";
|
||||
}
|
||||
}
|
||||
53
src/main/java/ru/ulstu/is/labwork/Lab4/mvc/UserSignUp.java
Normal file
53
src/main/java/ru/ulstu/is/labwork/Lab4/mvc/UserSignUp.java
Normal file
@@ -0,0 +1,53 @@
|
||||
package ru.ulstu.is.labwork.Lab4.mvc;
|
||||
|
||||
import ru.ulstu.is.labwork.Lab4.DTO.UserSignupDto;
|
||||
import ru.ulstu.is.labwork.Lab4.model.Customer;
|
||||
import ru.ulstu.is.labwork.Lab4.services.CustomerService;
|
||||
import ru.ulstu.is.labwork.Lab4.util.validation.ValidationException;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.validation.Valid;
|
||||
|
||||
@Controller
|
||||
@RequestMapping(UserSignUp.SIGNUP_URL)
|
||||
public class UserSignUp {
|
||||
public static final String SIGNUP_URL = "/signup";
|
||||
|
||||
private final CustomerService customerService;
|
||||
|
||||
public UserSignUp(CustomerService customerService) {
|
||||
this.customerService = customerService;
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public String showSignupForm(Model model) {
|
||||
model.addAttribute("userDto", new UserSignupDto());
|
||||
return "signup";
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
public String signup(@ModelAttribute("userDto") @Valid UserSignupDto userSignupDto,
|
||||
BindingResult bindingResult, HttpServletRequest request,
|
||||
Model model) {
|
||||
model.addAttribute("request", request);
|
||||
if (bindingResult.hasErrors()) {
|
||||
model.addAttribute("errors", bindingResult.getAllErrors());
|
||||
return "signup";
|
||||
}
|
||||
try {
|
||||
final Customer customer = customerService.addCustomer(
|
||||
userSignupDto.getUsername(), userSignupDto.getPassword());
|
||||
return "redirect:/login?created=" + customer.getUsername();
|
||||
} catch (ValidationException e) {
|
||||
model.addAttribute("errors", e.getMessage());
|
||||
return "signup";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package ru.ulstu.is.labwork.Lab4.repositories;
|
||||
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import ru.ulstu.is.labwork.Lab4.model.Comment;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface CommentRepository extends JpaRepository<Comment, Long> {
|
||||
@Query("SELECT DISTINCT c FROM Comment c WHERE c.content LIKE %:tag%")
|
||||
List<Comment> searchComments(@Param("tag") String tag);
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package ru.ulstu.is.labwork.Lab4.repositories;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import ru.ulstu.is.labwork.Lab4.model.Customer;
|
||||
|
||||
public interface CustomerRepository extends JpaRepository<Customer, Long> {
|
||||
Customer findByUsername(String username);
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package ru.ulstu.is.labwork.Lab4.repositories;
|
||||
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import ru.ulstu.is.labwork.Lab4.model.Post;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface PostRepository extends JpaRepository<Post, Long> {
|
||||
@Query("SELECT DISTINCT p FROM Post p WHERE p.title LIKE %:tag% OR p.content LIKE %:tag%")
|
||||
List<Post> searchPosts(@Param("tag") String tag);
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package ru.ulstu.is.labwork.Lab4.security;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import ru.ulstu.is.labwork.Lab4.Exceptions.JwtException;
|
||||
import ru.ulstu.is.labwork.Lab4.services.CustomerService;
|
||||
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 javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
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 CustomerService customerService;
|
||||
|
||||
public JwtFilter(CustomerService customerService) {
|
||||
this.customerService = customerService;
|
||||
}
|
||||
|
||||
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 = customerService.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 ru.ulstu.is.labwork.Lab4.security;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
108
src/main/java/ru/ulstu/is/labwork/Lab4/security/JwtProvider.java
Normal file
108
src/main/java/ru/ulstu/is/labwork/Lab4/security/JwtProvider.java
Normal file
@@ -0,0 +1,108 @@
|
||||
package ru.ulstu.is.labwork.Lab4.security;
|
||||
|
||||
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 ru.ulstu.is.labwork.Lab4.Exceptions.JwtException;
|
||||
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,14 @@
|
||||
package ru.ulstu.is.labwork.Lab4.security;
|
||||
|
||||
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 createPasswordEncoder() {
|
||||
return new BCryptPasswordEncoder();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
package ru.ulstu.is.labwork.Lab4.security;
|
||||
|
||||
import ru.ulstu.is.labwork.Lab4.controller.CustomerController;
|
||||
import ru.ulstu.is.labwork.Lab4.model.UserRole;
|
||||
import ru.ulstu.is.labwork.Lab4.mvc.UserSignUp;
|
||||
import ru.ulstu.is.labwork.Lab4.services.CustomerService;
|
||||
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.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.builders.WebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||
import org.springframework.security.web.authentication.DelegatingAuthenticationEntryPoint;
|
||||
import org.springframework.security.web.authentication.Http403ForbiddenEntryPoint;
|
||||
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||
import ru.ulstu.is.labwork.WebConfiguration;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
@EnableGlobalMethodSecurity(securedEnabled = true)
|
||||
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
|
||||
private final Logger log = LoggerFactory.getLogger(SecurityConfiguration.class);
|
||||
private static final String LOGIN_URL = "/login";
|
||||
public static final String SPA_URL_MASK = "/{path:[^\\.]*}";
|
||||
private final CustomerService customerService;
|
||||
private final JwtFilter jwtFilter;
|
||||
|
||||
public SecurityConfiguration(CustomerService customerService) {
|
||||
this.customerService = customerService;
|
||||
this.jwtFilter = new JwtFilter(customerService);
|
||||
createAdminOnStartup();
|
||||
}
|
||||
|
||||
private void createAdminOnStartup() {
|
||||
final String admin = "admin";
|
||||
if (customerService.findByUsername(admin) == null) {
|
||||
log.info("Admin user successfully created");
|
||||
customerService.addCustomer(admin, admin, UserRole.ADMIN);
|
||||
}
|
||||
}
|
||||
|
||||
//mvc java
|
||||
// @Override
|
||||
// protected void configure(HttpSecurity http) throws Exception {
|
||||
// http.exceptionHandling().authenticationEntryPoint(delegatingEntryPoint());
|
||||
// http.headers().frameOptions().sameOrigin().and()
|
||||
// .cors().and()
|
||||
// .csrf().disable()
|
||||
// .authorizeRequests()
|
||||
// .antMatchers(UserSignUp.SIGNUP_URL).permitAll()
|
||||
// .antMatchers(HttpMethod.GET, LOGIN_URL).permitAll()
|
||||
// .anyRequest().authenticated()
|
||||
// .and()
|
||||
// .formLogin()
|
||||
// .loginPage(LOGIN_URL).permitAll()
|
||||
// .and()
|
||||
// .logout().permitAll();
|
||||
// }
|
||||
|
||||
//jwt vue
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
log.info("Creating security configuration");
|
||||
http.cors()
|
||||
.and()
|
||||
.csrf().disable()
|
||||
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
|
||||
.and()
|
||||
.authorizeRequests()
|
||||
.antMatchers("/", SPA_URL_MASK).permitAll()
|
||||
.antMatchers(HttpMethod.POST, WebConfiguration.REST_API + "/customer" + CustomerController.URL_LOGIN).permitAll()
|
||||
.antMatchers(HttpMethod.POST, WebConfiguration.REST_API + "/customer").permitAll()
|
||||
.anyRequest()
|
||||
.authenticated()
|
||||
.and()
|
||||
.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class)
|
||||
.anonymous();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth.userDetailsService(customerService);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(WebSecurity web) {
|
||||
web.ignoring()
|
||||
.antMatchers("/css/**")
|
||||
.antMatchers("/js/**")
|
||||
.antMatchers("/templates/**")
|
||||
.antMatchers("/webjars/**")
|
||||
.antMatchers("/swagger-resources/**")
|
||||
.antMatchers("/v3/api-docs/**");
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AuthenticationEntryPoint delegatingEntryPoint() {
|
||||
final LinkedHashMap<RequestMatcher, AuthenticationEntryPoint> map = new LinkedHashMap();
|
||||
map.put(new AntPathRequestMatcher("/"), new LoginUrlAuthenticationEntryPoint("/login"));
|
||||
map.put(new AntPathRequestMatcher("/api/1.0/**"), new Http403ForbiddenEntryPoint());
|
||||
|
||||
final DelegatingAuthenticationEntryPoint entryPoint = new DelegatingAuthenticationEntryPoint(map);
|
||||
entryPoint.setDefaultEntryPoint(new LoginUrlAuthenticationEntryPoint("/login"));
|
||||
|
||||
return entryPoint;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package ru.ulstu.is.labwork.Lab4.services;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import ru.ulstu.is.labwork.Lab4.Exceptions.CommentNotFoundException;
|
||||
import ru.ulstu.is.labwork.Lab4.model.Comment;
|
||||
import ru.ulstu.is.labwork.Lab4.model.Customer;
|
||||
import ru.ulstu.is.labwork.Lab4.model.Post;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StringUtils;
|
||||
import ru.ulstu.is.labwork.Lab4.repositories.CommentRepository;
|
||||
import ru.ulstu.is.labwork.Lab4.util.validation.ValidatorUtil;
|
||||
|
||||
import javax.persistence.EntityNotFoundException;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@Service
|
||||
public class CommentService {
|
||||
private final CommentRepository commentRepository;
|
||||
private final ValidatorUtil validatorUtil;
|
||||
|
||||
public CommentService(CommentRepository commentRepository,
|
||||
ValidatorUtil validatorUtil) {
|
||||
this.commentRepository = commentRepository;
|
||||
this.validatorUtil = validatorUtil;
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public Comment findComment(Long id) {
|
||||
final Optional<Comment> comment = commentRepository.findById(id);
|
||||
return comment.orElseThrow(() -> new CommentNotFoundException(id));
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public List<Comment> findAllComments() {
|
||||
return commentRepository.findAll();
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public Comment addComment(Customer customer, Post post, String content) {
|
||||
final Comment comment = new Comment(customer, post, content);
|
||||
validatorUtil.validate(comment);
|
||||
customer.getComments().add(comment);
|
||||
post.getComments().add(comment);
|
||||
return commentRepository.save(comment);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public Comment updateComment(Long id, String content) {
|
||||
final Comment currentComment = findComment(id);
|
||||
currentComment.setContent(content);
|
||||
validatorUtil.validate(currentComment);
|
||||
return commentRepository.save(currentComment);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public Comment deleteComment(Long id) {
|
||||
final Comment currentComment = findComment(id);
|
||||
commentRepository.delete(currentComment);
|
||||
currentComment.getPost().getComments().remove(currentComment);
|
||||
currentComment.getCustomer().getComments().remove(currentComment);
|
||||
return currentComment;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void deleteAllComments() {
|
||||
commentRepository.deleteAll();
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public List<Comment> searchComments(String tag) {
|
||||
return commentRepository.searchComments(tag);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
package ru.ulstu.is.labwork.Lab4.services;
|
||||
|
||||
import javax.persistence.EntityNotFoundException;
|
||||
import javax.transaction.Transactional;
|
||||
|
||||
import org.springframework.util.StringUtils;
|
||||
import ru.ulstu.is.labwork.Lab4.DTO.CustomerDto;
|
||||
import ru.ulstu.is.labwork.Lab4.Exceptions.CustomerNotFoundException;
|
||||
import ru.ulstu.is.labwork.Lab4.Exceptions.JwtException;
|
||||
import ru.ulstu.is.labwork.Lab4.model.Customer;
|
||||
import ru.ulstu.is.labwork.Lab4.model.UserRole;
|
||||
import ru.ulstu.is.labwork.Lab4.repositories.CustomerRepository;
|
||||
import ru.ulstu.is.labwork.Lab4.security.JwtProvider;
|
||||
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 ru.ulstu.is.labwork.Lab4.util.validation.ValidatorUtil;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@Service
|
||||
public class CustomerService implements UserDetailsService {
|
||||
private final CustomerRepository customerRepository;
|
||||
private final ValidatorUtil validatorUtil;
|
||||
private final PasswordEncoder passwordEncoder;
|
||||
private final JwtProvider jwtProvider;
|
||||
|
||||
public CustomerService(CustomerRepository customerRepository,
|
||||
ValidatorUtil validatorUtil,
|
||||
PasswordEncoder passwordEncoder,
|
||||
JwtProvider jwtProvider) {
|
||||
this.customerRepository = customerRepository;
|
||||
this.validatorUtil = validatorUtil;
|
||||
this.passwordEncoder = passwordEncoder;
|
||||
this.jwtProvider = jwtProvider;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public Customer findCustomer(Long id) {
|
||||
return customerRepository.findById(id).orElseThrow(() -> new CustomerNotFoundException(id));
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public List<Customer> findAllCustomers() {
|
||||
return customerRepository.findAll();
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public Customer addCustomer(String username, String password) {
|
||||
Customer customer = new Customer(username, passwordEncoder.encode(password));
|
||||
validatorUtil.validate(customer);
|
||||
return customerRepository.save(customer);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public Customer addCustomer(String username, String password, UserRole role) {
|
||||
Customer customer = new Customer(username, passwordEncoder.encode(password), role);
|
||||
validatorUtil.validate(customer);
|
||||
return customerRepository.save(customer);
|
||||
}
|
||||
|
||||
|
||||
@Transactional
|
||||
public Customer updateCustomer(Long id, String username, String password) {
|
||||
Customer customer = findCustomer(id);
|
||||
customer.setUsername(username);
|
||||
customer.setPassword(passwordEncoder.encode(password));
|
||||
validatorUtil.validate(customer);
|
||||
return customerRepository.save(customer);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public Customer deleteCustomer(Long id) {
|
||||
Customer customer = findCustomer(id);
|
||||
customerRepository.delete(customer);
|
||||
return customer;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void deleteAllCustomers() {
|
||||
customerRepository.deleteAll();
|
||||
}
|
||||
|
||||
public Customer findByUsername(String username) {
|
||||
return customerRepository.findByUsername(username);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
|
||||
final Customer customerEntity = findByUsername(username);
|
||||
if (customerEntity == null) {
|
||||
throw new UsernameNotFoundException(username);
|
||||
}
|
||||
return new org.springframework.security.core.userdetails.User(
|
||||
customerEntity.getUsername(), customerEntity.getPassword(), Collections.singleton(customerEntity.getRole()));
|
||||
}
|
||||
|
||||
public String loginAndGetToken(CustomerDto customerDto) {
|
||||
try {
|
||||
final Customer customer = findByUsername(customerDto.getUsername());
|
||||
if (customer == null) {
|
||||
throw new Exception("Login not found" + customerDto.getUsername());
|
||||
}
|
||||
if (!passwordEncoder.matches(customerDto.getPassword(), customer.getPassword())) {
|
||||
throw new Exception("User not found" + customer.getUsername());
|
||||
}
|
||||
return jwtProvider.generateToken(customer.getUsername());
|
||||
}
|
||||
catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
101
src/main/java/ru/ulstu/is/labwork/Lab4/services/PostService.java
Normal file
101
src/main/java/ru/ulstu/is/labwork/Lab4/services/PostService.java
Normal file
@@ -0,0 +1,101 @@
|
||||
package ru.ulstu.is.labwork.Lab4.services;
|
||||
|
||||
import javax.persistence.EntityNotFoundException;
|
||||
import javax.transaction.Transactional;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import ru.ulstu.is.labwork.Lab4.DTO.PostDto;
|
||||
import ru.ulstu.is.labwork.Lab4.Exceptions.PostNotFoundException;
|
||||
import ru.ulstu.is.labwork.Lab4.model.Comment;
|
||||
import ru.ulstu.is.labwork.Lab4.model.Customer;
|
||||
import ru.ulstu.is.labwork.Lab4.model.Post;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StringUtils;
|
||||
import ru.ulstu.is.labwork.Lab4.repositories.CommentRepository;
|
||||
import ru.ulstu.is.labwork.Lab4.repositories.PostRepository;
|
||||
import ru.ulstu.is.labwork.Lab4.util.validation.ValidatorUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
@Service
|
||||
public class PostService {
|
||||
private final PostRepository postRepository;
|
||||
private final CommentRepository commentRepository;
|
||||
private final ValidatorUtil validatorUtil;
|
||||
|
||||
public PostService(PostRepository postRepository,
|
||||
CommentRepository commentRepository, ValidatorUtil validatorUtil) {
|
||||
this.postRepository = postRepository;
|
||||
this.commentRepository = commentRepository;
|
||||
this.validatorUtil = validatorUtil;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public Post findPost(Long id) {
|
||||
return postRepository.findById(id).orElseThrow(() -> new PostNotFoundException(id));
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public List<Post> findAllPosts() {
|
||||
return postRepository.findAll();
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public Post addPost(Customer customer, String title, String content) {
|
||||
Post post = new Post(customer, title, content);
|
||||
validatorUtil.validate(post);
|
||||
customer.getPosts().add(post);
|
||||
return postRepository.save(post);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public Post updatePost(Long id, String title, String content) {
|
||||
Post post = findPost(id);
|
||||
post.setTitle(title);
|
||||
post.setContent(content);
|
||||
validatorUtil.validate(post);
|
||||
return postRepository.save(post);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public Post deletePost(Long id) {
|
||||
Post post = findPost(id);
|
||||
post.getCustomer().getPosts().remove(post);
|
||||
postRepository.delete(post);
|
||||
return post;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void deleteAllPosts() {
|
||||
postRepository.deleteAll();
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public List<PostDto> searchPosts(String search) {
|
||||
var posts = new ArrayList<>(postRepository.searchPosts(search));
|
||||
var comments = commentRepository.searchComments(search);
|
||||
for (var post: posts) {
|
||||
post.getComments().clear();
|
||||
}
|
||||
for (var comment: comments) {
|
||||
boolean found = false;
|
||||
for (var post: posts) {
|
||||
if (Objects.equals(comment.getPost().getId(), post.getId())) {
|
||||
post.getComments().add(comment);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
var newPost = comment.getPost();
|
||||
newPost.getComments().clear();
|
||||
newPost.getComments().add(comment);
|
||||
posts.add(newPost);
|
||||
}
|
||||
}
|
||||
return posts.stream().map(PostDto::new).toList();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package ru.ulstu.is.labwork.Lab4.util.error;
|
||||
|
||||
import ru.ulstu.is.labwork.Lab4.Exceptions.CommentNotFoundException;
|
||||
import ru.ulstu.is.labwork.Lab4.Exceptions.CustomerNotFoundException;
|
||||
import ru.ulstu.is.labwork.Lab4.Exceptions.PostNotFoundException;
|
||||
import ru.ulstu.is.labwork.Lab4.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 org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@ControllerAdvice(annotations = RestController.class)
|
||||
public class AdviceController {
|
||||
@ExceptionHandler({
|
||||
CommentNotFoundException.class,
|
||||
CustomerNotFoundException.class,
|
||||
PostNotFoundException.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,9 @@
|
||||
package ru.ulstu.is.labwork.Lab4.util.validation;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public class ValidationException extends RuntimeException {
|
||||
public ValidationException(Set<String> errors) {
|
||||
super(String.join("\n", errors));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package ru.ulstu.is.labwork.Lab4.util.validation;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.validation.ConstraintViolation;
|
||||
import javax.validation.Validation;
|
||||
import javax.validation.Validator;
|
||||
import javax.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()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,44 +2,12 @@ package ru.ulstu.is.labwork;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@SpringBootApplication
|
||||
@RestController
|
||||
public class LabworkApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(LabworkApplication.class, args);
|
||||
}
|
||||
|
||||
@GetMapping("/good_morning")
|
||||
public String good_morning(@RequestParam(value = "name", defaultValue = "Sir") String name) {
|
||||
return String.format("Good morning, %s!", name);
|
||||
}
|
||||
@GetMapping("/sum")
|
||||
public int sum( @RequestParam(value = "num1", defaultValue = "0") int num1,
|
||||
@RequestParam(value = "num2", defaultValue = "0") int num2)
|
||||
{
|
||||
return num1 + num2;
|
||||
}
|
||||
@GetMapping("/sub")
|
||||
public int sub( @RequestParam(value = "num1", defaultValue = "0") int num1,
|
||||
@RequestParam(value = "num2", defaultValue = "0") int num2)
|
||||
{
|
||||
return num1 - num2;
|
||||
}
|
||||
@GetMapping("/mul")
|
||||
public int mul( @RequestParam(value = "num1", defaultValue = "0") int num1,
|
||||
@RequestParam(value = "num2", defaultValue = "0") int num2)
|
||||
{
|
||||
return num1 * num2;
|
||||
}
|
||||
@GetMapping("/div")
|
||||
public int div( @RequestParam(value = "num1", defaultValue = "0") int num1,
|
||||
@RequestParam(value = "num2", defaultValue = "0") int num2)
|
||||
{
|
||||
return num1 / num2;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
package ru.ulstu.is.labwork;
|
||||
|
||||
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;
|
||||
import ru.ulstu.is.labwork.Lab4.security.JwtFilter;
|
||||
|
||||
@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")));
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,33 @@
|
||||
package ru.ulstu.is.labwork;
|
||||
|
||||
import ru.ulstu.is.labwork.Lab4.security.SecurityConfiguration;
|
||||
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 {
|
||||
public static final String REST_API = OpenAPI30Configuration.API_PREFIX;
|
||||
|
||||
@Override
|
||||
public void addViewControllers(ViewControllerRegistry registry) {
|
||||
WebMvcConfigurer.super.addViewControllers(registry);
|
||||
registry.addViewController("login");
|
||||
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("*");
|
||||
|
||||
@@ -1 +1,13 @@
|
||||
|
||||
spring.main.banner-mode=off
|
||||
#server.port=8080
|
||||
spring.datasource.url=jdbc:h2:file:./data
|
||||
spring.datasource.driverClassName=org.h2.Driver
|
||||
spring.datasource.username=sa
|
||||
spring.datasource.password=password
|
||||
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
|
||||
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
|
||||
|
||||
103
src/main/resources/templates/admin.html
Normal file
103
src/main/resources/templates/admin.html
Normal file
@@ -0,0 +1,103 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{default}">
|
||||
<head>
|
||||
</head>
|
||||
<body>
|
||||
<div layout:fragment="content">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="row mb-5">
|
||||
<p class='is-center h2'>Профили</p>
|
||||
</div>
|
||||
<div class="row mb-5">
|
||||
<div class="col"></div>
|
||||
<div class="col-10 is-center">
|
||||
<button class="button primary" data-bs-toggle="modal" data-bs-target="#customerCreate">
|
||||
Добавить нового пользователя
|
||||
</button>
|
||||
</div>
|
||||
<div class="col"></div>
|
||||
</div>
|
||||
|
||||
<p class='h3 is-center row mb-5'>Список профилей</p>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="row card mb-3">
|
||||
<div class="row">
|
||||
<div class="col-3 is-left h3 fw-bold" th:text="ID"></div>
|
||||
<div class="col-3 is-center h3 fw-bold" th:text="Никнейм"></div>
|
||||
<div class="col-3 is-right h3 fw-bold" th:text="Пароль"></div>
|
||||
<div class="col-2"></div>
|
||||
<div class="col-1"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div th:each="customer: ${customers}" class="row card mb-3">
|
||||
<div class="row">
|
||||
<div class="col-3 is-left h3" th:text="${customer.id}"></div>
|
||||
<a th:href="${ '/customers/' + customer.id}" class="col-3 is-center h3" th:text="${customer.username}"></a>
|
||||
<div class="col-3 is-right h3"><span th:text="${customer.password}" style="text-overflow: ellipsis; overflow: hidden; max-width: 10ch; white-space: nowrap"></span></div>
|
||||
|
||||
<button style="max-width: 66px; max-height: 38px;" th:data-username="${customer.username}" th:data-password="${customer.password}" th:data-id="${customer.id}" th:onclick="|prepareEditModal(this)|" class="button primary outline is-right" data-bs-toggle="modal" data-bs-target="#customerEdit"><i class="fa fa-pencil" aria-hidden="true"></i></button>
|
||||
|
||||
<form th:action="@{/admin/delete/{id}(id=${customer.id})}" method="post" class="col-1 is-right">
|
||||
<button class="button dark outline is-right" style="max-width: 66px; max-height: 38px;" type="submit"><i class="fa fa-trash" aria-hidden="true"></i></button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Modal -->
|
||||
<div class="modal fade" id="customerCreate" tabindex="-1" role="dialog" aria-labelledby="customerCreateLabel" aria-hidden="true">
|
||||
<div class="modal-dialog" role="document">
|
||||
<form class="modal-content" th:action="@{/admin/}" method="post">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="customerCreateLabel">Создать профиль</h5>
|
||||
</div>
|
||||
<div class="modal-body text-center">
|
||||
<p>Логин</p>
|
||||
<textarea name="username" id="usernameTextC" cols="30" rows="1"></textarea>
|
||||
<p>Пароль</p>
|
||||
<textarea name="password" id="passwordTextC" cols="30" rows="1"></textarea>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Закрыть</button>
|
||||
<button type="submit" class="btn btn-primary">Сохранить</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal -->
|
||||
<div class="modal fade" id="customerEdit" tabindex="-1" role="dialog" aria-labelledby="customerEditLabel" aria-hidden="true">
|
||||
<div class="modal-dialog" role="document">
|
||||
<form class="modal-content" id="edit-customer-form" method="post">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="customerEditLabel">Редактировать профиль</h5>
|
||||
</div>
|
||||
<div class="modal-body text-center">
|
||||
<p>Логин</p>
|
||||
<textarea name="username" id="usernameTextE" cols="30" rows="1"></textarea>
|
||||
<p>Пароль</p>
|
||||
<textarea name="password" id="passwordTextE" cols="30" rows="1"></textarea>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Закрыть</button>
|
||||
<button type="submit" class="btn btn-primary">Изменить</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
<th:block layout:fragment="scripts">
|
||||
<script th:inline="javascript">
|
||||
function prepareEditModal(btn) {
|
||||
document.getElementById('usernameTextE').value = btn.getAttribute("data-username");
|
||||
document.getElementById('passwordTextE').value = btn.getAttribute("data-password");
|
||||
document.getElementById('edit-customer-form').setAttribute('action', '/admin/' + btn.getAttribute("data-id"));
|
||||
}
|
||||
</script>
|
||||
</th:block>
|
||||
</html>
|
||||
79
src/main/resources/templates/customers.html
Normal file
79
src/main/resources/templates/customers.html
Normal file
@@ -0,0 +1,79 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="" xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{default}">
|
||||
<head>
|
||||
</head>
|
||||
<body>
|
||||
<div layout:fragment="content">
|
||||
<div class="ms-5">
|
||||
<p class='h4 m-3'>Профили</p>
|
||||
<p class='h4 ms-3'>Список</p>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-5">
|
||||
<div th:each="customer: ${customers}" class="row card is-left mb-3">
|
||||
<div class="row is-left">
|
||||
<div class="row is-left h3" th:text="${customer.username}"></div>
|
||||
</div>
|
||||
|
||||
<div class="row" th:if="!${#arrays.isEmpty(customer.comments)}">
|
||||
<p class="h4">Посты:</p>
|
||||
<div class="col">
|
||||
<div th:each="post: ${customer.posts}" class="row is-left card mb-3">
|
||||
<div class="row is-left h4" th:text="${post.title}"></div>
|
||||
<div class="row is-left h5" th:text="${post.content}"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row" th:if="!${#arrays.isEmpty(customer.comments)}">
|
||||
<p class="h4">Комментарии:</p>
|
||||
<div class="col">
|
||||
<div th:each="comment: ${customer.comments}" class="row is-left card mb-3">
|
||||
<div class="row is-left h5" th:text="${'"' + comment.content + '"' + ' - к посту ' + '"' + comment.postTitle + '"' + ' от пользователя ' + comment.postAuthor}"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row" th:if="${currentCustomerId == customer.id}">
|
||||
<form th:action="@{/customers/delete/{id}(id=${customer.id})}" method="post" class="col">
|
||||
<button class="button dark outline is-full-width" type="submit">Удалить</button>
|
||||
</form>
|
||||
<button th:data-username="${customer.username}" th:data-password="${customer.password}" th:data-id="${customer.id}" th:onclick="|prepareEditModal(this)|" class="col button primary outline" data-bs-toggle="modal" data-bs-target="#customerEdit">Редактировать</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal -->
|
||||
<div class="modal fade" id="customerEdit" tabindex="-1" role="dialog" aria-labelledby="customerEditLabel" aria-hidden="true">
|
||||
<div class="modal-dialog" role="document">
|
||||
<form class="modal-content" id="edit-customer-form" method="post">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="customerEditLabel">Изменение пользователя</h5>
|
||||
</div>
|
||||
<div class="modal-body text-center">
|
||||
<p>Имя пользователя</p>
|
||||
<textarea name="username" id="usernameTextE" cols="30" rows="1"></textarea>
|
||||
<p>Пароль</p>
|
||||
<textarea name="password" id="passwordTextE" cols="30" rows="1"></textarea>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Отмена</button>
|
||||
<button type="submit" class="btn btn-primary">Применить</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
<th:block layout:fragment="scripts">
|
||||
<script th:inline="javascript">
|
||||
function prepareEditModal(btn) {
|
||||
document.getElementById('usernameTextE').value = btn.getAttribute("data-username");
|
||||
document.getElementById('passwordTextE').value = btn.getAttribute("data-password");
|
||||
document.getElementById('edit-customer-form').setAttribute('action', '/customers/' + btn.getAttribute("data-id"));
|
||||
}
|
||||
</script>
|
||||
</th:block>
|
||||
</html>
|
||||
32
src/main/resources/templates/default.html
Normal file
32
src/main/resources/templates/default.html
Normal file
@@ -0,0 +1,32 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="" xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
|
||||
xmlns:sec="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-Zenh87qX5JnK2Jl0vWa8Ck2rdkQ2Bzep5IDxbcnCeuOxjzrPF/et3URy9Bv1WTRi" crossorigin="anonymous">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<link rel="stylesheet" href="https://unpkg.com/chota@latest">
|
||||
<title>LabWork05 - Social Network</title>
|
||||
</head>
|
||||
<body class="container">
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-OERcA2EqjJCMA+/3y+gxIOqMEjwtxJY7qPCqsdltbNJuaOe923+mo//f6V8Qbsw3" crossorigin="anonymous"></script>
|
||||
<div>
|
||||
<div class="nav row" th:with="activeLink=${#request.requestURI}">
|
||||
<div class="nav-left col-4">
|
||||
<a href="/customers/" class="button primary" th:classappend="${#strings.startsWith(activeLink, '/customers')} ? 'clear' : ''">Профили</a>
|
||||
<a href="/feed" class="button primary" th:classappend="${#strings.startsWith(activeLink, '/feed')} ? 'clear' : ''">Посты</a>
|
||||
<a sec:authorize="hasRole('ROLE_ADMIN')" href="/admin/" class="button primary" th:classappend="${#strings.startsWith(activeLink, '/admin')} ? 'clear' : ''">Администрирование</a>
|
||||
<a sec:authorize="!isAuthenticated()" href="/login" class="button primary" th:classappend="${#strings.startsWith(activeLink, '/login')} ? 'clear' : ''">Войти</a>
|
||||
<a sec:authorize="isAuthenticated()" href="/logout" class="button primary" th:classappend="${#strings.startsWith(activeLink, '/logout')} ? 'clear' : ''">Выйти</a>
|
||||
</div>
|
||||
</div>
|
||||
<div layout:fragment="content">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
<th:block layout:fragment="scripts">
|
||||
</th:block>
|
||||
</html>
|
||||
13
src/main/resources/templates/error.html
Normal file
13
src/main/resources/templates/error.html
Normal file
@@ -0,0 +1,13 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en"
|
||||
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
|
||||
layout:decorate="~{default}" xmlns:th="http://www.w3.org/1999/xhtml">
|
||||
<body>
|
||||
<div class="container" layout:fragment="content">
|
||||
<div class="alert alert-danger">
|
||||
<span th:text="${error}"></span>
|
||||
</div>
|
||||
<a href="/" class="is-center">На главную</a>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
144
src/main/resources/templates/feed.html
Normal file
144
src/main/resources/templates/feed.html
Normal file
@@ -0,0 +1,144 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{default}">
|
||||
<head>
|
||||
</head>
|
||||
<body>
|
||||
<div layout:fragment="content">
|
||||
<div class="ms-5">
|
||||
<p class='h3 m-3'>Посты</p>
|
||||
|
||||
<div class="col-5">
|
||||
<form class="row mb-5" action="/feed">
|
||||
<input type="search" name="search" class="col-7">
|
||||
<button class="button primary col" type="submit">Найти</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="row" th:if="${currentCustomerId > 0}">
|
||||
<form class="col-7" th:action="@{/posts/}" method="post">
|
||||
<input name="customerId" type="text" style="display: none;" th:value="${currentCustomerId}">
|
||||
<div class="row is-left">
|
||||
<p class="col-2 is-left mb-2">Заголовок:</p>
|
||||
<input name="title" type="text" class="col-5" id="createPostTitle" />
|
||||
</div>
|
||||
<div class="row is-left">
|
||||
<p class="col-2 is-left mb-2">Текст:</p>
|
||||
<textarea name="content" type="textarea" class="col-5" id="createPostContent"></textarea>
|
||||
</div>
|
||||
<div class="row is-left">
|
||||
<button type='submit' class="button primary col-7">
|
||||
Опубликовать
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div th:unless="${#arrays.isEmpty(posts)}" class="row">
|
||||
<div class="col-5">
|
||||
<div class="row mb-5 card" th:each="post: ${posts}">
|
||||
<div class="col">
|
||||
|
||||
<div class="row is-left my-3">
|
||||
<p><a th:href="${'/customers/' + post.customerId}" class="text-primary" th:text="${post.customerName}"></a></p>
|
||||
</div>
|
||||
|
||||
<div class="row text-left">
|
||||
<span class="h2" th:text="${post.title}"></span>
|
||||
<span class="h3" th:text="${post.content}"></span>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div th:unless="${#arrays.isEmpty(post.comments)}">
|
||||
<p class="row h3 is-left my-3">Комментарии</p>
|
||||
|
||||
<div class="row text-left mb-4 card" th:each="comment: ${post.comments}">
|
||||
<div class="row is-left">
|
||||
<span class="h2 text-primary" th:text="${comment.customerName}"></span>
|
||||
<span class="h3" th:text="${comment.content}"></span>
|
||||
</div>
|
||||
<div th:if="${currentCustomerId == comment.customerId}" class="row mt-3">
|
||||
<button th:data-content="${comment.content}" th:data-id="${comment.id}" th:onclick="|prepareCommentEditModal(this)|" class="button primary outline col" data-bs-toggle="modal" data-bs-target="#commentEdit">Изменить</button>
|
||||
<form th:action="@{/comments/delete/{id}(id=${comment.id})}" method="post" class="col">
|
||||
<button type="submit" class="button error is-full-width">Удалить</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form class="row" th:if="${currentCustomerId != -1}" th:action="@{/comments/}" method="post">
|
||||
<input name="content" type="text" class="col-6"/>
|
||||
<input name="customerId" type="text" style="display: none;" th:value="${currentCustomerId}">
|
||||
<input name="postId" type="text" style="display: none;" th:value="${post.id}">
|
||||
<button type="submit" class="button col-6 secondary outline">Комментировать</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="row" th:if="${currentCustomerId == post.customerId}">
|
||||
<form class="col" th:action="@{/posts/delete/{id}(id=${post.id})}" method="post">
|
||||
<button type="submit" class="is-full-width button dark outline">Удалить пост</button>
|
||||
</form>
|
||||
<button th:data-customer="${currentCustomerId}" th:data-id="${post.id}" th:data-title="${post.title}" th:data-content="${post.content}" type="button" th:onclick="|preparePostEditModal(this)|" class="col button primary outline" data-bs-toggle="modal" data-bs-target="#postEdit">Изменить пост</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Modal -->
|
||||
<div class="modal fade" id="postEdit" tabindex="-1" role="dialog" aria-labelledby="postEditLabel" aria-hidden="true">
|
||||
<div class="modal-dialog" role="document">
|
||||
<form method="post" class="modal-content" id="editPost">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="postEditLabel">Редактировать пост</h5>
|
||||
</div>
|
||||
<div class="modal-body text-center">
|
||||
<p>Заголовок</p>
|
||||
<textarea name="title" id="editModalTitle" cols="30" rows="1"></textarea>
|
||||
<p>Содержание</p>
|
||||
<textarea name="content" id="editModalPostContent" cols="30" rows="1"></textarea>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Закрыть</button>
|
||||
<button type="submit" class="btn btn-primary">Изменить</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal -->
|
||||
<div class="modal fade" id="commentEdit" tabindex="-1" role="dialog" aria-labelledby="commentEditLabel" aria-hidden="true">
|
||||
<div class="modal-dialog" role="document">
|
||||
<form method="post" class="modal-content" id="editComment">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="commentEditLabel">Изменить комментарий</h5>
|
||||
</div>
|
||||
<div class="modal-body text-center">
|
||||
<textarea name='content' id="editModalText" cols="30" rows="1"></textarea>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Закрыть</button>
|
||||
<button type="submit" class="btn btn-primary">Изменить</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
<th:block layout:fragment="scripts">
|
||||
<script th:inline="javascript">
|
||||
function prepareCommentEditModal(btn) {
|
||||
document.getElementById('editModalText').value = btn.getAttribute("data-content");
|
||||
document.getElementById('editComment').setAttribute('action', '/comments/' + btn.getAttribute("data-id"));
|
||||
}
|
||||
|
||||
function preparePostEditModal(btn) {
|
||||
document.getElementById('editModalTitle').value = btn.getAttribute("data-title");
|
||||
document.getElementById('editModalPostContent').value = btn.getAttribute("data-content");
|
||||
document.getElementById('editPost').setAttribute('action', '/posts/' + btn.getAttribute("data-id"));
|
||||
}
|
||||
</script>
|
||||
</th:block>
|
||||
</html>
|
||||
31
src/main/resources/templates/login.html
Normal file
31
src/main/resources/templates/login.html
Normal file
@@ -0,0 +1,31 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{default}">
|
||||
<head>
|
||||
</head>
|
||||
<body>
|
||||
<div layout:fragment="content">
|
||||
<div class="row">
|
||||
<div class="col"></div>
|
||||
<form class="col-6" action="/login" method="post">
|
||||
<div class="row">
|
||||
<label>Логин: <input type="text" name="username" required /></label>
|
||||
</div>
|
||||
<div class="row">
|
||||
<label>Пароль: <input type="password" name="password" id="password" required /></label>
|
||||
</div>
|
||||
<div class="row mt-3">
|
||||
<div class="col-4"></div>
|
||||
<button type="submit" class="button primary col" id="enter">Войти</button>
|
||||
<div class="col-4"></div>
|
||||
</div>
|
||||
<div class="row mt-5">
|
||||
<a class="button primary outline" href="/signup">Регистрация</a>
|
||||
</div>
|
||||
</form>
|
||||
<div class="col"></div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
<th:block layout:fragment="scripts">
|
||||
</th:block>
|
||||
</html>
|
||||
44
src/main/resources/templates/signup.html
Normal file
44
src/main/resources/templates/signup.html
Normal file
@@ -0,0 +1,44 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{default}">
|
||||
<head>
|
||||
</head>
|
||||
<body>
|
||||
<div layout:fragment="content">
|
||||
<div class="row">
|
||||
<div class="col"></div>
|
||||
<form class="col-6" action="/signup" method="post">
|
||||
<div class="row">
|
||||
<label>Логин: <input type="text" name="username" required /></label>
|
||||
</div>
|
||||
<div class="row">
|
||||
<label>Пароль: <input type="password" name="password" id="password" th:onchange="|check()|" required /></label>
|
||||
</div>
|
||||
<div class="row">
|
||||
<label>Пароль повторно: <input type="password" id="confirm-password" th:onchange="|check()|" required /></label>
|
||||
</div>
|
||||
<div class="row mt-3">
|
||||
<div class="col-4"></div>
|
||||
<button type="submit" class="button primary col" id="enter" disabled>Регистрация</button>
|
||||
<div class="col-4"></div>
|
||||
</div>
|
||||
</form>
|
||||
<div class="col"></div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
<th:block layout:fragment="scripts">
|
||||
<script th:inline="javascript">
|
||||
function check() {
|
||||
const password = document.getElementById("password")
|
||||
const confpassword = document.getElementById("confirm-password")
|
||||
const button = document.getElementById("enter")
|
||||
|
||||
if (password.value !== confpassword.value) {
|
||||
button.setAttribute('disabled', 'disabled')
|
||||
} else {
|
||||
button.removeAttribute('disabled')
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</th:block>
|
||||
</html>
|
||||
@@ -1,13 +1,118 @@
|
||||
package ru.ulstu.is.labwork;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
@SpringBootTest
|
||||
class LabworkApplicationTests {
|
||||
|
||||
@Test
|
||||
void contextLoads() {
|
||||
}
|
||||
// @Autowired
|
||||
// CustomerService customerService;
|
||||
//
|
||||
// @Autowired
|
||||
// CommentService commentService;
|
||||
//
|
||||
// @Autowired
|
||||
// PostService postService;
|
||||
//
|
||||
// @Test
|
||||
// void testCustomers() {
|
||||
// commentService.deleteAllComment();
|
||||
// postService.deleteAllPosts();
|
||||
// customerService.deleteAllCustomers();
|
||||
//
|
||||
// Customer c1 = customerService.addCustomer("first", "1");
|
||||
// Customer c2 = customerService.addCustomer("second", "2");
|
||||
// Customer c3 = customerService.addCustomer("third", "3");
|
||||
//
|
||||
// Assertions.assertEquals("first", c1.getUsername());
|
||||
// Assertions.assertEquals("second", c2.getUsername());
|
||||
// Assertions.assertEquals("third", c3.getUsername());
|
||||
//
|
||||
// Assertions.assertEquals(c1, customerService.findCustomer(c1.getId()));
|
||||
//
|
||||
// customerService.deleteCustomer(c2.getId());
|
||||
//
|
||||
// Assertions.assertEquals(2, customerService.findAllCustomers().size());
|
||||
//
|
||||
// Customer c4 = customerService.updateCustomer(c3.getId(), "fourth", "4");
|
||||
//
|
||||
// Assertions.assertNotEquals(c3.getUsername(), c4.getUsername());
|
||||
// Assertions.assertNotEquals(c3.getHashedPassword(), c4.getHashedPassword());
|
||||
//
|
||||
// commentService.deleteAllComment();
|
||||
// postService.deleteAllPosts();
|
||||
// customerService.deleteAllCustomers();
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// void testPost() {
|
||||
// commentService.deleteAllComment();
|
||||
// postService.deleteAllPosts();
|
||||
// customerService.deleteAllCustomers();
|
||||
//
|
||||
// Customer c1 = customerService.addCustomer("first", "1");
|
||||
// Customer c2 = customerService.addCustomer("second", "2");
|
||||
//
|
||||
// Post p1 = postService.addPost(c1, "first title", "nonsense");
|
||||
// Post p2 = postService.addPost(c2, "second title", "ordinal");
|
||||
//
|
||||
// Assertions.assertEquals(2, postService.findAllPosts().size());
|
||||
//
|
||||
// Assertions.assertEquals(p1.getCustomer(), c1);
|
||||
// Assertions.assertEquals(p2.getCustomer(), c2);
|
||||
//
|
||||
// Assertions.assertEquals(c1.getPosts().get(0), p1);
|
||||
// Assertions.assertEquals(c2.getPosts().get(0), p2);
|
||||
//
|
||||
// Assertions.assertEquals(p1, postService.findPost(p1.getId()));
|
||||
// Assertions.assertEquals(p2, postService.findPost(p2.getId()));
|
||||
//
|
||||
// Post p3 = postService.addPost(c1, "asdf", "asd");
|
||||
// postService.deletePost(p1.getId());
|
||||
// Assertions.assertEquals(1, customerService.findCustomer(c1.getId()).getPosts().size());
|
||||
//
|
||||
// Post p4 = postService.updatePost(p2.getId(), "third title", "wow");
|
||||
//
|
||||
// Assertions.assertNotEquals(p2.getTitle(), p4.getTitle());
|
||||
// Assertions.assertNotEquals(p2.getContent(), p4.getContent());
|
||||
//
|
||||
// commentService.deleteAllComment();
|
||||
// postService.deleteAllPosts();
|
||||
// customerService.deleteAllCustomers();
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// void testComment() {
|
||||
// commentService.deleteAllComment();
|
||||
// postService.deleteAllPosts();
|
||||
// customerService.deleteAllCustomers();
|
||||
//
|
||||
// Customer c1 = customerService.addCustomer("first", "1");
|
||||
// Customer c2 = customerService.addCustomer("second", "2");
|
||||
//
|
||||
// Post p1 = postService.addPost(c1, "first title", "nonsense");
|
||||
// Post p2 = postService.addPost(c2, "second title", "ordinal");
|
||||
//
|
||||
// Assertions.assertEquals(2, postService.findAllPosts().size());
|
||||
//
|
||||
// Comment com1 = commentService.addComment(c1, p2, "What");
|
||||
// Comment com2 = commentService.addComment(c2, p1, "How");
|
||||
//
|
||||
// Assertions.assertEquals(c1, p2.getComments().get(0).getCustomer());
|
||||
// Assertions.assertEquals(c2, p1.getComments().get(0).getCustomer());
|
||||
//
|
||||
// Comment com3 = commentService.addComment(c1, p1, "Really");
|
||||
//
|
||||
// Assertions.assertEquals(com2, commentService.findComment(p1.getComments().get(0).getId()));
|
||||
//
|
||||
// Comment com4 = commentService.updateComment(com3.getId(), "Not really");
|
||||
//
|
||||
// Assertions.assertNotEquals(com3.getContent(), com4.getContent());
|
||||
// Assertions.assertEquals(com3.getCustomer().getId(), com4.getCustomer().getId());
|
||||
//
|
||||
// commentService.deleteAllComment();
|
||||
// postService.deleteAllPosts();
|
||||
// customerService.deleteAllCustomers();
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user