Compare commits

...

10 Commits

Author SHA1 Message Date
794388d0f6 kaif 2023-06-20 15:49:30 +04:00
e42408413a le maraffette 2023-06-20 14:28:49 +04:00
71dd9b4fde that's all, folks? 2023-06-20 13:51:27 +04:00
263182db1d comments & users start 2023-06-20 13:00:30 +04:00
89c76406ea posts & topics 2023-06-20 12:22:08 +04:00
a038eba691 signup 2023-06-20 10:35:56 +04:00
072a9514b6 services 2023-06-20 10:19:53 +04:00
db18f20ff6 dto + dao 2023-06-20 09:50:11 +04:00
e656fe0664 models 2023-06-20 09:34:21 +04:00
8ae10007bb template 2023-06-19 19:06:46 +04:00
63 changed files with 1138 additions and 5494 deletions

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@ -1,10 +1,8 @@
package com.webproglabs.lab1;
import com.webproglabs.lab1.lab34.controller.ProfileController;
import com.webproglabs.lab1.lab34.controller.mvc_controllers.UserSignupMvcController;
import com.webproglabs.lab1.lab34.jwt.JwtFilter;
import com.webproglabs.lab1.lab34.model.UserRole;
import com.webproglabs.lab1.lab34.services.ProfileService;
import com.webproglabs.lab1.models.UserRole;
import com.webproglabs.lab1.mvc.UserSignupMvcController;
import com.webproglabs.lab1.services.UserService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
@ -17,12 +15,10 @@ 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;
@ -36,18 +32,16 @@ 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 ProfileService userService;
private final JwtFilter jwtFilter;
private final UserService userService;
public SecurityConfiguration(ProfileService userService) {
public SecurityConfiguration(UserService userService) {
this.userService = userService;
this.jwtFilter = new JwtFilter(userService);
createAdminOnStartup();
}
private void createAdminOnStartup() {
final String admin = "admin";
if (userService.findByLogin(admin) == null) {
if (userService.findUserByLogin(admin) == null) {
log.info("Admin user successfully created");
try {
userService.createUser(admin, admin, admin, UserRole.ADMIN);

View File

@ -1,105 +0,0 @@
package com.webproglabs.lab1;
import com.webproglabs.lab1.lab34.controller.ProfileController;
import com.webproglabs.lab1.lab34.controller.mvc_controllers.UserSignupMvcController;
import com.webproglabs.lab1.lab34.jwt.JwtFilter;
import com.webproglabs.lab1.lab34.model.UserRole;
import com.webproglabs.lab1.lab34.services.ProfileService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
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 java.util.LinkedHashMap;
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true)
@Profile("spa")
public class SecurityConfigurationSPA 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 ProfileService userService;
private final JwtFilter jwtFilter;
public SecurityConfigurationSPA(ProfileService userService) {
this.userService = userService;
this.jwtFilter = new JwtFilter(userService);
createAdminOnStartup();
}
private void createAdminOnStartup() {
final String admin = "admin";
if (userService.findByLogin(admin) == null) {
log.info("Admin user successfully created");
try {
userService.createUser(admin, admin, admin, UserRole.ADMIN);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
@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 + "/profile" + ProfileController.URL_LOGIN).permitAll()
.anyRequest()
.authenticated()
.and()
.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class)
.anonymous();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService);
}
@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;
}
}

View File

@ -1,6 +1,5 @@
package com.webproglabs.lab1;
import com.webproglabs.lab1.lab34.OpenAPI30Configuration;
import org.springframework.boot.web.server.ErrorPage;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory;
@ -13,7 +12,7 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfiguration implements WebMvcConfigurer {
public static final String REST_API = OpenAPI30Configuration.API_PREFIX;
public static final String REST_API = "/api";
@Override
public void addViewControllers(ViewControllerRegistry registry) {
WebMvcConfigurer.super.addViewControllers(registry);

View File

@ -1,10 +1,12 @@
package com.webproglabs.lab1.lab34.repository;
package com.webproglabs.lab1.dao;
import com.webproglabs.lab1.lab34.model.Comment;
import com.webproglabs.lab1.models.Comment;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
import java.util.Optional;
public interface CommentRepository extends JpaRepository<Comment, Long> {
List<Comment> findByTextLike(String text);
Optional<Comment> findById(Long Id);
}

View File

@ -0,0 +1,18 @@
package com.webproglabs.lab1.dao;
import com.webproglabs.lab1.models.Post;
import com.webproglabs.lab1.models.Topic;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import java.util.List;
import java.util.Optional;
public interface PostRepository extends JpaRepository<Post, Long> {
List<Post> findByTextLike(String text);
Optional<Post> findById(Long Id);
@Query("SELECT p FROM Post p WHERE p.topic = :topic AND p.text LIKE %:text%")
List<Post> findPostsByTextInTopic(@Param("text") String text, @Param("topic") Topic topic);
}

View File

@ -0,0 +1,15 @@
package com.webproglabs.lab1.dao;
import com.webproglabs.lab1.models.Post;
import com.webproglabs.lab1.models.Topic;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import java.util.List;
import java.util.Optional;
public interface TopicRepository extends JpaRepository<Topic, Long> {
Optional<Topic> findById(Long Id);
}

View File

@ -0,0 +1,12 @@
package com.webproglabs.lab1.dao;
import com.webproglabs.lab1.models.User;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
import java.util.Optional;
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findOneByLoginIgnoreCase(String login);
Optional<User> findById(Long Id);
}

View File

@ -1,11 +1,15 @@
package com.webproglabs.lab1.lab34.controller;
package com.webproglabs.lab1.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.webproglabs.lab1.lab34.model.Comment;
import com.webproglabs.lab1.models.Comment;
import javax.validation.constraints.NotBlank;
public class CommentDto {
private Long id;
@NotBlank
private String text;
@NotBlank
private String authorLogin;
public CommentDto(Comment comment) {
@ -19,5 +23,8 @@ public class CommentDto {
return id;
}
public String getText() {return text;}
public void setText(String text) {
this.text = text;
}
public String getAuthor() {return authorLogin;}
}

View File

@ -1,18 +1,21 @@
package com.webproglabs.lab1.lab34.controller;
package com.webproglabs.lab1.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.webproglabs.lab1.lab34.model.Comment;
import com.webproglabs.lab1.lab34.model.Post;
import com.webproglabs.lab1.lab34.model.Profile;
import com.webproglabs.lab1.models.Comment;
import com.webproglabs.lab1.models.Post;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.util.ArrayList;
import java.util.List;
public class PostDto {
private Long id;
@NotBlank
private String text;
@NotNull
private List<CommentDto> comments = new ArrayList<>();
@NotBlank
private String authorLogin;
public PostDto(Post post){
@ -23,12 +26,16 @@ public class PostDto {
}
this.authorLogin = post.getAuthor().getLogin();
}
public PostDto(){}
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
public long getId() {
return id;
}
public String getText() {return text;}
public void setText(String text) {
this.text = text;
}
public List<CommentDto> getComments() {return comments;}
public String getAuthor() {return authorLogin;}
}

View File

@ -0,0 +1,54 @@
package com.webproglabs.lab1.dto;
import com.webproglabs.lab1.models.Comment;
import com.webproglabs.lab1.models.Post;
import com.webproglabs.lab1.models.Topic;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.util.ArrayList;
import java.util.List;
public class TopicDto {
private Long id;
@NotBlank
private String name;
@NotBlank
private String description;
@NotNull
private List<PostDto> posts = new ArrayList<>();
public TopicDto(Topic topic) {
this.id = topic.getId();
this.name = topic.getName();
this.description = topic.getDescription();
for(Post post: topic.getPosts()){
posts.add(new PostDto(post));
}
}
public TopicDto(){}
public Long getId() {
return id;
}
public String getName() {
return name;
}
public String getDescription() {
return description;
}
public List<PostDto> getPosts() {
return posts;
}
public void setName(String name) {
this.name = name;
}
public void setDescription(String description) {
this.description = description;
}
}

View File

@ -1,35 +1,41 @@
package com.webproglabs.lab1.lab34.controller;
package com.webproglabs.lab1.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.webproglabs.lab1.lab34.model.Comment;
import com.webproglabs.lab1.lab34.model.Post;
import com.webproglabs.lab1.lab34.model.Profile;
import com.webproglabs.lab1.lab34.model.UserRole;
import com.webproglabs.lab1.models.Comment;
import com.webproglabs.lab1.models.Post;
import com.webproglabs.lab1.models.User;
import com.webproglabs.lab1.models.UserRole;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.util.ArrayList;
import java.util.List;
public class ProfileDto {
public class UserDto {
private Long id;
@NotBlank
private String login;
@NotBlank
private String password;
@NotNull
private List<CommentDto> comments = new ArrayList<>();
@NotNull
private List<PostDto> posts = new ArrayList<>();
@NotNull
private UserRole role;
public ProfileDto(){}
public ProfileDto(Profile profile){
this.id = profile.getId();
this.login = profile.getLogin();
this.password = profile.getPassword();
this.role = profile.getRole();
for(Comment comment: profile.getComments()){
public UserDto(){}
public UserDto(User user){
this.id = user.getId();
this.login = user.getLogin();
this.password = user.getPassword();
this.role = user.getRole();
for(Comment comment: user.getComments()){
comments.add(new CommentDto(comment));
}
for (Post post: profile.getPosts()) {
for (Post post: user.getPosts()) {
posts.add(new PostDto(post));
}
}
@ -41,10 +47,9 @@ public class ProfileDto {
public String getLogin() {return login;}
public void setLogin(String login) {this.login = login;}
public String getPassword() {return password;}
public UserRole getRole() {return role;}
public void setPassword(String password) {this.password = password;}
public List<CommentDto> getComments() {return comments;}
public List<PostDto> getPosts() {return posts;}
}

View File

@ -1,17 +1,17 @@
package com.webproglabs.lab1.lab34.controller.mvc_controllers;
package com.webproglabs.lab1.dto;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;
public class UserSignupDto {
@NotBlank
@Size(min = 3, max = 64)
@Size(min = 4, max = 64)
private String login;
@NotBlank
@Size(min = 6, max = 64)
@Size(min = 4, max = 64)
private String password;
@NotBlank
@Size(min = 6, max = 64)
@Size(min = 4, max = 64)
private String passwordConfirm;
public String getLogin() {

View File

@ -1,44 +0,0 @@
package com.webproglabs.lab1.lab1;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Random;
@RestController
public class Lab1Controller {
@GetMapping("/ilove")
public String ilove(@RequestParam(value = "thing", defaultValue = "cookies") String thing) {
return String.format("I love %s!", thing);
}
@GetMapping("/ask")
public String question(@RequestParam(value = "question", defaultValue = "Задайте вопрос") String question) {
if (question.contains("Задайте вопрос")) return question;
String[] answers = new String[] {
"Не знаю",
"Да",
"Нет",
"Спросите у мамы"
};
Random random = new Random();
return answers[random.nextInt(4)];
}
@GetMapping("/touppercase")
public String tuupper(@RequestParam(value = "content", defaultValue = "Введите строку") String content) {
return content.toUpperCase();
}
@GetMapping("/date")
public String date () {
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
LocalDateTime now = LocalDateTime.now();
return dtf.format(now);
}
}

View File

@ -1,27 +0,0 @@
package com.webproglabs.lab1.lab2.config;
import com.webproglabs.lab1.lab2.domain.Lab2Int;
import com.webproglabs.lab1.lab2.domain.Lab2String;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class Lab2Config {
@Bean(value="int")
public Lab2Int createLab2Int() {
return new Lab2Int();
}
@Bean(value="String")
public Lab2String createLab2String() {
return new Lab2String();
}
}
//фронт на жсон сервер был когда то
//сделать на основе старых сущностей сущности в жпа
//делаем сущности, зависимость для жпа, сервис для круд операций,
//тесты
//фронт не нужно
//двунправленная связь один ко многим

View File

@ -1,35 +0,0 @@
package com.webproglabs.lab1.lab2.controllers;
import com.webproglabs.lab1.lab2.Lab2Service;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class Lab2Controller {
private final Lab2Service lab2Service;
public Lab2Controller(Lab2Service lab2Service) {this.lab2Service = lab2Service;}
@GetMapping("/lab2/sum")
public Object sum(@RequestParam (value = "first", defaultValue = "hello") String first,
@RequestParam (value = "second", defaultValue = "world") String second,
@RequestParam (value = "type") String type) {
return lab2Service.sum(first, second, type);
}
@GetMapping("/lab2/makeitbigger")
public Object makeItBigger(@RequestParam (value = "small") String small, @RequestParam (value = "type") String type) {
return lab2Service.makeItBigger(small, type);
}
@GetMapping("/lab2/reverse")
public Object reverse(@RequestParam(value="value")String value, @RequestParam (value = "type") String type) {
return lab2Service.reverse(value, type);
}
@GetMapping("lab2/lenght")
public Object lenght(@RequestParam(value = "value") String value, @RequestParam (value = "type") String type) {
return lab2Service.lenght(value, type);
}
}

View File

@ -1,47 +0,0 @@
package com.webproglabs.lab1.lab2;
import com.webproglabs.lab1.lab2.domain.Lab2Interface;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
@Service
public class Lab2Service {
private final ApplicationContext applicationContext;
public Lab2Service(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
public Object sum(Object first, Object second, String type) {
if (type.equals("int")) {
first = Integer.parseInt(first.toString());
second = Integer.parseInt(second.toString());
}
final Lab2Interface summator =(Lab2Interface)applicationContext.getBean(type);
return summator.sum(first, second);
}
public Object makeItBigger(Object small, String type) {
if (type.equals("int")) {
small = Integer.parseInt(small.toString());
}
final Lab2Interface biggernator = (Lab2Interface)applicationContext.getBean(type);
return biggernator.makeItBigger(small);
}
public Object reverse (Object value, String type) {
if (type.equals("int")) {
value = Integer.parseInt(value.toString());
}
final Lab2Interface reversenator = (Lab2Interface) applicationContext.getBean(type);
return reversenator.reverse(value);
}
public Object lenght (Object value, String type) {
if (type.equals("int")) {
value = Integer.parseInt(value.toString());
}
final Lab2Interface leghtgetter = (Lab2Interface) applicationContext.getBean(type);
return leghtgetter.lenght(value);
}
}

View File

@ -1,58 +0,0 @@
package com.webproglabs.lab1.lab2.domain;
public class Lab2Int implements Lab2Interface<Integer>{
// @Override
// public Object sum(Object first, Object second) {
// if (!(first instanceof Integer && second instanceof Integer)) {
// throw new IllegalArgumentException();
// }
// return (Integer)first + (Integer)second;
// }
//
// @Override
// public Object makeItBigger(Object small) {
// if (!(small instanceof Integer)) {
// throw new IllegalArgumentException();
// }
// return (Integer)small * (Integer)small;
// }
//
// @Override
// public Object reverse(Object value) {
// if (!(value instanceof Integer)) {
// throw new IllegalArgumentException();
// }
// return (Integer)value * (-1);
// }
//
// @Override
// public Object lenght(Object value) {
// if (!(value instanceof Integer)) {
// throw new IllegalArgumentException();
// }
// int newvalue = (Integer)value;
// if (newvalue < 0) newvalue = newvalue * (-1);
// return newvalue;
// }
@Override
public Integer sum(Integer first, Integer second) {
return first + second;
}
@Override
public Integer makeItBigger(Integer small) {
return small*small;
}
@Override
public Integer reverse(Integer value) {
return value*(-1);
}
@Override
public Integer lenght(Integer value) {
if (value < 0) return value *(-1);
else return value;
}
}

View File

@ -1,11 +0,0 @@
package com.webproglabs.lab1.lab2.domain;
public interface Lab2Interface<T> {
T sum(T first, T second);
T makeItBigger (T small);
T reverse (T value);
T lenght (T value);
}

View File

@ -1,61 +0,0 @@
package com.webproglabs.lab1.lab2.domain;
public class Lab2String implements Lab2Interface<String>{
// @Override
// public Object sum(Object first, Object second) {
// if (!(first instanceof String && second instanceof String)) {
// throw new IllegalArgumentException();
// }
// return ((String) first).concat((String)second);
// }
//
// @Override
// public Object makeItBigger(Object small) {
// if (!(small instanceof String)) {
// throw new IllegalArgumentException();
// }
// return ((String) small).toUpperCase();
// }
//
// @Override
// public Object reverse(Object value) {
// if (!(value instanceof String)) {
// throw new IllegalArgumentException();
// }
// StringBuilder sb = new StringBuilder((String)value);
// sb.reverse();
// return sb.toString();
// }
//
// @Override
// public Object lenght(Object value) {
// if (!(value instanceof String)) {
// throw new IllegalArgumentException();
// }
// return ((String) value).length();
// }
@Override
public String sum(String first, String second) {
return first.concat(second);
}
@Override
public String makeItBigger(String small) {
return small.toUpperCase();
}
@Override
public String reverse(String value) {
StringBuilder sb = new StringBuilder(value);
sb.reverse();
return sb.toString();
}
@Override
public String lenght(String value) {
int len = value.length();
return Integer.toString(len);
}
}

View File

@ -1,28 +0,0 @@
package com.webproglabs.lab1.lab34;
import com.webproglabs.lab1.lab34.jwt.JwtFilter;
import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.security.SecurityRequirement;
import io.swagger.v3.oas.models.security.SecurityScheme;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class OpenAPI30Configuration {
public static final String API_PREFIX = "/api/1.0";
@Bean
public OpenAPI customizeOpenAPI() {
final String securitySchemeName = JwtFilter.TOKEN_BEGIN_STR;
return new OpenAPI()
.addSecurityItem(new SecurityRequirement()
.addList(securitySchemeName))
.components(new Components()
.addSecuritySchemes(securitySchemeName, new SecurityScheme()
.name(securitySchemeName)
.type(SecurityScheme.Type.HTTP)
.scheme("bearer")
.bearerFormat("JWT")));
}
}

View File

@ -1,59 +0,0 @@
package com.webproglabs.lab1.lab34.controller;
import com.webproglabs.lab1.WebConfiguration;
import com.webproglabs.lab1.lab34.model.Comment;
import com.webproglabs.lab1.lab34.model.Profile;
import com.webproglabs.lab1.lab34.services.CommentService;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping(WebConfiguration.REST_API + "/comment")
public class CommentController {
private final CommentService commentService;
public CommentController(CommentService commentService) {
this.commentService = commentService;
}
@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();
}
@GetMapping("/find")
public List<PostDto> getFilteredComments(@RequestParam("text") String text) {
return commentService.findFilteredComments(text).stream()
.map(PostDto::new)
.toList();
}
@PostMapping
public CommentDto createComment(@RequestParam("text") String text, @RequestParam("ownerId") Long id, @RequestParam("postId") Long postId){
final Comment comment = commentService.addComment(text, id, postId);
return new CommentDto(comment);
}
@PutMapping("/{id}")
public CommentDto updateComment(@RequestParam("text") String text, @PathVariable Long id) {
return new CommentDto(commentService.updateComment(id, text));
}
@DeleteMapping("/{id}")
public CommentDto deleteComment(@PathVariable Long id) {
return new CommentDto(commentService.deleteComment(id));
}
@DeleteMapping
public void deleteAllComments(){
commentService.deleteAllComments();
}
}

View File

@ -1,64 +0,0 @@
package com.webproglabs.lab1.lab34.controller;
import com.webproglabs.lab1.WebConfiguration;
import com.webproglabs.lab1.lab34.services.PostService;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.List;
@RestController
@RequestMapping(WebConfiguration.REST_API + "/post")
public class PostController {
private final PostService postService;
public PostController(PostService postService) {
this.postService = postService;
}
@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();
}
@GetMapping("/find")
public List<PostDto> getFilteredPosts(@RequestParam("text") String text) {
return postService.findFilteredPosts(text).stream()
.map(PostDto::new)
.toList();
}
@PostMapping
public PostDto createPost(
@RequestParam("text") String text,
@RequestParam("authorId") Long authorId
)
{
return new PostDto(postService.addPost(text, new ArrayList<>(),authorId));
}
@PutMapping("/{id}")
public PostDto updatePost(
@PathVariable Long id,
@RequestParam("text") String text
)
{
return new PostDto(postService.updatePost(id, text));
}
@DeleteMapping("/{id}")
public PostDto deletePost (@PathVariable Long id) {
return new PostDto(postService.deletePost(id));
}
@DeleteMapping
public void deleteAllPosts() {
postService.deleteAllPosts();
}
}

View File

@ -1,88 +0,0 @@
package com.webproglabs.lab1.lab34.controller;
import com.webproglabs.lab1.WebConfiguration;
import com.webproglabs.lab1.lab34.model.Profile;
import com.webproglabs.lab1.lab34.model.UserRole;
import com.webproglabs.lab1.lab34.services.ProfileService;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import java.util.ArrayList;
import java.util.List;
@RestController
@RequestMapping(WebConfiguration.REST_API + "/profile")
public class ProfileController {
public static final String URL_LOGIN = "/jwt/login";
private final ProfileService profileService;
public ProfileController(ProfileService profileService) {
this.profileService = profileService;
}
@GetMapping("/{id}")
public ProfileDto getProfile(@PathVariable Long id) {
return new ProfileDto(profileService.findUser(id));
}
@GetMapping("/find/{login}")
public ProfileDto getProfileByLogin(@PathVariable String login) {
return new ProfileDto(profileService.findByLogin(login));
}
@GetMapping
public List<ProfileDto> getProfiles() {
return profileService.findAllUsers().stream()
.map(ProfileDto::new)
.toList();
}
@PostMapping
public ProfileDto createProfile(
@RequestParam("login") String login,
@RequestParam("password") String password
)
{
return new ProfileDto(profileService.addUser(login, password, new ArrayList<>(),new ArrayList<>() ));
}
@PutMapping("/{id}")
public ProfileDto updateProfile(
@PathVariable Long id,
@RequestParam("login") String login,
@RequestParam("password") String password
)
{
return new ProfileDto(profileService.updateUser(id, login, password));
}
@DeleteMapping("/{id}")
public ProfileDto deleteProfile (@PathVariable Long id) {
return new ProfileDto(profileService.deleteUser(id));
}
@DeleteMapping
public void deleteAllProfiles() {
profileService.deleteAllUsers();
}
@PostMapping(URL_LOGIN)
public String login(@RequestBody @Valid ProfileDto userDto) {
return profileService.loginAndGetToken(userDto);
}
// этот метод юзать для проверки на администраторсткую роль вместо старой проверки по логину админа ЛОЛ
//
@GetMapping("role/{token}")
public String getRoleByToken(@PathVariable String token) {
var userDetails = profileService.loadUserByToken(token);
Profile user = profileService.findByLogin(userDetails.getUsername());
if (user != null) {
return user.getRole().toString();
}
return null;
}
}

View File

@ -1,121 +0,0 @@
package com.webproglabs.lab1.lab34.controller.mvc_controllers;
import com.webproglabs.lab1.lab34.controller.CommentDto;
import com.webproglabs.lab1.lab34.controller.PostDto;
import com.webproglabs.lab1.lab34.controller.ProfileDto;
import com.webproglabs.lab1.lab34.model.Profile;
import com.webproglabs.lab1.lab34.services.CommentService;
import com.webproglabs.lab1.lab34.services.PostService;
import com.webproglabs.lab1.lab34.services.ProfileService;
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 java.util.ArrayList;
@Controller
@RequestMapping("/feed")
public class FeedMvcController {
private final ProfileService profileService;
private final PostService postService;
private final CommentService commentService;
public FeedMvcController(ProfileService profileService, PostService postService, CommentService commentService) {
this.profileService = profileService;
this.postService = postService;
this.commentService = commentService;
}
@GetMapping
public String getFeedPage(Model model) {
UserDetails principal = (UserDetails)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
var user = profileService.findByLogin(principal.getUsername());
if (user != null) {
return "redirect:/feed/" + user.getId().toString();
}
model.addAttribute("profiles", profileService.findAllUsers().stream().map(ProfileDto::new).toList());
return "feed";
}
@GetMapping(value = {"/{id}"})
public String getFeedPageAuthorized(@PathVariable Long id, Model model) {
model.addAttribute("profiles", profileService.findAllUsers().stream().map(ProfileDto::new).toList());
model.addAttribute("posts", postService.findAllPosts().stream().map(PostDto::new).toList());
model.addAttribute("selectedProfile", new ProfileDto(profileService.findUser(id)));
return "feedPosts";
}
@GetMapping(value= {"/filter/{id}/"})
public String getFeedPageFiltered(@PathVariable Long id, @RequestParam(value="searchField") String searchField, Model model) {
model.addAttribute("profiles", profileService.findAllUsers().stream().map(ProfileDto::new).toList());
model.addAttribute("posts", postService.findFilteredPosts(searchField).stream().map(PostDto::new).toList());
model.addAttribute("selectedProfile", new ProfileDto(profileService.findUser(id)));
return "feedPosts";
}
@PostMapping(value={"/post/{id}/"})
public String createPost(@PathVariable Long id, @RequestParam(value="postInputField") String postInputField) {
postService.addPost(postInputField, new ArrayList<>(), id);
return "redirect:/feed/" + id.toString();
}
@PostMapping(value = {"/deletePost/{id}/{authorId}"})
public String deletePost(@PathVariable Long id, @PathVariable Long authorId) {
postService.deletePost(id);
return "redirect:/feed/" + authorId.toString();
}
@GetMapping(value = {"postModal/{id}/{authorId}"})
public String getPostEditModal(@PathVariable Long id,@PathVariable Long authorId, Model model) {
model.addAttribute("selectedPost", new PostDto(postService.findPost(id)));
model.addAttribute("profiles", profileService.findAllUsers().stream().map(ProfileDto::new).toList());
model.addAttribute("posts", postService.findAllPosts().stream().map(PostDto::new).toList());
model.addAttribute("selectedProfile", new ProfileDto(profileService.findUser(authorId)));
return "editPostModal";
}
@PostMapping(value = {"editPost/{id}/{authorId}/"})
public String editPost(@PathVariable Long id, @PathVariable Long authorId, @RequestParam(value="postEditField") String postEditField) {
postService.updatePost(id, postEditField);
return "redirect:/feed/" + authorId.toString();
}
@GetMapping(value = {"commentModal/{authorId}/{postId}"})
public String getCommentModal(@PathVariable Long authorId,@PathVariable Long postId, Model model) {
model.addAttribute("selectedPost", new PostDto(postService.findPost(postId)));
model.addAttribute("profiles", profileService.findAllUsers().stream().map(ProfileDto::new).toList());
model.addAttribute("posts", postService.findAllPosts().stream().map(PostDto::new).toList());
model.addAttribute("selectedProfile", new ProfileDto(profileService.findUser(authorId)));
return "commentModal";
}
@PostMapping(value = {"comment/{authorId}/{postId}/"})
public String createComment(@PathVariable Long authorId,@PathVariable Long postId, @RequestParam(value="commentInputField") String commentInputField) {
commentService.addComment(commentInputField, authorId, postId);
return "redirect:/feed/" + authorId.toString();
}
@PostMapping(value = {"/deleteComment/{id}/{authorId}"})
public String deleteComment(@PathVariable Long id, @PathVariable Long authorId) {
commentService.deleteComment(id);
return "redirect:/feed/" + authorId.toString();
}
@GetMapping(value = {"commentEditModal/{id}/{authorId}"})
public String getCommentEditModal(@PathVariable Long id,@PathVariable Long authorId, Model model) {
model.addAttribute("selectedComment", new CommentDto(commentService.findComment(id)));
model.addAttribute("profiles", profileService.findAllUsers().stream().map(ProfileDto::new).toList());
model.addAttribute("posts", postService.findAllPosts().stream().map(PostDto::new).toList());
model.addAttribute("selectedProfile", new ProfileDto(profileService.findUser(authorId)));
return "editCommentModal";
}
@PostMapping(value = {"editComment/{authorId}/{commentId}/"})
public String editComment(@PathVariable Long authorId,@PathVariable Long commentId, @RequestParam(value="commentEditField") String commentEditField) {
commentService.updateComment(commentId, commentEditField);
return "redirect:/feed/" + authorId.toString();
}
}

View File

@ -1,52 +0,0 @@
package com.webproglabs.lab1.lab34.controller.mvc_controllers;
import com.webproglabs.lab1.lab34.controller.ProfileDto;
import com.webproglabs.lab1.lab34.model.UserRole;
import com.webproglabs.lab1.lab34.services.ProfileService;
import org.springframework.security.access.annotation.Secured;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
@Controller
@RequestMapping("/")
public class ProfileMvcController {
private final ProfileService profileService;
public ProfileMvcController(ProfileService profileService) {
this.profileService = profileService;
}
@GetMapping
public String index() {
return "default";
}
@GetMapping(value={"profiles"})
@Secured({UserRole.AsString.ADMIN})
public String getProfiles(Model model) {
model.addAttribute("profiles", profileService.findAllUsers().stream().map(ProfileDto::new).toList());
model.addAttribute("profileDto", new ProfileDto());
return "profiles";
}
@GetMapping(value = {"profile/{login}"})
public String getProfile(@PathVariable String login, Model model) {
model.addAttribute("profile", new ProfileDto(profileService.findUserByLogin(login)));
return "profilePage";
}
@PostMapping(value = {"profile/{id}"})
public String deleteProfile(@PathVariable Long id) {
profileService.deleteUser(id);
return "redirect:/profiles";
}
@PostMapping(value = {"profile/create/"})
public String createProfile(@ModelAttribute ProfileDto profileDto) {
profileService.addUser(profileDto.getLogin(), profileDto.getPassword(), new ArrayList<>(), new ArrayList<>());
return "redirect:/profiles";
}
}

View File

@ -1,11 +0,0 @@
package com.webproglabs.lab1.lab34.jwt;
public class JwtException extends RuntimeException {
public JwtException(Throwable throwable) {
super(throwable);
}
public JwtException(String message) {
super(message);
}
}

View File

@ -1,72 +0,0 @@
package com.webproglabs.lab1.lab34.jwt;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.webproglabs.lab1.lab34.services.ProfileService;
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 ProfileService userService;
public JwtFilter(ProfileService userService) {
this.userService = userService;
}
private String getTokenFromRequest(HttpServletRequest request) {
String bearer = request.getHeader(AUTHORIZATION);
if (StringUtils.hasText(bearer) && bearer.startsWith(TOKEN_BEGIN_STR)) {
return bearer.substring(TOKEN_BEGIN_STR.length());
}
return null;
}
private void raiseException(ServletResponse response, int status, String message) throws IOException {
if (response instanceof final HttpServletResponse httpResponse) {
httpResponse.setContentType(MediaType.APPLICATION_JSON_VALUE);
httpResponse.setStatus(status);
final byte[] body = new ObjectMapper().writeValueAsBytes(message);
response.getOutputStream().write(body);
}
}
@Override
public void doFilter(ServletRequest request,
ServletResponse response,
FilterChain chain) throws IOException, ServletException {
if (request instanceof final HttpServletRequest httpRequest) {
final String token = getTokenFromRequest(httpRequest);
if (StringUtils.hasText(token)) {
try {
final UserDetails user = userService.loadUserByToken(token);
final UsernamePasswordAuthenticationToken auth =
new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(auth);
} catch (JwtException e) {
raiseException(response, HttpServletResponse.SC_UNAUTHORIZED, e.getMessage());
return;
} catch (Exception e) {
e.printStackTrace();
raiseException(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
String.format("Internal error: %s", e.getMessage()));
return;
}
}
}
chain.doFilter(request, response);
}
}

View File

@ -1,27 +0,0 @@
package com.webproglabs.lab1.lab34.jwt;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
@Configuration
@ConfigurationProperties(prefix = "jwt", ignoreInvalidFields = true)
public class JwtProperties {
private String devToken = "";
private Boolean isDev = true;
public String getDevToken() {
return devToken;
}
public void setDevToken(String devToken) {
this.devToken = devToken;
}
public Boolean isDev() {
return isDev;
}
public void setDev(Boolean dev) {
isDev = dev;
}
}

View File

@ -1,107 +0,0 @@
package com.webproglabs.lab1.lab34.jwt;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.auth0.jwt.interfaces.JWTVerifier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.Date;
import java.util.Optional;
import java.util.UUID;
@Component
public class JwtProvider {
private final static Logger LOG = LoggerFactory.getLogger(JwtProvider.class);
private final static byte[] HEX_ARRAY = "0123456789ABCDEF".getBytes(StandardCharsets.US_ASCII);
private final static String ISSUER = "auth0";
private final Algorithm algorithm;
private final JWTVerifier verifier;
public JwtProvider(JwtProperties jwtProperties) {
if (!jwtProperties.isDev()) {
LOG.info("Generate new JWT key for prod");
try {
final MessageDigest salt = MessageDigest.getInstance("SHA-256");
salt.update(UUID.randomUUID().toString().getBytes(StandardCharsets.UTF_8));
LOG.info("Use generated JWT key for prod \n{}", bytesToHex(salt.digest()));
algorithm = Algorithm.HMAC256(bytesToHex(salt.digest()));
} catch (NoSuchAlgorithmException e) {
throw new JwtException(e);
}
} else {
LOG.info("Use default JWT key for dev \n{}", jwtProperties.getDevToken());
algorithm = Algorithm.HMAC256(jwtProperties.getDevToken());
}
verifier = JWT.require(algorithm)
.withIssuer(ISSUER)
.build();
}
private static String bytesToHex(byte[] bytes) {
byte[] hexChars = new byte[bytes.length * 2];
for (int j = 0; j < bytes.length; j++) {
int v = bytes[j] & 0xFF;
hexChars[j * 2] = HEX_ARRAY[v >>> 4];
hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
}
return new String(hexChars, StandardCharsets.UTF_8);
}
public String generateToken(String login) {
final Date issueDate = Date.from(LocalDate.now()
.atStartOfDay(ZoneId.systemDefault())
.toInstant());
final Date expireDate = Date.from(LocalDate.now()
.plusDays(15)
.atStartOfDay(ZoneId.systemDefault())
.toInstant());
return JWT.create()
.withIssuer(ISSUER)
.withIssuedAt(issueDate)
.withExpiresAt(expireDate)
.withSubject(login)
.sign(algorithm);
}
private DecodedJWT validateToken(String token) {
try {
return verifier.verify(token);
} catch (JWTVerificationException e) {
throw new JwtException(String.format("Token verification error: %s", e.getMessage()));
}
}
public boolean isTokenValid(String token) {
if (!StringUtils.hasText(token)) {
return false;
}
try {
validateToken(token);
return true;
} catch (JwtException e) {
LOG.error(e.getMessage());
return false;
}
}
public Optional<String> getLoginFromToken(String token) {
try {
return Optional.ofNullable(validateToken(token).getSubject());
} catch (JwtException e) {
LOG.error(e.getMessage());
return Optional.empty();
}
}
}

View File

@ -1,11 +0,0 @@
package com.webproglabs.lab1.lab34.repository;
import com.webproglabs.lab1.lab34.model.Comment;
import com.webproglabs.lab1.lab34.model.Post;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
public interface PostRepository extends JpaRepository<Post, Long> {
List<Post> findByTextLike(String text);
}

View File

@ -1,12 +0,0 @@
package com.webproglabs.lab1.lab34.repository;
import com.webproglabs.lab1.lab34.model.Post;
import com.webproglabs.lab1.lab34.model.Profile;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
public interface ProfileRepository extends JpaRepository<Profile, Long> {
List<Profile> findByLoginLike(String login);
Profile findOneByLoginIgnoreCase(String login);
}

View File

@ -1,90 +0,0 @@
package com.webproglabs.lab1.lab34.services;
import com.webproglabs.lab1.lab34.model.Comment;
import com.webproglabs.lab1.lab34.model.Post;
import com.webproglabs.lab1.lab34.model.Profile;
import com.webproglabs.lab1.lab34.repository.CommentRepository;
import com.webproglabs.lab1.lab34.repository.PostRepository;
import com.webproglabs.lab1.lab34.repository.ProfileRepository;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import javax.persistence.EntityNotFoundException;
import javax.transaction.Transactional;
import java.util.List;
import java.util.Optional;
@Service
public class PostService {
private final PostRepository postRepository;
private final CommentRepository commentRepository;
private final ProfileRepository profileRepository;
public PostService(PostRepository postRepository, CommentRepository commentRepository, ProfileRepository profileRepository) {
this.postRepository = postRepository;
this.commentRepository = commentRepository;
this.profileRepository = profileRepository;
}
@Transactional
public Post findPost(Long id) {
final Optional<Post> post = postRepository.findById(id);
return post.orElseThrow(EntityNotFoundException::new);
}
@Transactional
public List<Post> findAllPosts() {
return postRepository.findAll();
}
@Transactional
public List<Post> findFilteredPosts(String filter) {
List<Post> postList = postRepository.findByTextLike("%" + filter + "%");
List<Comment> commentList = commentRepository.findByTextLike("%" + filter + "%");
List<Post> allPosts = postRepository.findAll();
for(Post post : allPosts) {
for (Comment comm : commentList) {
if (post.getComments().contains(comm) && !(postList.contains(post))) {
postList.add(post);
}
}
}
return postList;
}
@Transactional
public Post addPost (String text, List<Comment> comments, Long authorId) {
if (!StringUtils.hasText(text)) {
throw new IllegalArgumentException("Post data is null or empty");
}
Profile author = profileRepository.findById(authorId).get();
Post post = new Post(text, author, comments);
author.addPost(post);
profileRepository.save(author);
return postRepository.save(post);
}
@Transactional
public Post updatePost(Long id, String text) {
if (!StringUtils.hasText(text)) {
throw new IllegalArgumentException("Post data is null or empty");
}
final Post currentPost = findPost(id);
currentPost.setText(text);
final Profile author = currentPost.getAuthor();
profileRepository.save(author);
return postRepository.save(currentPost);
}
@Transactional
public Post deletePost(Long id) {
final Post currentPost = findPost(id);
postRepository.delete(currentPost);
return currentPost;
}
@Transactional
public void deleteAllPosts() {
postRepository.deleteAll();
}
}

View File

@ -1,158 +0,0 @@
package com.webproglabs.lab1.lab34.services;
import com.webproglabs.lab1.lab34.controller.ProfileDto;
import com.webproglabs.lab1.lab34.jwt.JwtException;
import com.webproglabs.lab1.lab34.jwt.JwtProvider;
import com.webproglabs.lab1.lab34.model.Comment;
import com.webproglabs.lab1.lab34.model.Post;
import com.webproglabs.lab1.lab34.model.Profile;
import com.webproglabs.lab1.lab34.model.UserRole;
import com.webproglabs.lab1.lab34.repository.ProfileRepository;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import javax.persistence.EntityNotFoundException;
import javax.transaction.Transactional;
import java.util.*;
@Service
public class ProfileService implements UserDetailsService {
private final ProfileRepository profileRepository;
private final PasswordEncoder passwordEncoder;
private final JwtProvider jwtProvider;
public ProfileService(ProfileRepository profileRepository, PasswordEncoder passwordEncoder, JwtProvider jwtProvider) {
this.profileRepository = profileRepository;
this.passwordEncoder = passwordEncoder;
this.jwtProvider = jwtProvider;
}
@Transactional
public Profile findUser(Long id) {
final Optional<Profile> profile = profileRepository.findById(id);
return profile.orElseThrow(EntityNotFoundException::new);
}
@Transactional
public Profile findUserByLogin(String login) {
final Optional<Profile> profile = profileRepository.findByLoginLike(login).stream().findFirst();
return profile.orElseThrow(EntityNotFoundException::new);
}
@Transactional
public List<Profile> findAllUsers() {
return profileRepository.findAll();
}
@Transactional
public Profile addUser(String login, String password, List<Comment> comments, List<Post> posts) {
if (!StringUtils.hasText(login) || !StringUtils.hasText(password)) {
throw new IllegalArgumentException("User data is null or empty");
}
final Profile user = new Profile(login, password, comments, posts, UserRole.USER);
return profileRepository.save(user);
}
@Transactional
public Profile addUser(String login, String password, List<Comment> comments, List<Post> posts, UserRole role) {
if (!StringUtils.hasText(login) || !StringUtils.hasText(password)) {
throw new IllegalArgumentException("User data is null or empty");
}
final Profile user = new Profile(login, password, comments, posts, role);
return profileRepository.save(user);
}
@Transactional
public Profile createUser(String login, String password, String passwordConfirm) throws Exception {
if (findByLogin(login) != null) {
throw new Exception("User " + login + " already exists");
}
final Profile user = new Profile(login, passwordEncoder.encode(password), new ArrayList<>(), new ArrayList<>(), UserRole.USER);
if (!Objects.equals(password, passwordConfirm)) {
throw new Exception("Passwords not equals");
}
return profileRepository.save(user);
}
@Transactional
public Profile createUser(String login, String password, String passwordConfirm, UserRole role) throws Exception {
if (findByLogin(login) != null) {
throw new Exception("User " + login + " already exists");
}
final Profile user = new Profile(login, passwordEncoder.encode(password), new ArrayList<>(), new ArrayList<>(), role);
if (!Objects.equals(password, passwordConfirm)) {
throw new Exception("Passwords not equals");
}
return profileRepository.save(user);
}
@Transactional
public Profile updateUser(Long id, String login, String password) {
if (!StringUtils.hasText(login) || !StringUtils.hasText(password)) {
throw new IllegalArgumentException("User data is null or empty");
}
final Profile currentUser = findUser(id);
currentUser.setLogin(login);
currentUser.setPassword(password);
return profileRepository.save(currentUser);
}
@Transactional
public Profile deleteUser(Long id) {
final Profile currentUser = findUser(id);
profileRepository.delete(currentUser);
return currentUser;
}
@Transactional
public void deleteAllUsers() {
profileRepository.deleteAll();
}
public Profile findByLogin(String login) {
return profileRepository.findOneByLoginIgnoreCase(login);
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
final Profile userEntity = findByLogin(username);
if (userEntity == null) {
throw new UsernameNotFoundException(username);
}
return new org.springframework.security.core.userdetails.User(
userEntity.getLogin(), userEntity.getPassword(), Collections.singleton(userEntity.getRole()));
}
public String loginAndGetToken(ProfileDto userDto) {
try {
final Profile user = findByLogin(userDto.getLogin());
if (user == null) {
throw new Exception("Login not found" + userDto.getLogin());
}
if (!passwordEncoder.matches(userDto.getPassword(), user.getPassword())) {
throw new Exception("User not found" + user.getLogin());
}
return jwtProvider.generateToken(user.getLogin());
}
catch (Exception e) {
var ex = 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);
}
}

View File

@ -1,4 +1,4 @@
package com.webproglabs.lab1.lab34.model;
package com.webproglabs.lab1.models;
import javax.persistence.*;
import java.util.Objects;
@ -12,13 +12,13 @@ public class Comment {
private String text;
@ManyToOne()
private Profile owner;
private User owner;
@ManyToOne()
private Post post;
public Comment(){};
public Comment(String text, Profile owner, Post post) {
public Comment(String text, User owner, Post post) {
this.text = text;
this.owner = owner;
this.post = post;
@ -36,10 +36,11 @@ public class Comment {
this.text = text;
}
public Profile getOwner() {
public User getOwner() {
return owner;
}
public void setOwner(Profile owner) {
public void setOwner(User owner) {
this.owner.getComments().remove(this);
this.owner = owner;
if (!owner.getComments().contains(this)) {

View File

@ -1,4 +1,4 @@
package com.webproglabs.lab1.lab34.model;
package com.webproglabs.lab1.models;
import javax.persistence.*;
import java.util.ArrayList;
@ -17,15 +17,16 @@ public class Post {
private List<Comment> comments = new ArrayList<Comment>();
@ManyToOne()
private Profile author;
private User author;
@ManyToOne()
private Topic topic;
public Post(){}
public Post(String text, Profile author, List<Comment> comments) {
public Post(String text, User author, Topic topic) {
this.text = text;
this.author = author;
for (int i = 0; i < comments.size(); i++) {
addComment(comments.get(i));
}
this.topic = topic;
}
public Long getId() {
@ -40,11 +41,11 @@ public class Post {
this.text = text;
}
public Profile getAuthor() {
public User getAuthor() {
return author;
}
public void setAuthor(Profile author) {
public void setAuthor(User author) {
this.author.getPosts().remove(this);
this.author = author;
if (!author.getPosts().contains(this)) {
@ -52,12 +53,20 @@ public class Post {
}
}
public List<Comment> getComments() { return comments; }
public int getCommentsSize() {
if (comments == null) return 0;
else return comments.size();
public Topic getTopic() {
return topic;
}
public void setTopic(Topic topic) {
this.topic.getPosts().remove(this);
this.topic = topic;
if (!topic.getPosts().contains(this)) {
topic.getPosts().add(this);
}
}
public List<Comment> getComments() { return comments; }
public void addComment(Comment comment) {
this.comments.add(comment);
if (comment.getPost() != this) {
@ -83,8 +92,6 @@ public class Post {
"id=" + id +
", text='" + text + '\'' +
", author='" + author.ToString() + '\'' +
", comments='" + getCommentsSize() + '\'' +
'}';
}
}

View File

@ -0,0 +1,78 @@
package com.webproglabs.lab1.models;
import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
@Entity
public class Topic {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String description;
@OneToMany(mappedBy = "topic", orphanRemoval = true, fetch = FetchType.EAGER)
private List<Post> posts;
public Topic(String name, String description) {
this.name = name;
this.description = description;
this.posts = new ArrayList<Post>();
}
public Topic(){}
public Long getId() {
return id;
}
public String getName() {
return name;
}
public String getDescription() {
return description;
}
public List<Post> getPosts() {
return posts;
}
public void setName(String name) {
this.name = name;
}
public void setDescription(String description) {
this.description = description;
}
public void addPost(Post post) {
this.posts.add(post);
if (post.getTopic() != this) {
post.setTopic(this);
}
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Topic topic = (Topic) o;
return Objects.equals(id, topic.id);
}
@Override
public int hashCode() {
return Objects.hash(id);
}
public String ToString() {
return "Topic{" +
"id=" + id +
", name='" + name + '\'' +
", description='" + description + '\'' +
'}';
}
}

View File

@ -1,4 +1,4 @@
package com.webproglabs.lab1.lab34.model;
package com.webproglabs.lab1.models;
import javax.persistence.*;
import javax.validation.constraints.NotBlank;
@ -8,8 +8,8 @@ import java.util.List;
import java.util.Objects;
@Entity
@Table(name="tab_user")
public class Profile {
@Table(name="users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@ -21,43 +21,28 @@ public class Profile {
@Column(nullable = false, length = 64)
@NotBlank
@Size(min = 6, max = 64)
@Size(min = 4, max = 64)
private String password;
private UserRole role;
public UserRole getRole() {
return role;
}
@OneToMany(mappedBy = "owner", orphanRemoval = true, fetch = FetchType.EAGER)
private List<Comment> comments = new ArrayList<Comment>();
@OneToMany(mappedBy = "author", orphanRemoval = true, fetch = FetchType.EAGER)
private List<Post> posts = new ArrayList<Post>();
public Profile(){}
public Profile(String login, String password, List<Comment> comments, List<Post> posts) {
public User(){}
public User(String login, String password) {
this.login = login;
this.password=password;
this.role = UserRole.USER;
for (int i = 0; i < comments.size(); i++) {
addComment(comments.get(i));
}
for (int i = 0; i < posts.size(); i++) {
addPost(posts.get(i));
}
};
public Profile(String login, String password, List<Comment> comments, List<Post> posts, UserRole role) {
public User(String login, String password, UserRole role) {
this.login = login;
this.password=password;
this.role = role;
for (int i = 0; i < comments.size(); i++) {
addComment(comments.get(i));
}
for (int i = 0; i < posts.size(); i++) {
addPost(posts.get(i));
}
};
public Long getId() {
@ -68,6 +53,10 @@ public class Profile {
return login;
}
public UserRole getRole() {
return role;
}
public void setLogin(String login) {
this.login = login;
}
@ -84,16 +73,6 @@ public class Profile {
public List<Post> getPosts() {return posts; }
public int getCommentsSize() {
if (comments == null) return 0;
else return comments.size();
}
public int getPostsSize() {
if (posts == null) return 0;
else return posts.size();
}
public void addComment(Comment comment) {
this.comments.add(comment);
if (comment.getOwner() != this) {
@ -112,7 +91,7 @@ public class Profile {
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Profile user = (Profile) o;
User user = (User) o;
return Objects.equals(id, user.id);
}
@ -126,8 +105,6 @@ public class Profile {
"id=" + id +
", login='" + login + '\'' +
", password='" + password + '\'' +
", comments='" + getCommentsSize() + '\'' +
", comments='" + getPostsSize() + '\'' +
'}';
}
}

View File

@ -1,4 +1,4 @@
package com.webproglabs.lab1.lab34.model;
package com.webproglabs.lab1.models;
import org.springframework.security.core.GrantedAuthority;

View File

@ -0,0 +1,142 @@
package com.webproglabs.lab1.mvc;
import com.webproglabs.lab1.dto.CommentDto;
import com.webproglabs.lab1.dto.PostDto;
import com.webproglabs.lab1.dto.TopicDto;
import com.webproglabs.lab1.dto.UserDto;
import com.webproglabs.lab1.services.CommentService;
import com.webproglabs.lab1.services.PostService;
import com.webproglabs.lab1.services.TopicService;
import com.webproglabs.lab1.services.UserService;
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.*;
@Controller
@RequestMapping("/feed")
public class FeedMvcController {
private final UserService userService;
private final PostService postService;
private final CommentService commentService;
private final TopicService topicService;
public FeedMvcController(UserService userService, PostService postService, CommentService commentService, TopicService topicService) {
this.userService = userService;
this.postService = postService;
this.commentService = commentService;
this.topicService = topicService;
}
@GetMapping
public String getFeedPage(Model model) {
model.addAttribute("topics", topicService.findAllTopics().stream().map(TopicDto::new).toList());
return "feed";
}
@GetMapping(value = {"/{id}"})
public String getFeedPageWithTopic(@PathVariable Long id, Model model) {
model.addAttribute("profiles", userService.findAllUsers().stream().map(UserDto::new).toList());
model.addAttribute("posts", topicService.findTopicById(id).getPosts().stream().map(PostDto::new).toList());
model.addAttribute("topics", topicService.findAllTopics().stream().map(TopicDto::new).toList());
UserDetails principal = (UserDetails)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
var user = userService.findUserByLogin(principal.getUsername());
model.addAttribute("selectedProfile", new UserDto(userService.findUserById(user.getId())));
model.addAttribute("selectedTopic", new TopicDto(topicService.findTopicById(id)));
return "feedPosts";
}
@GetMapping(value= {"/filter/{id}/"})
public String getFeedPageFiltered(@PathVariable Long id, @RequestParam(value="searchField") String searchField, Model model) {
model.addAttribute("profiles", userService.findAllUsers().stream().map(UserDto::new).toList());
model.addAttribute("topics", topicService.findAllTopics().stream().map(TopicDto::new).toList());
UserDetails principal = (UserDetails)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
var user = userService.findUserByLogin(principal.getUsername());
model.addAttribute("selectedProfile", new UserDto(userService.findUserById(user.getId())));
model.addAttribute("selectedTopic", new TopicDto(topicService.findTopicById(id)));
model.addAttribute("posts", postService.findPostsInTopicByText(searchField, id).stream().map(PostDto::new).toList());
return "feedPosts";
}
@PostMapping(value={"/{topicId}/post/{id}/"})
public String createPost(@PathVariable Long topicId, @PathVariable Long id, @RequestParam(value="postInputField") String postInputField) {
postService.addPost(postInputField, id, topicId);
return "redirect:/feed/" + topicId.toString();
}
@PostMapping(value = {"/deletePost/{id}/{topicId}"})
public String deletePost(@PathVariable Long id, @PathVariable Long topicId) {
postService.deletePost(id);
return "redirect:/feed/" + topicId.toString();
}
@GetMapping(value = {"postModal/{id}/{topicId}"})
public String getPostEditModal(@PathVariable Long id,@PathVariable Long topicId, Model model) {
model.addAttribute("selectedPost", new PostDto(postService.findPostById(id)));
model.addAttribute("profiles", userService.findAllUsers().stream().map(UserDto::new).toList());
model.addAttribute("posts", topicService.findTopicById(topicId).getPosts().stream().map(PostDto::new).toList());
UserDetails principal = (UserDetails)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
var user = userService.findUserByLogin(principal.getUsername());
model.addAttribute("selectedProfile", new UserDto(userService.findUserById(user.getId())));
model.addAttribute("selectedTopic", new TopicDto(topicService.findTopicById(topicId)));
return "editPostModal";
}
@PostMapping(value = {"editPost/{id}/{topicId}/"})
public String editPost(@PathVariable Long id, @PathVariable Long topicId, @RequestParam(value="postEditField") String postEditField) {
postService.updatePost(id, postEditField);
return "redirect:/feed/" + topicId.toString();
}
@GetMapping(value = {"commentModal/{topicId}/{postId}"})
public String getCommentModal(@PathVariable Long topicId,@PathVariable Long postId, Model model) {
model.addAttribute("selectedPost", new PostDto(postService.findPostById(postId)));
model.addAttribute("profiles", userService.findAllUsers().stream().map(UserDto::new).toList());
model.addAttribute("posts", topicService.findTopicById(topicId).getPosts().stream().map(PostDto::new).toList());
UserDetails principal = (UserDetails)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
var user = userService.findUserByLogin(principal.getUsername());
model.addAttribute("selectedProfile", new UserDto(userService.findUserById(user.getId())));
model.addAttribute("selectedTopic", new TopicDto(topicService.findTopicById(topicId)));
return "commentModal";
}
@PostMapping(value = {"comment/{authorId}/{topicId}/{postId}/"})
public String createComment(@PathVariable Long authorId, @PathVariable Long topicId, @PathVariable Long postId, @RequestParam(value="commentInputField") String commentInputField) {
commentService.addComment(commentInputField, authorId, postId);
return "redirect:/feed/" + topicId.toString();
}
@PostMapping(value = {"/deleteComment/{id}/{topicId}"})
public String deleteComment(@PathVariable Long id, @PathVariable Long topicId) {
commentService.deleteComment(id);
return "redirect:/feed/" + topicId.toString();
}
@GetMapping(value = {"commentEditModal/{id}/{topicId}"})
public String getCommentEditModal(@PathVariable Long id,@PathVariable Long topicId, Model model) {
model.addAttribute("selectedComment", new CommentDto(commentService.findCommentById(id)));
model.addAttribute("profiles", userService.findAllUsers().stream().map(UserDto::new).toList());
model.addAttribute("posts", topicService.findTopicById(topicId).getPosts().stream().map(PostDto::new).toList());
UserDetails principal = (UserDetails)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
var user = userService.findUserByLogin(principal.getUsername());
model.addAttribute("selectedProfile", new UserDto(userService.findUserById(user.getId())));
model.addAttribute("selectedTopic", new TopicDto(topicService.findTopicById(topicId)));
return "editCommentModal";
}
@PostMapping(value = {"editComment/{topicId}/{commentId}/"})
public String editComment(@PathVariable Long topicId,@PathVariable Long commentId, @RequestParam(value="commentEditField") String commentEditField) {
commentService.updateComment(commentId, commentEditField);
return "redirect:/feed/" + topicId.toString();
}
}

View File

@ -0,0 +1,58 @@
package com.webproglabs.lab1.mvc;
import com.webproglabs.lab1.dto.TopicDto;
import com.webproglabs.lab1.dto.UserSignupDto;
import com.webproglabs.lab1.models.UserRole;
import com.webproglabs.lab1.services.TopicService;
import org.springframework.security.access.annotation.Secured;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
@Controller
@RequestMapping("/topic")
public class TopicMvcController {
private final TopicService topicService;
public TopicMvcController(TopicService topicService) {
this.topicService = topicService;
}
@GetMapping
@Secured({UserRole.AsString.ADMIN})
public String getTopics(Model model) {
model.addAttribute("topics", topicService.findAllTopics().stream().map(TopicDto::new).toList());
model.addAttribute("topicDto", new TopicDto());
return "topics";
}
@PostMapping(value = {"/create"})
@Secured({UserRole.AsString.ADMIN})
public String createTopic(@ModelAttribute TopicDto topicDto) {
topicService.addTopic(topicDto.getName(), topicDto.getDescription());
return "redirect:/topic";
}
@PostMapping(value = {"/delete/{Id}"})
@Secured({UserRole.AsString.ADMIN})
public String deleteTopic(@PathVariable Long Id) {
topicService.deleteTopic(Id);
return "redirect:/topic";
}
@GetMapping(value = {"/edit/{Id}"})
@Secured({UserRole.AsString.ADMIN})
public String getTopicEdit(@PathVariable Long Id, Model model) {
model.addAttribute("topic", new TopicDto(topicService.findTopicById(Id)));
model.addAttribute("topicDto", new TopicDto(topicService.findTopicById(Id)));
return "topicEdit";
}
@PostMapping(value = {"/edit/{Id}"})
@Secured({UserRole.AsString.ADMIN})
public String editTopic(@PathVariable Long Id, @ModelAttribute TopicDto topicDto) {
topicService.updateTopic(Id, topicDto.getName(), topicDto.getDescription());
return "redirect:/topic";
}
}

View File

@ -0,0 +1,79 @@
package com.webproglabs.lab1.mvc;
import com.webproglabs.lab1.dto.UserDto;
import com.webproglabs.lab1.dto.UserSignupDto;
import com.webproglabs.lab1.models.User;
import com.webproglabs.lab1.models.UserRole;
import com.webproglabs.lab1.services.UserService;
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.*;
import javax.validation.Valid;
@Controller
@RequestMapping("/users")
public class UserMvcController {
private final UserService userService;
public UserMvcController(UserService userService) {
this.userService = userService;
}
@GetMapping
@Secured({UserRole.AsString.ADMIN})
public String getUsersPage(Model model){
model.addAttribute("profiles", userService.findAllUsers().stream().map(UserDto::new).toList());
return "users";
}
@PostMapping(value = {"/{id}"})
@Secured({UserRole.AsString.ADMIN})
public String deleteUser(@PathVariable Long id) {
userService.deleteUser(id);
return "redirect:/users";
}
@GetMapping(value = {"/{login}"})
public String getUserPage(@PathVariable String login, Model model) {
model.addAttribute("user", new UserDto(userService.findUserByLogin(login)));
return "userPage";
}
@GetMapping(value = {"/settings"})
public String getUserEditPage(Model model) {
UserDetails principal = (UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
var currentUser = userService.findUserByLogin(principal.getUsername());
model.addAttribute("user", new UserDto(userService.findUserById(currentUser.getId())));
model.addAttribute("userDto", new UserSignupDto());
return "userEditPage";
}
@PostMapping(value = {"/edit/{id}"})
public String editUserData(@PathVariable Long id, @ModelAttribute("userDto") @Valid UserSignupDto userSignupDto,
BindingResult bindingResult,
Model model) {
UserDetails principal = (UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
var currentUser = userService.findUserByLogin(principal.getUsername());
model.addAttribute("user", new UserDto(userService.findUserById(currentUser.getId())));
model.addAttribute("userDto", new UserSignupDto());
if (bindingResult.hasErrors()) {
model.addAttribute("errors", bindingResult.getAllErrors());
return "userEditPage";
}
try {
final User user = userService.updateUser(id, userSignupDto.getLogin(), userSignupDto.getPassword(), userSignupDto.getPasswordConfirm());
model.addAttribute("success", "Данные успешно изменены");
return "userEditPage";
} catch (Exception e) {
model.addAttribute("errors", e.getMessage());
return "userEditPage";
}
}
}

View File

@ -1,7 +1,8 @@
package com.webproglabs.lab1.lab34.controller.mvc_controllers;
package com.webproglabs.lab1.mvc;
import com.webproglabs.lab1.lab34.model.Profile;
import com.webproglabs.lab1.lab34.services.ProfileService;
import com.webproglabs.lab1.dto.UserSignupDto;
import com.webproglabs.lab1.models.User;
import com.webproglabs.lab1.services.UserService;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
@ -13,23 +14,28 @@ import org.springframework.web.bind.annotation.RequestMapping;
import javax.validation.Valid;
@Controller
@RequestMapping(UserSignupMvcController.SIGNUP_URL)
@RequestMapping()
public class UserSignupMvcController {
public static final String SIGNUP_URL = "/signup";
private final ProfileService userService;
private final UserService userService;
public UserSignupMvcController(ProfileService userService) {
public UserSignupMvcController(UserService userService) {
this.userService = userService;
}
@GetMapping
public String toFeed() {
return "redirect:/feed";
}
@GetMapping(SIGNUP_URL)
public String showSignupForm(Model model) {
model.addAttribute("userDto", new UserSignupDto());
return "signup";
}
@PostMapping
@PostMapping(SIGNUP_URL)
public String signup(@ModelAttribute("userDto") @Valid UserSignupDto userSignupDto,
BindingResult bindingResult,
Model model) {
@ -38,12 +44,12 @@ public class UserSignupMvcController {
return "signup";
}
try {
final Profile user = userService.createUser(
userSignupDto.getLogin(), userSignupDto.getPassword(), userSignupDto.getPasswordConfirm());
final User user = userService.createUser(userSignupDto.getLogin(), userSignupDto.getPassword(), userSignupDto.getPasswordConfirm());
return "redirect:/login?created=" + user.getLogin();
} catch (Exception e) {
model.addAttribute("errors", e.getMessage());
return "signup";
}
}
}

View File

@ -1,11 +1,11 @@
package com.webproglabs.lab1.lab34.services;
package com.webproglabs.lab1.services;
import com.webproglabs.lab1.lab34.model.Comment;
import com.webproglabs.lab1.lab34.model.Post;
import com.webproglabs.lab1.lab34.model.Profile;
import com.webproglabs.lab1.lab34.repository.CommentRepository;
import com.webproglabs.lab1.lab34.repository.PostRepository;
import com.webproglabs.lab1.lab34.repository.ProfileRepository;
import com.webproglabs.lab1.dao.CommentRepository;
import com.webproglabs.lab1.dao.PostRepository;
import com.webproglabs.lab1.dao.UserRepository;
import com.webproglabs.lab1.models.Comment;
import com.webproglabs.lab1.models.Post;
import com.webproglabs.lab1.models.User;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
@ -16,20 +16,18 @@ import java.util.Optional;
@Service
public class CommentService {
private final ProfileRepository profileRepository;
private final UserRepository userRepository;
private final CommentRepository commentRepository;
private final PostRepository postRepository;
private final PostRepository postRepository;
public CommentService(ProfileRepository profileRepository, CommentRepository commentRepository, PostRepository postRepository) {
this.profileRepository = profileRepository;
public CommentService(UserRepository profileRepository, CommentRepository commentRepository, PostRepository postRepository) {
this.userRepository = profileRepository;
this.commentRepository = commentRepository;
this.postRepository = postRepository;
}
@Transactional
public Comment findComment(Long id) {
public Comment findCommentById(Long id) {
final Optional<Comment> comment = commentRepository.findById(id);
return comment.orElseThrow(EntityNotFoundException::new);
}
@ -40,32 +38,17 @@ public class CommentService {
}
@Transactional
public List<Post> findFilteredComments(String filter) {
List<Post> postList = postRepository.findByTextLike("%" + filter + "%");
List<Comment> commentList = commentRepository.findByTextLike("%" + filter + "%");
List<Post> allPosts = postRepository.findAll();
for(Post post : allPosts) {
for (Comment comm : commentList) {
if (post.getComments().contains(comm) && !(postList.contains(post))) {
postList.add(post);
}
}
}
return postList;
}
@Transactional
public Comment addComment(String text, Long profileId, Long postId) {
public Comment addComment(String text, Long userId, Long postId) {
if (!StringUtils.hasText(text)) {
throw new IllegalArgumentException("Comment data is null or empty");
}
try{
Profile user = profileRepository.findById(profileId).get();
User user = userRepository.findById(userId).get();
Post post = postRepository.findById(postId).get();
Comment comment = new Comment(text, user, post);
user.addComment(comment);
post.addComment(comment);
profileRepository.save(user);
userRepository.save(user);
postRepository.save(post);
return commentRepository.save(comment);
}
@ -79,10 +62,10 @@ public class CommentService {
if (!StringUtils.hasText(text)) {
throw new IllegalArgumentException("Comment data is null or empty");
}
Comment currentComment = findComment(id);
Comment currentComment = findCommentById(id);
currentComment.setText(text);
final Profile owner = currentComment.getOwner();
profileRepository.save(owner);
final User owner = currentComment.getOwner();
userRepository.save(owner);
final Post post = currentComment.getPost();
postRepository.save(post);
return commentRepository.save(currentComment);
@ -90,13 +73,8 @@ public class CommentService {
@Transactional
public Comment deleteComment(Long id) {
final Comment currentComment = findComment(id);
final Comment currentComment = findCommentById(id);
commentRepository.delete(currentComment);
return currentComment;
}
@Transactional
public void deleteAllComments() {
commentRepository.deleteAll();
}
}

View File

@ -0,0 +1,86 @@
package com.webproglabs.lab1.services;
import com.webproglabs.lab1.dao.CommentRepository;
import com.webproglabs.lab1.dao.PostRepository;
import com.webproglabs.lab1.dao.TopicRepository;
import com.webproglabs.lab1.dao.UserRepository;
import com.webproglabs.lab1.models.Comment;
import com.webproglabs.lab1.models.Post;
import com.webproglabs.lab1.models.Topic;
import com.webproglabs.lab1.models.User;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import javax.persistence.EntityNotFoundException;
import javax.transaction.Transactional;
import java.util.List;
import java.util.Optional;
@Service
public class PostService {
private final PostRepository postRepository;
private final CommentRepository commentRepository;
private final UserRepository userRepository;
private final TopicRepository topicRepository;
public PostService(PostRepository postRepository, CommentRepository commentRepository, UserRepository userRepository, TopicRepository topicRepository) {
this.postRepository = postRepository;
this.commentRepository = commentRepository;
this.userRepository = userRepository;
this.topicRepository = topicRepository;
}
@Transactional
public Post findPostById(Long id) {
final Optional<Post> post = postRepository.findById(id);
return post.orElseThrow(EntityNotFoundException::new);
}
@Transactional
public List<Post> findAllPosts() {
return postRepository.findAll();
}
@Transactional
public List<Post> findPostsInTopicByText(String text, Long topicId) {
Topic topic = topicRepository.findById(topicId).get();
return postRepository.findPostsByTextInTopic(text, topic);
}
@Transactional
public Post addPost (String text, Long authorId, Long topicId) {
if (!StringUtils.hasText(text)) {
throw new IllegalArgumentException("Post data is null or empty");
}
User author = userRepository.findById(authorId).get();
Topic topic = topicRepository.findById(topicId).get();
Post post = new Post(text, author, topic);
author.addPost(post);
topic.addPost(post);
userRepository.save(author);
topicRepository.save(topic);
return postRepository.save(post);
}
@Transactional
public Post updatePost(Long id, String text) {
if (!StringUtils.hasText(text)) {
throw new IllegalArgumentException("Post data is null or empty");
}
final Post currentPost = findPostById(id);
currentPost.setText(text);
final User author = currentPost.getAuthor();
userRepository.save(author);
final Topic topic = currentPost.getTopic();
topicRepository.save(topic);
return postRepository.save(currentPost);
}
@Transactional
public Post deletePost(Long id) {
final Post currentPost = findPostById(id);
postRepository.delete(currentPost);
return currentPost;
}
}

View File

@ -0,0 +1,62 @@
package com.webproglabs.lab1.services;
import com.webproglabs.lab1.dao.TopicRepository;
import com.webproglabs.lab1.models.Post;
import com.webproglabs.lab1.models.Topic;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import javax.persistence.EntityNotFoundException;
import javax.transaction.Transactional;
import java.util.List;
@Service
public class TopicService {
private final TopicRepository topicRepository;
public TopicService(TopicRepository topicRepository) {
this.topicRepository = topicRepository;
}
@Transactional
public Topic addTopic(String name, String description) {
if (!StringUtils.hasText(name) || !StringUtils.hasText(description)) {
throw new IllegalArgumentException("Name is null or empty");
}
final Topic topic = new Topic(name, description);
return topicRepository.save(topic);
}
@Transactional
public Topic findTopicById(Long id) {
final Topic topic = topicRepository.findById(id).orElse(null);
if (topic == null) {
throw new EntityNotFoundException(String.format(" topic with id [%s] is not found", id));
}
return topic;
}
@Transactional
public List<Topic> findAllTopics() {
return topicRepository.findAll();
}
@Transactional
public Topic updateTopic(Long id, String name, String description) {
if (!StringUtils.hasText(name) || !StringUtils.hasText(description)) {
throw new IllegalArgumentException("Name is null or empty");
}
final Topic topic = findTopicById(id);
topic.setName(name);
topic.setDescription(description);
return topicRepository.save(topic);
}
@Transactional
public Topic deleteTopic(Long id) {
final Topic topic = findTopicById(id);
topicRepository.delete(topic);
return topic;
}
}

View File

@ -0,0 +1,102 @@
package com.webproglabs.lab1.services;
import com.webproglabs.lab1.dao.UserRepository;
import com.webproglabs.lab1.models.User;
import com.webproglabs.lab1.models.UserRole;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import javax.persistence.EntityNotFoundException;
import javax.transaction.Transactional;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
@Service
public class UserService implements UserDetailsService {
private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;
public UserService(UserRepository userRepository, PasswordEncoder passwordEncoder) {
this.userRepository = userRepository;
this.passwordEncoder = passwordEncoder;
}
@Transactional
public User findUserById(Long id) {
final Optional<User> user = userRepository.findById(id);
return user.orElseThrow(EntityNotFoundException::new);
}
@Transactional
public User findUserByLogin(String login) {
final User user = userRepository.findOneByLoginIgnoreCase(login).orElse(null);
return user;
}
@Transactional
public List<User> findAllUsers() {
return userRepository.findAll();
}
@Transactional
public User createUser(String login, String password, String passwordConfirm, UserRole role) {
if (findUserByLogin(login) != null) {
throw new IllegalArgumentException("User " + login + " already exists");
}
final User user = new User(login, passwordEncoder.encode(password), role);
if (!Objects.equals(password, passwordConfirm)) {
throw new IllegalArgumentException("Passwords not equals");
}
return userRepository.save(user);
}
@Transactional
public User createUser(String login, String password, String passwordConfirm) {
return createUser(login, password, passwordConfirm, UserRole.USER);
}
@Transactional
public User updateUser(Long id, String login, String password, String passwordConfirm) {
if (!StringUtils.hasText(login) || !StringUtils.hasText(password)) {
throw new IllegalArgumentException("User data is null or empty");
}
final User currentUser = findUserById(id);
if (Objects.equals(password, currentUser.getPassword())) {
throw new IllegalArgumentException("New password is the same as old");
}
if (!Objects.equals(password, passwordConfirm)) {
throw new IllegalArgumentException("Password mismatch");
}
currentUser.setLogin(login);
currentUser.setPassword(passwordEncoder.encode(password));
return userRepository.save(currentUser);
}
@Transactional
public User deleteUser(Long id) {
final User currentUser = findUserById(id);
userRepository.delete(currentUser);
return currentUser;
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
final User userEntity = findUserByLogin(username);
if (userEntity == null) {
throw new UsernameNotFoundException(username);
}
return new org.springframework.security.core.userdetails.User(
userEntity.getLogin(), userEntity.getPassword(), Collections.singleton(userEntity.getRole()));
}
}

View File

@ -1,3 +1,4 @@
<!DOCTYPE html>
<html lang="en"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
@ -13,7 +14,7 @@
<div class="modal-header">
<h5 class="modal-title" id="commentCreateLabel">Создать комментарий</h5>
</div>
<form th:action="@{/feed/comment/{id}/{postId}/{text} (id=${selectedProfile.id}, postId=${selectedPost.id}, text=${commentInputField}) }" method="post" class="modal-body text-center">
<form th:action="@{/feed/comment/{id}/{topicId}/{postId}/{text} (id=${selectedProfile.id}, topicId=${selectedTopic.id}, postId=${selectedPost.id}, text=${commentInputField}) }" method="post" class="modal-body text-center">
<p>Текст комментария:</p>
<input th:value="${commentInputField}" id="commentInputField" name="commentInputField" type="text" class="mb-2">
<br>

View File

@ -1,12 +1,13 @@
<!DOCTYPE html>
<html lang="ru"
xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5">
>
<head>
<meta charset="UTF-8"/>
<title>Лабораторная работа 5</title>
<title>СоцСеточка</title>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<link rel="icon" href="/favicon.svg">
<script type="text/javascript" src="/webjars/bootstrap/5.1.3/js/bootstrap.bundle.min.js"></script>
@ -18,14 +19,16 @@
</head>
<body>
<div>
<p class='text-center m-3 h3'> Лабораторная работа 5</p>
<div class="bg-light w-75 rounded-pill text-center pt-4 pb-4 mt-3 mx-auto">
<p class='text-center h3'> СоцСеточка</p>
</div>
<div>
<p class='h4 text-center'>
<a sec:authorize="hasRole('ROLE_ADMIN')" href="/profiles" class="text-decoration-none m-3">Профили</a>
<a sec:authorize="isAuthenticated()" href="/feed/" class="text-decoration-none m-3">Лента</a>
<a sec:authorize="isAuthenticated()" href="/logout" class="text-decoration-none m-3">
<p class='h4 mt-2 text-center'>
<a sec:authorize="hasRole('ROLE_ADMIN')" href="/topic" class="text-decoration-none m-3 text-secondary ">Темы</a>
<a sec:authorize="hasRole('ROLE_ADMIN')" href="/users" class="text-decoration-none m-3 text-secondary ">Пользователи</a>
<a sec:authorize="isAuthenticated()" href="/feed" class="text-decoration-none m-3 text-secondary ">Лента</a>
<a sec:authorize="isAuthenticated()" href="/users/settings" class="text-decoration-none m-3 text-secondary ">Настройки</a>
<a sec:authorize="isAuthenticated()" href="/logout" class="text-decoration-none m-3 text-secondary ">
Выход
</a>
</p>

View File

@ -13,7 +13,7 @@
<div class="modal-header">
<h5 class="modal-title" id="commentEditLabel">Редактировать комментарий</h5>
</div>
<form th:action="@{/feed/editComment/{id}/{commentId}/{text} (id=${selectedProfile.id}, commentId=${selectedComment.id}, text=${commentEditField}) }" method="post" class="modal-body text-center">
<form th:action="@{/feed/editComment/{id}/{commentId}/{text} (id=${selectedTopic.id}, commentId=${selectedComment.id}, text=${commentEditField}) }" method="post" class="modal-body text-center">
<p>Новый текст комментария:</p>
<input th:attr="value = ${selectedComment.text}" id="commentEditField" name="commentEditField" type="text" class="mb-2">
<br>

View File

@ -14,7 +14,7 @@
<div class="modal-header">
<h5 class="modal-title" id="postEditLabel">Редактировать пост</h5>
</div>
<form th:action="@{/feed/editPost/{id}/{authorId}/{text} (id=${selectedPost.id}, authorId=${selectedProfile.id}, text=${postEditField} ) }" method="post" class="modal-body text-center">
<form th:action="@{/feed/editPost/{id}/{topicId}/{text} (id=${selectedPost.id}, topicId=${selectedTopic.id}, text=${postEditField} ) }" method="post" class="modal-body text-center">
<p>Текст поста:</p>
<input th:attr="value = ${selectedPost.text}" id="postEditField" name="postEditField" type="text" class="mb-2">
<br>

View File

@ -4,10 +4,10 @@
layout:decorate="~{default}">
<body>
<div class="container" layout:fragment="content">
<div class="alert alert-danger">
<span th:text="${error}"></span>
</div>
<a href="/">На главную</a>
<div class="alert alert-danger">
<span th:text="${error}"></span>
</div>
<a href="/">На главную</a>
</div>
</body>
</html>

View File

@ -1,3 +1,4 @@
<!DOCTYPE html>
<html lang="en"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
@ -5,27 +6,26 @@
<head>
</head>
<body>
<div layout:fragment="content">
<div class="text-center">
<!-- <div class="dropdown text-center mx-auto w-25 ">-->
<!-- <div class="text-end mt-3">-->
<!-- <button class="btn btn-secondary dropdown-toggle ms-3 mb-3 " type="button" data-bs-toggle="dropdown" aria-expanded="false">-->
<!-- Выбор пользователя-->
<!-- </button>-->
<!-- <ul class="dropdown-menu " >-->
<!-- <li th:each="profile: ${profiles}">-->
<!-- <a class="dropdown-item" th:href="@{/feed/{id}(id=${profile.id})}" th:text="${profile.login}">-->
<!-- </a>-->
<!-- </li>-->
<!-- </ul>-->
<!-- </div>-->
<!-- </div>-->
<div layout:fragment="contentFeed"></div>
<div layout:fragment="content">
<div class="text-center">
<div class="dropdown text-center mx-auto w-75">
<div class="text-center mt-3">
<button class="btn btn-secondary dropdown-toggle mb-3 w-100" type="button" data-bs-toggle="dropdown" aria-expanded="false">
Выбор темы
</button>
<ul class="dropdown-menu w-100" >
<li th:each="topic: ${topics}">
<a class="dropdown-item" th:href="@{/feed/{id}(id=${topic.id})}" th:text="${topic.name + '. ' + topic.description}">
</a>
</li>
</ul>
</div>
</div>
<div layout:fragment="contentFeed"></div>
</div>
</div>
</div>
</div>
</body>

View File

@ -5,102 +5,106 @@
<head>
</head>
<body>
<div layout:fragment="contentFeed">
<div class='h3 mb-3 mx-auto w-25'>
<p class="text-end" th:text="${selectedProfile.login}"></p>
</div>
<div class='h3 m-3 d-flex justify-content-between text-center mx-auto w-25'>
Лента
<button type='button' class="btn btn-primary ms-5 mb-3 " data-bs-toggle="modal" data-bs-target="#postCreate">
Добавить новый пост
</button>
</div>
<form th:action="@{/feed/filter/{id}/{text} (id=${selectedProfile.id}, text=${searchField}) }" method="get">
<input th:value="${searchField}" id="searchField" name="searchField" type="text" class="mb-2" style="width: 20%" placeholder="Поиск...">
<button type='submit' class="btn btn-primary mb-2" style="width: 5%">
Найти
</button>
</form>
<div th:each="post: ${posts}" class="text-center mx-auto w-25 mb-3 ">
<div class="border p-2">
<p th:text="${post.text}" class="h4 text-start"></p>
<div class="d-flex justify-content-between fst-italic">
<div>
Автор:
<a th:text="${post.getAuthor()}" class="text-start fst-italic" th:href="@{/profile/{login}(login=${post.getAuthor()})}"></a>
</div>
<div th:if="${selectedProfile.getLogin() == post.getAuthor()}" class="d-flex justify-content-between fst-italic">
<form th:action="@{/feed/deletePost/{id}/{authorId} (id=${post.id}, authorId=${selectedProfile.id})}" method="post" >
<button type="submit" class="btn btn-danger me-1 mb-1 ms-2" >
<i class="fa fa-trash" aria-hidden="true"></i>
</button>
</form>
<form th:action="@{/feed/postModal/{id}/{authorId} (id=${post.id}, authorId=${selectedProfile.id})}" method="get">
<button type="submit" class="btn btn-warning mb-1" data-bs-toggle="modal" data-bs-target="#postEdit" >
<i class="fa fa-pencil" aria-hidden="true"></i>
</button>
</form>
</div>
</div>
</div>
<div class="border p-2" th:if="${post.comments.size() > 0}" >
<div class="text-start">
<p class="text-start h5">Комментарии:</p>
<div th:each="comment: ${post.comments}" >
<p class="fst-italic" th:text="${comment.getAuthor() + ':'}"> </p>
<div class="d-flex justify-content-between fst-italic">
<p class="ms-3" th:text="${comment.text}"></p>
<div th:if="${selectedProfile.getLogin() == comment.getAuthor()}" class="d-flex justify-content-between fst-italic">
<form th:action="@{/feed/deleteComment/{id}/{authorId} (id=${comment.id}, authorId=${selectedProfile.id})}" method="post" >
<button type="submit" class="btn btn-danger me-1 mb-1"> <i class="fa fa-trash" aria-hidden="true"> </i> </button>
</form>
<form th:action="@{/feed/commentEditModal/{id}/{authorId} (id=${comment.id}, authorId=${selectedProfile.id})}" method="get">
<button type="submit" class="btn btn-warning mb-1" data-bs-toggle="modal" data-bs-target="#postEdit" >
<i class="fa fa-pencil" aria-hidden="true"></i>
</button>
</form>
</div>
</div>
</div>
</div>
</div>
<form th:action="@{/feed/commentModal/{authorId}/{postId}/ ( authorId=${selectedProfile.id}, postId=${post.id} ) }" method="get" class="text-end">
<button type="submit" class="btn btn-info mb-3" data-bs-toggle="modal" data-bs-target="#commentCreate">Добавить комментарий</button>
</form>
</div>
<div class="modal fade" id="postCreate" tabindex="-1" role="dialog" aria-labelledby="postCreateLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="postCreateLabel">Создать пост</h5>
</div>
<form th:action="@{/feed/post/{id}/{text} (id=${selectedProfile.id}, text=${postInputField}) }" method="post" class="modal-body text-center">
<p>Текст поста:</p>
<input th:value="${postInputField}" id="postInputField" name="postInputField" type="text" class="mb-2">
<br>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Закрыть</button>
<button type="submit" class="btn btn-primary" >Сохранить</button>
</form>
</div>
</div>
</div>
<div layout:fragment="modalFeed"></div>
<div layout:fragment="contentFeed">
<div class='mb-3 mx-auto w-75'>
<div class="text-center w-100 border border-2 bg-light">
<p class="text-center h3" th:text="${selectedTopic.name}"></p>
<p class="text-center fw-light" th:text="${selectedTopic.description}"></p>
</div>
<p class="text-end h3 " th:text="${selectedProfile.login}"></p>
</div>
<div class='h3 m-3 d-flex justify-content-between text-center mx-auto w-75'>
Лента
<button type='button' class="btn btn-primary ms-5 mb-3 " data-bs-toggle="modal" data-bs-target="#postCreate">
Добавить новый пост
</button>
</div>
<form th:action="@{/feed/filter/{id}/{text} (id=${selectedTopic.id}, text=${searchField}) }" method="get">
<input th:value="${searchField}" id="searchField" name="searchField" type="text" class="mb-2" style="width: 70%" placeholder="Поиск...">
<button type='submit' class="btn btn-primary mb-2" style="width: 5%">
Найти
</button>
</form>
<div th:each="post: ${posts}" class="text-center mx-auto w-75 mb-3 ">
<div class="border p-2">
<p th:text="${post.text}" class="h4 text-start"></p>
<div class="d-flex justify-content-between fst-italic">
<div>
Автор:
<a th:text="${post.getAuthor()}" class="text-start fst-italic" th:href="@{/users/{login}(login=${post.getAuthor()})}"></a>
</div>
<div th:if="${selectedProfile.getLogin() == post.getAuthor()}" class="d-flex justify-content-between fst-italic">
<form th:action="@{/feed/deletePost/{id}/{topicId} (id=${post.id}, topicId=${selectedTopic.id})}" method="post" >
<button type="submit" class="btn btn-danger me-1 mb-1 ms-2" >
<i class="fa fa-trash" aria-hidden="true"></i>
</button>
</form>
<form th:action="@{/feed/postModal/{id}/{topicId} (id=${post.id}, topicId=${selectedTopic.id})}" method="get">
<button type="submit" class="btn btn-warning mb-1" data-bs-toggle="modal" data-bs-target="#postEdit" >
<i class="fa fa-pencil" aria-hidden="true"></i>
</button>
</form>
</div>
</div>
</div>
<div class="border p-2" th:if="${post.comments.size() > 0}" >
<div class="text-start">
<p class="text-start h5">Комментарии:</p>
<div th:each="comment: ${post.comments}" >
<p class="fst-italic" th:text="${comment.getAuthor() + ':'}"> </p>
<div class="d-flex justify-content-between fst-italic">
<p class="ms-3" th:text="${comment.text}"></p>
<div th:if="${selectedProfile.getLogin() == comment.getAuthor()}" class="d-flex justify-content-between fst-italic">
<form th:action="@{/feed/deleteComment/{id}/{topicId} (id=${comment.id}, topicId=${selectedTopic.id})}" method="post" >
<button type="submit" class="btn btn-danger me-1 mb-1"> <i class="fa fa-trash" aria-hidden="true"> </i> </button>
</form>
<form th:action="@{/feed/commentEditModal/{id}/{topicId} (id=${comment.id}, topicId=${selectedTopic.id})}" method="get">
<button type="submit" class="btn btn-warning mb-1" data-bs-toggle="modal" data-bs-target="#postEdit" >
<i class="fa fa-pencil" aria-hidden="true"></i>
</button>
</form>
</div>
</div>
</div>
</div>
</div>
<form th:action="@{/feed/commentModal/{topicId}/{postId}/ ( topicId=${selectedTopic.id}, postId=${post.id} ) }" method="get" class="text-end">
<button type="submit" class="btn btn-info mb-3" data-bs-toggle="modal" data-bs-target="#commentCreate">Добавить комментарий</button>
</form>
</div>
<div class="modal fade" id="postCreate" tabindex="-1" role="dialog" aria-labelledby="postCreateLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="postCreateLabel">Создать пост</h5>
</div>
<form th:action="@{/feed/{topicId}/post/{id}/{text} (topicId=${selectedTopic.id}, id=${selectedProfile.id}, text=${postInputField}) }" method="post" class="modal-body text-center">
<p>Текст поста:</p>
<input th:value="${postInputField}" id="postInputField" name="postInputField" type="text" class="mb-2">
<br>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Закрыть</button>
<button type="submit" class="btn btn-primary" >Сохранить</button>
</form>
</div>
</div>
</div>
<div layout:fragment="modalFeed"></div>
</div>
</body>
</html>

View File

@ -1,30 +1,31 @@
<!DOCTYPE html>
<html lang="en"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{default}">
<body>
<div class="container" layout:fragment="content">
<div th:if="${param.error}" class="alert alert-danger margin-bottom">
Пользователь не найден или пароль указан не верно
<div th:if="${param.error}" class="alert alert-danger margin-bottom">
Пользователь не найден или пароль указан не верно
</div>
<div th:if="${param.logout}" class="alert alert-success margin-bottom">
Выход успешно произведен
</div>
<div th:if="${param.created}" class="alert alert-success margin-bottom">
Пользователь '<span th:text="${param.created}"></span>' успешно создан
</div>
<form th:action="@{/login}" method="post" class="container-padding">
<div class="mb-3">
<input type="text" name="username" id="username" class="form-control"
placeholder="Логин" required="true" autofocus="true"/>
</div>
<div th:if="${param.logout}" class="alert alert-success margin-bottom">
Выход успешно произведен
<div class="mb-3">
<input type="password" name="password" id="password" class="form-control"
placeholder="Пароль" required="true"/>
</div>
<div th:if="${param.created}" class="alert alert-success margin-bottom">
Пользователь '<span th:text="${param.created}"></span>' успешно создан
</div>
<form th:action="@{/login}" method="post" class="container-padding">
<div class="mb-3">
<input type="text" name="username" id="username" class="form-control"
placeholder="Логин" required="true" autofocus="true"/>
</div>
<div class="mb-3">
<input type="password" name="password" id="password" class="form-control"
placeholder="Пароль" required="true"/>
</div>
<button type="submit" class="btn btn-success button-fixed">Войти</button>
<a class="btn btn-primary button-fixed" href="/signup">Регистрация</a>
</form>
<button type="submit" class="btn btn-success button-fixed">Войти</button>
<a class="btn btn-primary button-fixed" href="/signup">Регистрация</a>
</form>
</div>
</body>
</html>

View File

@ -1,56 +0,0 @@
<!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">
<head>
</head>
<body>
<div layout:fragment="content" class="text-center">
<div th:each="profile: ${profiles}" class="mb-2">
<div class="text-center">
<div class="text-start mx-auto w-25 border p-3">
<p class='h5'>Профиль</p>
<p th:text="${'Логин: ' + profile.login}"></p>
<p class='h6 '>Список постов пользователя: </p>
<div th:if="${profile.posts.size()>0}">
<div th:each="post: ${profile.posts}">
<p th:text="${post.text}" class="mb-3 ms-2 border p-2"></p>
</div>
</div>
<p th:unless="${profile.posts.size()>0}">
Нет постов
</p>
<div class="text-end">
<form action="#" th:action="@{/profile/{id} (id=${profile.id})}" method="post" >
<button type="submit" class="btn btn-danger">Удалить</button>
</form>
</div>
</div>
</div>
</div>
<div class="modal fade" id="profileCreate" tabindex="-1" role="dialog" aria-labelledby="profileCreateLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="profileCreateLabel">Регистрация профиля</h5>
</div>
<form action="#" th:action="@{/profile/create/}" th:object="${profileDto}" method="post" class="modal-body text-center">
<p>Логин: </p>
<input th:field="${profileDto.login}" type="text" class="mb-2 form-control" required="true" />
<br>
<p>Пароль: </p>
<input th:field="${profileDto.password}" type="text" class="mb-2 form-control" required="true" />
<br>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Закрыть</button>
<button type="submit" class="btn btn-primary" >Регистрация</button>
</form>
</div>
</div>
</div>
</div>
</body>
</html>

View File

@ -4,25 +4,25 @@
layout:decorate="~{default}">
<body>
<div class="container container-padding" layout:fragment="content">
<div th:if="${errors}" th:text="${errors}" class="margin-bottom alert alert-danger"></div>
<form action="#" th:action="@{/signup}" th:object="${userDto}" method="post">
<div class="mb-3">
<input type="text" class="form-control" th:field="${userDto.login}"
placeholder="Логин" required="true" autofocus="true" maxlength="64"/>
</div>
<div class="mb-3">
<input type="password" class="form-control" th:field="${userDto.password}"
placeholder="Пароль" required="true" minlength="6" maxlength="64"/>
</div>
<div class="mb-3">
<input type="password" class="form-control" th:field="${userDto.passwordConfirm}"
placeholder="Пароль (подтверждение)" required="true" minlength="6" maxlength="64"/>
</div>
<div class="mb-3">
<button type="submit" class="btn btn-success button-fixed">Создать</button>
<a class="btn btn-primary button-fixed" href="/login">Назад</a>
</div>
</form>
<div th:if="${errors}" th:text="${errors}" class="margin-bottom alert alert-danger"></div>
<form action="#" th:action="@{/signup}" th:object="${userDto}" method="post">
<div class="mb-3">
<input type="text" class="form-control" th:field="${userDto.login}"
placeholder="Логин" required="true" autofocus="true" maxlength="64"/>
</div>
<div class="mb-3">
<input type="password" class="form-control" th:field="${userDto.password}"
placeholder="Пароль" required="true" minlength="4" maxlength="64"/>
</div>
<div class="mb-3">
<input type="password" class="form-control" th:field="${userDto.passwordConfirm}"
placeholder="Пароль (подтверждение)" required="true" minlength="4" maxlength="64"/>
</div>
<div class="mb-3">
<button type="submit" class="btn btn-success button-fixed">Создать</button>
<a class="btn btn-primary button-fixed" href="/login">Назад</a>
</div>
</form>
</div>
</body>
</html>

View File

@ -0,0 +1,19 @@
<!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 container-padding" layout:fragment="content">
<div class="h4 text-center">Изменить топик</div>
<form action="#" th:action="@{/topic/edit/{id} (id=${topic.id})}" th:object="${topicDto}" method="post">
<p>Новое название:</p>
<input th:field="${topicDto.name}" type="text" class="mb-2 form-control" required="true" />
<p>Новое описание:</p>
<input th:field="${topicDto.description}" type="text" class="mb-2 form-control" required="true" />
<button type="submit" class="btn btn-success" >Сохранить изменения</button>
</form>
</div>
</body>
</html>

View File

@ -0,0 +1,32 @@
<!DOCTYPE html>
<html lang="en"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{default}">
<body>
<div class="container container-padding" layout:fragment="content">
<form action="#" th:action="@{/topic/create}" th:object="${topicDto}" method="post">
<p>Название:</p>
<input th:field="${topicDto.name}" type="text" class="mb-2 form-control" required="true" />
<p>Описание:</p>
<input th:field="${topicDto.description}" type="text" class="mb-2 form-control" required="true" />
<button type="submit" class="btn btn-success">Добавить</button>
</form>
<div th:each="topic: ${topics}" class="border p-3 m-3 shadow-lg bg-body rounded">
<div th:text="${'Топик: ' + topic.name}"></div>
<div th:text="${'Описание: ' + topic.description}"></div>
<form action="#" th:action="@{/topic/edit/{id} (id=${topic.id})}" method="get" class="text-end m-1">
<button type="submit" class="btn btn-warning">Изменить</button>
</form>
<form action="#" th:action="@{/topic/delete/{id} (id=${topic.id})}" method="post" class="text-end m-1">
<button type="submit" class="btn btn-danger" >Удалить</button>
</form>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,31 @@
<!DOCTYPE html>
<html lang="en"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{default}">
<head>
</head>
<body>
<div layout:fragment="content">
<div class="text-center">
<div th:if="${errors}" th:text="${errors}" class="margin-bottom alert alert-danger"></div>
<div th:if="${success}" th:text="${success}" class="margin-bottom text-success"></div>
<div class="text-start mx-auto w-50 border p-5">
<p class='h5'>Изменение данных пользователя</p>
<p th:text="${'Логин: ' + user.login}"></p>
<form action="#" th:action="@{/users/edit/{id} (id=${user.id})}" th:object="${userDto}" method="post">
<p>Новый логин:</p>
<input th:field="${userDto.login}" type="text" class="mb-2 form-control" required="true" autofocus="true" maxlength="64"/>
<p>Новый пароль:</p>
<input th:field="${userDto.password}" type="password" class="mb-2 form-control" required="true" minlength="4" maxlength="64"/>
<p>Повторите пароль:</p>
<input th:field="${userDto.passwordConfirm}" type="password" class="mb-2 form-control" required="true" minlength="4" maxlength="64"/>
<button type="submit" class="btn btn-success" >Сохранить изменения</button>
</form>
</div>
</div>
</div>
</body>
</html>

View File

@ -8,17 +8,17 @@
<div layout:fragment="content">
<div class="text-center">
<div class="text-start mx-auto w-25 border p-5">
<div class="text-start mx-auto w-50 border p-5">
<p class='h5'>Профиль</p>
<p th:text="${'Логин: ' + profile.login}"></p>
<p th:text="${'Логин: ' + user.login}"></p>
<p class='h6 '>Список постов пользователя: </p>
<div th:if="${profile.posts.size()>0}">
<div th:each="post: ${profile.posts}">
<div th:if="${user.posts.size()>0}">
<div th:each="post: ${user.posts}">
<p th:text="${post.text}" class="mb-3 ms-2 border p-2"></p>
</div>
</div>
<p th:unless="${profile.posts.size()>0}">
<p th:unless="${user.posts.size()>0}">
Нет постов
</p>
</div>

View File

@ -0,0 +1,36 @@
<!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">
<head>
</head>
<body>
<div layout:fragment="content" class="text-center">
<div th:each="profile: ${profiles}" class="mb-2">
<div class="text-center">
<div class="text-start mx-auto w-50 border shadow p-3">
<p class='h5'>Профиль</p>
<p th:text="${'Логин: ' + profile.login}"></p>
<p class='h6 '>Список постов пользователя: </p>
<div th:if="${profile.posts.size()>0}">
<div th:each="post: ${profile.posts}">
<p th:text="${post.text}" class="mb-3 ms-2 border p-2"></p>
</div>
</div>
<p th:unless="${profile.posts.size()>0}">
Нет постов
</p>
<div class="text-end" th:if="${profile.login!='admin'}">
<form action="#" th:action="@{/users/{id} (id=${profile.id})}" method="post" >
<button type="submit" class="btn btn-danger">Удалить</button>
</form>
</div>
</div>
</div>
</div>
</div>
</body>
</html>