diff --git a/src/main/java/site/easystartup/web/EasyStartupApplication.java b/src/main/java/site/easystartup/web/EasyStartupApplication.java index a5906e4..ac0472b 100644 --- a/src/main/java/site/easystartup/web/EasyStartupApplication.java +++ b/src/main/java/site/easystartup/web/EasyStartupApplication.java @@ -1,17 +1,10 @@ package site.easystartup.web; -import io.swagger.v3.oas.annotations.OpenAPIDefinition; -import io.swagger.v3.oas.annotations.enums.SecuritySchemeIn; -import io.swagger.v3.oas.annotations.enums.SecuritySchemeType; -import io.swagger.v3.oas.annotations.info.Info; -import io.swagger.v3.oas.annotations.security.SecurityRequirement; -import io.swagger.v3.oas.annotations.security.SecurityScheme; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.web.bind.annotation.RestController; @SpringBootApplication -@SecurityScheme(type = SecuritySchemeType.OAUTH2, name = "api_key", in = SecuritySchemeIn.HEADER) public class EasyStartupApplication { public static void main(String[] args) { diff --git a/src/main/java/site/easystartup/web/auth/controller/AuthController.java b/src/main/java/site/easystartup/web/auth/controller/AuthController.java index 9a09d76..ed4193b 100644 --- a/src/main/java/site/easystartup/web/auth/controller/AuthController.java +++ b/src/main/java/site/easystartup/web/auth/controller/AuthController.java @@ -1,7 +1,5 @@ package site.easystartup.web.auth.controller; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; import org.modelmapper.ModelMapper; import org.springframework.http.ResponseEntity; @@ -18,7 +16,6 @@ import org.springframework.web.bind.annotation.RestController; import site.easystartup.web.domain.model.User; import site.easystartup.web.domain.request.LoginRequest; import site.easystartup.web.domain.request.SignupRequest; -import site.easystartup.web.domain.request.UpdatePassportRequest; import site.easystartup.web.domain.response.MessageResponse; import site.easystartup.web.domain.response.SuccessAuthenticationResponse; import site.easystartup.web.domain.validation.ResponseErrorValidation; @@ -29,12 +26,10 @@ import site.easystartup.web.service.AuthService; import site.easystartup.web.service.UserService; import javax.validation.Valid; -import java.security.Principal; @RestController @RequestMapping("/api/auth") @RequiredArgsConstructor -@Tag(name = "Авторизация") public class AuthController { private final JWTUtil jwtUtil; private final UserService userService; @@ -44,7 +39,6 @@ public class AuthController { private final ModelMapper modelMapper; @PostMapping("/signup") - @Operation(summary = "Создание нового аккаунта") public ResponseEntity registration(@Valid @RequestBody SignupRequest signupRequest, BindingResult bindingResult) { ResponseEntity errors = responseErrorValidation.mapValidationService(bindingResult); @@ -56,7 +50,6 @@ public class AuthController { } @PostMapping("/login") - @Operation(summary = "Логин, получение JWT ключа и данных текущего пользователя") public ResponseEntity authentication(@Valid @RequestBody LoginRequest loginRequest, BindingResult bindingResult) { ResponseEntity errors = responseErrorValidation.mapValidationService(bindingResult); @@ -75,28 +68,5 @@ public class AuthController { return ResponseEntity.ok().body(response); } - @PostMapping("/password") - @Operation(summary = "обновление пароля и получение нового JWT") - public ResponseEntity updatePassword(@Valid @RequestBody UpdatePassportRequest passportRequest, - BindingResult bindingResult, - Principal principal) { - ResponseEntity errors = responseErrorValidation.mapValidationService(bindingResult); - if (!ObjectUtils.isEmpty(errors)) return errors; - - User user = authService.updatePassword(passportRequest, principal); - - - Authentication authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken( - user.getUsername(), - user.getPassword() - )); - - SecurityContextHolder.getContext().setAuthentication(authentication); - - String jwt = SecurityConstants.TOKEN_PREFIX + jwtUtil.generateToken(user.getUsername()); - SuccessAuthenticationResponse response = new SuccessAuthenticationResponse(jwt, modelMapper.map(user, UserDto.class)); - return ResponseEntity.ok().body(response); - } - } diff --git a/src/main/java/site/easystartup/web/domain/request/SignupRequest.java b/src/main/java/site/easystartup/web/domain/request/SignupRequest.java index 1119baf..ab74799 100644 --- a/src/main/java/site/easystartup/web/domain/request/SignupRequest.java +++ b/src/main/java/site/easystartup/web/domain/request/SignupRequest.java @@ -1,6 +1,10 @@ package site.easystartup.web.domain.request; import lombok.Data; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.ResponseBody; import site.easystartup.web.domain.annotation.PasswordMatches; import site.easystartup.web.domain.model.Role; diff --git a/src/main/java/site/easystartup/web/exception/NoPermissionException.java b/src/main/java/site/easystartup/web/exception/NoPermissionException.java index 6948923..59901ff 100644 --- a/src/main/java/site/easystartup/web/exception/NoPermissionException.java +++ b/src/main/java/site/easystartup/web/exception/NoPermissionException.java @@ -1,5 +1,9 @@ package site.easystartup.web.exception; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +@ResponseStatus(HttpStatus.FORBIDDEN) public class NoPermissionException extends RuntimeException { public NoPermissionException(String username) { super("User " + username + " do not have access for this action"); diff --git a/src/main/java/site/easystartup/web/notification/controller/NotificationController.java b/src/main/java/site/easystartup/web/notification/controller/NotificationController.java new file mode 100644 index 0000000..0ad20ac --- /dev/null +++ b/src/main/java/site/easystartup/web/notification/controller/NotificationController.java @@ -0,0 +1,28 @@ +package site.easystartup.web.notification.controller; + +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.messaging.handler.annotation.Payload; +import org.springframework.messaging.simp.SimpMessagingTemplate; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import site.easystartup.web.notification.domian.Notification; + +import java.security.Principal; +import java.util.logging.Logger; + +@Controller +@RequiredArgsConstructor(onConstructor_ = @Autowired) +public class NotificationController { + private final SimpMessagingTemplate messagingTemplate; + Logger logger = Logger.getGlobal(); + + @GetMapping("/notify") + public void notification(Principal principal, @Payload Notification notification) { + logger.info("Web socket sent notification to " + principal.getName()); + messagingTemplate.convertAndSendToUser(principal.getName(), "queue/notification", notification); + } + + +} diff --git a/src/main/java/site/easystartup/web/notification/controller/NotificationRestController.java b/src/main/java/site/easystartup/web/notification/controller/NotificationRestController.java new file mode 100644 index 0000000..635ae00 --- /dev/null +++ b/src/main/java/site/easystartup/web/notification/controller/NotificationRestController.java @@ -0,0 +1,32 @@ +package site.easystartup.web.notification.controller; + +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RestController; +import site.easystartup.web.notification.domian.Notification; +import site.easystartup.web.notification.repo.NotificationRepo; +import site.easystartup.web.notification.service.NotificationService; + +import java.security.Principal; +import java.util.List; + +@RestController +@RequiredArgsConstructor(onConstructor_ = @Autowired) +public class NotificationRestController { + private final NotificationService notificationService; + + @GetMapping("/notifications/{username}") + public ResponseEntity> notification(@PathVariable String username, Principal principal) { + return ResponseEntity.ok().body(notificationService.getNotificationByRecipient(username, principal)); + } + + @DeleteMapping("/notifications/{id}/delete") + public void delete(@PathVariable long id, + Principal principal) { + notificationService.delete(id, principal); + } +} diff --git a/src/main/java/site/easystartup/web/notification/domian/Notification.java b/src/main/java/site/easystartup/web/notification/domian/Notification.java new file mode 100644 index 0000000..a8eb10a --- /dev/null +++ b/src/main/java/site/easystartup/web/notification/domian/Notification.java @@ -0,0 +1,17 @@ +package site.easystartup.web.notification.domian; + +import lombok.*; + +import javax.persistence.Entity; +import javax.persistence.Id; + +@Getter +@Setter +@ToString +@Entity +public class Notification { + @Id + private long id; + private String text; + private String recipient; +} diff --git a/src/main/java/site/easystartup/web/notification/event/EventHandler.java b/src/main/java/site/easystartup/web/notification/event/EventHandler.java new file mode 100644 index 0000000..955dd9d --- /dev/null +++ b/src/main/java/site/easystartup/web/notification/event/EventHandler.java @@ -0,0 +1,12 @@ +package site.easystartup.web.notification.event; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) + +@Target(ElementType.METHOD) +public @interface EventHandler { +} diff --git a/src/main/java/site/easystartup/web/notification/event/NotificationEvent.java b/src/main/java/site/easystartup/web/notification/event/NotificationEvent.java new file mode 100644 index 0000000..c0a6a6a --- /dev/null +++ b/src/main/java/site/easystartup/web/notification/event/NotificationEvent.java @@ -0,0 +1,9 @@ +package site.easystartup.web.notification.event; + +public interface NotificationEvent { + void invoke(); + + String getRecipient(); + + String getMessage(); +} diff --git a/src/main/java/site/easystartup/web/notification/event/NotificationEventManager.java b/src/main/java/site/easystartup/web/notification/event/NotificationEventManager.java new file mode 100644 index 0000000..ae549e4 --- /dev/null +++ b/src/main/java/site/easystartup/web/notification/event/NotificationEventManager.java @@ -0,0 +1,36 @@ +package site.easystartup.web.notification.event; + +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +@Component +@Scope("singleton") +public class NotificationEventManager { + private List listeners = new ArrayList<>(); + + public void registerListener(NotificationListener listener) { + listeners.add(listener); + } + + public void removeListener(NotificationListener listener) { + listeners.remove(listener); + } + + public void notifyListeners(Class event) { + listeners.forEach(listener -> Arrays.stream(listener.getClass().getMethods()). + filter(m -> m.isAnnotationPresent(EventHandler.class)).forEach(method -> { + if (method.getParameterTypes()[0].equals(event.getClass())) { + try { + method.invoke(listener, event); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } + } + })); + } +} diff --git a/src/main/java/site/easystartup/web/notification/event/NotificationListener.java b/src/main/java/site/easystartup/web/notification/event/NotificationListener.java new file mode 100644 index 0000000..d6392c9 --- /dev/null +++ b/src/main/java/site/easystartup/web/notification/event/NotificationListener.java @@ -0,0 +1,4 @@ +package site.easystartup.web.notification.event; + +public interface NotificationListener { +} diff --git a/src/main/java/site/easystartup/web/notification/event/realtime/RealTimeListener.java b/src/main/java/site/easystartup/web/notification/event/realtime/RealTimeListener.java new file mode 100644 index 0000000..7dd41a8 --- /dev/null +++ b/src/main/java/site/easystartup/web/notification/event/realtime/RealTimeListener.java @@ -0,0 +1,26 @@ +package site.easystartup.web.notification.event.realtime; + +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import site.easystartup.web.notification.domian.Notification; +import site.easystartup.web.notification.event.EventHandler; +import site.easystartup.web.notification.event.NotificationEvent; +import site.easystartup.web.notification.event.NotificationListener; +import site.easystartup.web.notification.repo.NotificationRepo; +import site.easystartup.web.notification.service.NotificationService; +import site.easystartup.web.post.domain.event.RepostEvent; + +@RequiredArgsConstructor(onConstructor_ = @Autowired) +public class RealTimeListener implements NotificationListener { + private final NotificationService notificationService; + + private final NotificationRepo repo; + + @EventHandler + public void onEvent(NotificationEvent event) { + Notification notification = new Notification(); + notification.setText(event.getMessage()); + notification.setRecipient(event.getRecipient()); + notificationService.sendNotification(repo.save(notification)); + } +} diff --git a/src/main/java/site/easystartup/web/notification/repo/NotificationRepo.java b/src/main/java/site/easystartup/web/notification/repo/NotificationRepo.java new file mode 100644 index 0000000..175f913 --- /dev/null +++ b/src/main/java/site/easystartup/web/notification/repo/NotificationRepo.java @@ -0,0 +1,7 @@ +package site.easystartup.web.notification.repo; + +import org.springframework.data.jpa.repository.JpaRepository; +import site.easystartup.web.notification.domian.Notification; + +public interface NotificationRepo extends JpaRepository { +} diff --git a/src/main/java/site/easystartup/web/notification/service/NotificationService.java b/src/main/java/site/easystartup/web/notification/service/NotificationService.java new file mode 100644 index 0000000..403be0c --- /dev/null +++ b/src/main/java/site/easystartup/web/notification/service/NotificationService.java @@ -0,0 +1,43 @@ +package site.easystartup.web.notification.service; + +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.messaging.simp.SimpMessagingTemplate; +import org.springframework.stereotype.Component; +import site.easystartup.web.exception.NoPermissionException; +import site.easystartup.web.notification.domian.Notification; +import site.easystartup.web.notification.repo.NotificationRepo; +import site.easystartup.web.service.UserService; + +import java.security.Principal; +import java.util.List; + +@Component +@RequiredArgsConstructor(onConstructor_ = @Autowired) +public class NotificationService { + private final SimpMessagingTemplate messagingTemplate; + + private final NotificationRepo repo; + + private final UserService userService; + + public void sendNotification(Notification notification) { + messagingTemplate.convertAndSendToUser(notification.getRecipient(), "queue/notification", notification); + } + + public List getNotificationByRecipient(String recipient, Principal principal) { + if(!recipient.equals(principal.getName()) && !userService.getUserByPrincipal(principal).isAdmin()) + throw new NoPermissionException(String.format("User %s dont have access to see notifications of user %s", + principal.getName(), recipient)); + return repo.findAll().stream() + .filter(notification -> notification.getRecipient().equals(recipient)).toList(); + } + + public void delete(long id, Principal principal) { + Notification notification = repo.findById(id).orElseThrow(); + if(!notification.getRecipient().equals(principal.getName()) && !userService.getUserByPrincipal(principal).isAdmin()) + throw new NoPermissionException(String.format("User %s dont have access to delete notifications of user %s", + principal.getName(), notification.getRecipient())); + repo.delete(notification); + } +} diff --git a/src/main/java/site/easystartup/web/post/controller/PostController.java b/src/main/java/site/easystartup/web/post/controller/PostController.java new file mode 100644 index 0000000..7bd33e2 --- /dev/null +++ b/src/main/java/site/easystartup/web/post/controller/PostController.java @@ -0,0 +1,96 @@ +package site.easystartup.web.post.controller; + +import lombok.RequiredArgsConstructor; +import org.modelmapper.ModelMapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.annotation.Secured; +import org.springframework.util.ObjectUtils; +import org.springframework.validation.BindingResult; +import org.springframework.web.bind.annotation.*; +import site.easystartup.web.domain.model.User; +import site.easystartup.web.domain.response.MessageResponse; +import site.easystartup.web.domain.validation.ResponseErrorValidation; +import site.easystartup.web.exception.NoPermissionException; +import site.easystartup.web.post.domain.model.Post; +import site.easystartup.web.post.domain.request.PostRequest; +import site.easystartup.web.post.dto.PostDto; +import site.easystartup.web.post.service.PostService; +import site.easystartup.web.project.domain.requst.ProjectRequest; +import site.easystartup.web.service.UserService; + +import javax.validation.Valid; +import java.security.Principal; +import java.util.List; +import java.util.stream.Collectors; + +@RestController +@RequestMapping("/post") +@RequiredArgsConstructor(onConstructor_ = @Autowired) +public class PostController { + private final PostService postService; + private final UserService userService; + private final ModelMapper modelMapper; + private final ResponseErrorValidation responseErrorValidation; + + @GetMapping("/") + public ResponseEntity> main(Principal principal) { + var posts = postService.getAllPostsByAuthor(principal.getName()) + .stream().map(post -> modelMapper.map(post, PostDto.class)).toList(); + return ResponseEntity.ok().body(posts); + } + + @PostMapping("/new") + public ResponseEntity createPost(@Valid @RequestBody PostRequest postRequest, BindingResult bindingResult, + Principal principal) { + var errors = responseErrorValidation.mapValidationService(bindingResult); + if (!ObjectUtils.isEmpty(errors)) return errors; + + Post post = postService.createPost(postRequest, principal); + return ResponseEntity.ok().body(modelMapper.map(post, PostRequest.class)); + } + + @GetMapping("/user/{username}") + public ResponseEntity> user(@PathVariable String username) { + var posts = postService.getAllPostsByAuthor(username) + .stream().map(post -> modelMapper.map(post, PostDto.class)).toList(); + return ResponseEntity.ok().body(posts); + } + + @GetMapping("/{post_id}/edit/") + public ResponseEntity edit(@Valid @RequestBody PostRequest postRequest, BindingResult bindingResult, + Principal principal, @PathVariable long post_id) { + var errors = responseErrorValidation.mapValidationService(bindingResult); + if (!ObjectUtils.isEmpty(errors)) return errors; + + Post post = postService.edit(postRequest, post_id, principal); + return ResponseEntity.ok().body(modelMapper.map(post, PostRequest.class)); + } + + @DeleteMapping("/{post_id}/delete") + public void delete(@PathVariable long post_id, + Principal principal) { + Post post = postService.getPostById(post_id); + if(!post.getSender().equals(principal.getName()) && !userService.getUserByPrincipal(principal).isAdmin()) + throw new NoPermissionException(String.format("User %s cant delete post %s", principal.getName(), post_id)); + else postService.delete(post); + } + + @PutMapping("/{post_id}/like/") + public ResponseEntity like(Principal principal, @PathVariable long post_id) { + Post post = postService.getPostById(post_id); + + Post edited = postService.addLike(post, principal); + return ResponseEntity.ok().body(modelMapper.map(edited, PostRequest.class)); + } + + @PutMapping("/{post_id}/dislike/") + public ResponseEntity dislike(Principal principal, @PathVariable long post_id) { + Post post = postService.getPostById(post_id); + + Post edited = postService.disLike(post, principal); + return ResponseEntity.ok().body(modelMapper.map(edited, PostRequest.class)); + } + + +} diff --git a/src/main/java/site/easystartup/web/post/domain/event/RepostEvent.java b/src/main/java/site/easystartup/web/post/domain/event/RepostEvent.java new file mode 100644 index 0000000..776448a --- /dev/null +++ b/src/main/java/site/easystartup/web/post/domain/event/RepostEvent.java @@ -0,0 +1,27 @@ +package site.easystartup.web.post.domain.event; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import site.easystartup.web.notification.event.NotificationEvent; +import site.easystartup.web.notification.event.NotificationEventManager; + +@RequiredArgsConstructor +public class RepostEvent implements NotificationEvent { + @Autowired + private NotificationEventManager notificationEventManager; + + @Getter + private final String recipient; + + @Getter + private final String reportedUser; + + @Getter + private final String message; + + @Override + public void invoke() { + notificationEventManager.notifyListeners(getClass()); + } +} diff --git a/src/main/java/site/easystartup/web/post/domain/exception/AlreadyLikedException.java b/src/main/java/site/easystartup/web/post/domain/exception/AlreadyLikedException.java new file mode 100644 index 0000000..fc98f9d --- /dev/null +++ b/src/main/java/site/easystartup/web/post/domain/exception/AlreadyLikedException.java @@ -0,0 +1,7 @@ +package site.easystartup.web.post.domain.exception; + +public class AlreadyLikedException extends RuntimeException { + public AlreadyLikedException(String message) { + super(message); + } +} diff --git a/src/main/java/site/easystartup/web/post/domain/exception/DontLickedException.java b/src/main/java/site/easystartup/web/post/domain/exception/DontLickedException.java new file mode 100644 index 0000000..2c083d5 --- /dev/null +++ b/src/main/java/site/easystartup/web/post/domain/exception/DontLickedException.java @@ -0,0 +1,7 @@ +package site.easystartup.web.post.domain.exception; + +public class DontLickedException extends RuntimeException{ + public DontLickedException(String message) { + super(message); + } +} diff --git a/src/main/java/site/easystartup/web/post/domain/exception/PostNotFoundException.java b/src/main/java/site/easystartup/web/post/domain/exception/PostNotFoundException.java new file mode 100644 index 0000000..03d7228 --- /dev/null +++ b/src/main/java/site/easystartup/web/post/domain/exception/PostNotFoundException.java @@ -0,0 +1,21 @@ +package site.easystartup.web.post.domain.exception; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +import java.util.function.Supplier; + +@ResponseStatus(HttpStatus.BAD_REQUEST) +public class PostNotFoundException extends RuntimeException implements Supplier { + private String message; + + public PostNotFoundException(String message) { + super(message); + this.message = message; + } + + @Override + public PostNotFoundException get() { + return this; + } +} diff --git a/src/main/java/site/easystartup/web/post/domain/model/Post.java b/src/main/java/site/easystartup/web/post/domain/model/Post.java new file mode 100644 index 0000000..1499a20 --- /dev/null +++ b/src/main/java/site/easystartup/web/post/domain/model/Post.java @@ -0,0 +1,70 @@ +package site.easystartup.web.post.domain.model; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.Setter; +import lombok.ToString; +import org.hibernate.Hibernate; +import site.easystartup.web.domain.model.Role; + +import javax.persistence.*; +import java.util.Date; +import java.util.List; +import java.util.Objects; +import java.util.Set; + +@Getter +@Setter +@ToString +@RequiredArgsConstructor +@Entity +@Table(name = "pst") +public class Post implements Comparable{ + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + @Column(name = "post_id") + private long id; + @Column(columnDefinition="varchar(1024)") + private String text; + private String sender; + @Column(name = "lk") + private long like; + + @ElementCollection(targetClass = Long.class, fetch = FetchType.LAZY) + @CollectionTable(name = "post_answers", joinColumns = @JoinColumn(name = "post_id")) + private Set answers; + + @ElementCollection(targetClass = String.class, fetch = FetchType.LAZY) + @CollectionTable(name = "post_liked", joinColumns = @JoinColumn(name = "post_id")) + private Set liked_users; + + private Long answered_post; + + @Column(name = "date", updatable = false) + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date date; + + private boolean edited; + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || Hibernate.getClass(this) != Hibernate.getClass(o)) return false; + Post that = (Post) o; + return Objects.equals(id, that.id); + } + + @Override + public int hashCode() { + return getClass().hashCode(); + } + + @Override + public int compareTo(Post o) { + if (getDate() == null || o.getDate() == null) + return 0; + return getDate().compareTo(o.getDate()); + } + +} \ No newline at end of file diff --git a/src/main/java/site/easystartup/web/post/domain/request/PostRequest.java b/src/main/java/site/easystartup/web/post/domain/request/PostRequest.java new file mode 100644 index 0000000..c557906 --- /dev/null +++ b/src/main/java/site/easystartup/web/post/domain/request/PostRequest.java @@ -0,0 +1,26 @@ +package site.easystartup.web.post.domain.request; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Builder; +import lombok.Data; + +import javax.persistence.*; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; +import java.util.Date; +import java.util.List; + +@Data +@Builder +public class PostRequest { + @Size(min = 3, max = 1024, message = "Text should be no less 3 and no more 1024 signs") + @NotNull + private String text; + private String sender; + private Date date; + private boolean edited; + private List liked_users; + private long like; + private List answers; + private Long answered_post; +} diff --git a/src/main/java/site/easystartup/web/post/dto/PostDto.java b/src/main/java/site/easystartup/web/post/dto/PostDto.java new file mode 100644 index 0000000..ab4ef90 --- /dev/null +++ b/src/main/java/site/easystartup/web/post/dto/PostDto.java @@ -0,0 +1,24 @@ +package site.easystartup.web.post.dto; + +import lombok.Data; + +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; +import java.io.Serializable; +import java.util.Date; +import java.util.List; + +@Data +public class PostDto implements Serializable { + private long id; + @Size(min = 3, max = 1024, message = "Text should be no less 3 and no more 1024 signs") + @NotNull + private String text; + private String sender; + private Date date; + private boolean edited; + private List liked_users; + private long like; + private List answers; + private long answered_post; +} diff --git a/src/main/java/site/easystartup/web/post/repo/PostRepo.java b/src/main/java/site/easystartup/web/post/repo/PostRepo.java new file mode 100644 index 0000000..4b7dea9 --- /dev/null +++ b/src/main/java/site/easystartup/web/post/repo/PostRepo.java @@ -0,0 +1,7 @@ +package site.easystartup.web.post.repo; + +import org.springframework.data.jpa.repository.JpaRepository; +import site.easystartup.web.post.domain.model.Post; + +public interface PostRepo extends JpaRepository { +} diff --git a/src/main/java/site/easystartup/web/post/service/PostService.java b/src/main/java/site/easystartup/web/post/service/PostService.java new file mode 100644 index 0000000..25ceb51 --- /dev/null +++ b/src/main/java/site/easystartup/web/post/service/PostService.java @@ -0,0 +1,87 @@ +package site.easystartup.web.post.service; + +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import site.easystartup.web.post.domain.event.RepostEvent; +import site.easystartup.web.post.domain.exception.AlreadyLikedException; +import site.easystartup.web.post.domain.exception.PostNotFoundException; +import site.easystartup.web.post.domain.model.Post; +import site.easystartup.web.post.domain.request.PostRequest; +import site.easystartup.web.post.repo.PostRepo; + +import java.security.Principal; +import java.util.*; + +@Component +@RequiredArgsConstructor(onConstructor_ = @Autowired) +public class PostService { + private final PostRepo postRepo; + + public Post getPostById(long id) { + return postRepo.findById(id).orElseThrow(new PostNotFoundException("Post with id " + id + " dont exists!")); + } + + public List getAllPostsByAuthor(String username) { + return postRepo.findAll().stream().filter(post -> post.getSender().equals(username)).toList(); + } + + public Post createPost(PostRequest postRequest, Principal principal) { + Post post = new Post(); + post.setAnswered_post(post.getAnswered_post()); + post.setAnswers(post.getAnswers()); + post.setDate(new Date()); + post.setEdited(false); + post.setLike(0); + post.setSender(principal.getName()); + post.setText(postRequest.getText()); + checkAnswers(post); + return postRepo.save(post); + } + + public Post edit(PostRequest postRequest, long post_id, Principal principal) { + Post post = postRepo.findById(post_id).orElseThrow( + new PostNotFoundException("Post with id " + post_id + " didnt exists")); + post.setEdited(true); + post.setSender(principal.getName()); + post.setText(postRequest.getText()); + checkAnswers(post); + return postRepo.save(post); + } + + public void delete(Post post) { + postRepo.delete(post); + } + + public void checkAnswers(Post post) { + if(post.getAnswered_post() != null) { + Post answered = postRepo.findById(post.getAnswered_post()).orElseThrow( + new PostNotFoundException("Post didnt exists: " + post.getAnswered_post())); + Set answers = answered.getAnswers() != null ? answered.getAnswers() : new TreeSet<>(); + answers.add(post.getId()); + answered.setAnswers(answers); + new RepostEvent(answered.getSender(), + post.getSender(), + String.format("Пользователь %s ответил на вашу запись!", answered.getSender())).invoke(); + postRepo.save(answered); + } + } + + public Post addLike(Post post, Principal principal) { + if (post.getLiked_users().contains(principal.getName())) + throw new AlreadyLikedException(String.format("User %s already liked post %s", principal.getName(), post.getId())); + post.setLike(post.getLike() + 1); + Set liked = post.getLiked_users() == null ? new TreeSet<>() : post.getLiked_users(); + liked.add(principal.getName()); + return postRepo.save(post); + } + + public Post disLike(Post post, Principal principal) { + if (!post.getLiked_users().contains(principal.getName())) + throw new AlreadyLikedException(String.format("User %s already liked post %s", principal.getName(), post.getId())); + post.setLike(post.getLike() - 1); + Set liked = post.getLiked_users() == null ? new TreeSet<>() : post.getLiked_users(); + liked.remove(principal.getName()); + return postRepo.save(post); + } +} diff --git a/src/main/java/site/easystartup/web/project/controller/ProjectController.java b/src/main/java/site/easystartup/web/project/controller/ProjectController.java index df6c2a6..7db124f 100644 --- a/src/main/java/site/easystartup/web/project/controller/ProjectController.java +++ b/src/main/java/site/easystartup/web/project/controller/ProjectController.java @@ -1,7 +1,5 @@ package site.easystartup.web.project.controller; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; import org.modelmapper.ModelMapper; import org.springframework.http.ResponseEntity; @@ -26,7 +24,6 @@ import java.util.stream.Collectors; @RestController @RequiredArgsConstructor @RequestMapping("/api/project") -@Tag(name = "Проекты", description = "контроллер для работы с проектами") public class ProjectController { private final ProjectService projectService; private final ResponseErrorValidation responseErrorValidation; @@ -35,7 +32,6 @@ public class ProjectController { private final ParticipantService participantService; @PostMapping - @Operation(summary = "Создание проекта") public ResponseEntity createProject(@Valid @RequestBody ProjectRequest projectRequest, BindingResult bindingResult, Principal principal) { @@ -47,7 +43,6 @@ public class ProjectController { } @PutMapping("/{projectId}") - @Operation(summary = "Изменение проекта") public ResponseEntity editProject(@Valid @RequestBody ProjectRequest projectRequest, @PathVariable("projectId") Long projectId, BindingResult bindingResult, @@ -60,7 +55,6 @@ public class ProjectController { } @PutMapping("/{projectId}/{partId}/part") - @Operation(summary = "Изменение позиции в проекте: Дизайнер/Ui designer") public ResponseEntity editPart(@Valid @RequestBody ParticipantDto participantDto, @PathVariable("projectId") Long projectId, @PathVariable("partId") Long partId, @@ -75,7 +69,6 @@ public class ProjectController { @DeleteMapping("/{projectId}") - @Operation(summary = "Удаление проекта") public ResponseEntity deleteProject(@PathVariable("projectId") Long projectId, Principal principal) { projectService.deleteProject(projectId, principal); @@ -83,14 +76,12 @@ public class ProjectController { } @GetMapping("/{projectId}") - @Operation(summary = "получение проекта по id") public ResponseEntity getProject(@PathVariable("projectId") Long projectId) { return ResponseEntity.ok().body(modelMapper.map(projectService.getProjectById(projectId), ProjectDto.class)); } // @GetMapping("/{userId}/all") - @Operation(summary = "Получение всех проектов для пользователя") public ResponseEntity> getAllProjectsForUser(@PathVariable("userId") Long userId) { List projects = projectService.getAllProjectsForUser(userId) .stream().map(project -> modelMapper.map(project, ProjectDto.class)).collect(Collectors.toList()); @@ -98,7 +89,6 @@ public class ProjectController { } @GetMapping("/{technology}/tech") - @Operation(summary = "Получение всех проектов с опредлененной технологией") public ResponseEntity> getAllProjectsWithTechnology(@PathVariable("technology") String technology) { List projects = projectService.getAllProjectsWithTechnology(technology) .stream().map(project -> modelMapper.map(project, ProjectDto.class)).collect(Collectors.toList()); @@ -106,7 +96,6 @@ public class ProjectController { } @GetMapping("/my") - @Operation(summary = "получение всех проектов для текущего пользователя") public ResponseEntity> getAllProjectsForCurrentUser(Principal principal) { List projects = projectService.getAllProjectsForCurrentUser(principal) .stream().map(project -> modelMapper.map(project, ProjectDto.class)).collect(Collectors.toList()); @@ -115,7 +104,6 @@ public class ProjectController { @GetMapping("/{nameOfPosition}/positions") - @Operation(summary = "Получение всех проектов с указанной позицией : Дизайнер") public ResponseEntity> getAllProjectsWithPosition(@PathVariable("nameOfPosition") String nameOfPosition) { List projects = projectService.getAllProjectsWithPosition(nameOfPosition) .stream().map(project -> modelMapper.map(project, ProjectDto.class)).collect(Collectors.toList()); @@ -123,7 +111,6 @@ public class ProjectController { } @GetMapping("/{commercialStatus}/commercial_status") - @Operation(summary = "получение всех проектов по коммерческому статусу") public ResponseEntity> getAllProjectsWithCommercialStatus(@PathVariable("commercialStatus") int commercialStatus) { List projects = projectService.getAllProjectsWithCommercialStatus(commercialStatus) .stream().map(project -> modelMapper.map(project, ProjectDto.class)).collect(Collectors.toList()); @@ -131,7 +118,6 @@ public class ProjectController { } @GetMapping("/requests") - @Operation(summary = "получение всех поданных заявок на проект") public ResponseEntity> getAllRequestsForProject(@RequestBody Long projectId) { List requests = projectService.getAllRequestsForProject(projectId) .stream().map(project -> modelMapper.map(project, ParticipantDto.class)).collect(Collectors.toList()); @@ -139,7 +125,6 @@ public class ProjectController { } @PostMapping("/apply") - @Operation(summary = "подать заявку на участие в проекте") public ResponseEntity applyOnProject(@RequestBody ParticipantRequest participantRequest, Principal principal) { Project project = projectService @@ -148,7 +133,6 @@ public class ProjectController { } @PostMapping("/participant") - @Operation(summary = "утвердить участница на проект") public ResponseEntity confirmParticipant(@RequestBody ParticipantRequest participantRequest, Principal principal) { Project project = projectService.confirmParticipant(participantRequest.getProjectId(), diff --git a/src/main/java/site/easystartup/web/project/dto/ProjectDto.java b/src/main/java/site/easystartup/web/project/dto/ProjectDto.java index 0385754..643f992 100644 --- a/src/main/java/site/easystartup/web/project/dto/ProjectDto.java +++ b/src/main/java/site/easystartup/web/project/dto/ProjectDto.java @@ -1,6 +1,5 @@ package site.easystartup.web.project.dto; -import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import lombok.NoArgsConstructor; diff --git a/src/main/java/site/easystartup/web/security/SecurityConfig.java b/src/main/java/site/easystartup/web/security/SecurityConfig.java index f28255c..ad10fe2 100644 --- a/src/main/java/site/easystartup/web/security/SecurityConfig.java +++ b/src/main/java/site/easystartup/web/security/SecurityConfig.java @@ -33,29 +33,18 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { -// http -// .authorizeRequests() -// .antMatchers("/swagger-ui.html").permitAll() -// .antMatchers("/swagger-ui/index.html").permitAll() -// .antMatchers("/webjars/**").permitAll() -// .antMatchers("/swagger-resources/**").permitAll() -// .antMatchers("/v3/*").permitAll() -// .antMatchers("/csrf").permitAll() -// .antMatchers("/").permitAll() -// .and() + http.cors().and().csrf().disable() + .exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint) + .and() + .sessionManagement() + .sessionCreationPolicy(SessionCreationPolicy.STATELESS) + .and() + .authorizeRequests() + .antMatchers(SecurityConstants.SIGN_UP_URLS).permitAll() + .antMatchers("/").permitAll() + .anyRequest().authenticated(); + http.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class); - -// .cors().and().csrf().disable() -// .exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint) -// .and() -// .sessionManagement() -// .sessionCreationPolicy(SessionCreationPolicy.STATELESS) -// .and() -// .authorizeRequests() -// .antMatchers(SecurityConstants.SIGN_UP_URLS).permitAll() -// .anyRequest().authenticated(); -// -// http.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class); } @Bean diff --git a/src/main/java/site/easystartup/web/service/AuthService.java b/src/main/java/site/easystartup/web/service/AuthService.java index 24f45df..9ffaec4 100644 --- a/src/main/java/site/easystartup/web/service/AuthService.java +++ b/src/main/java/site/easystartup/web/service/AuthService.java @@ -7,10 +7,8 @@ import site.easystartup.web.domain.exception.UserExistException; import site.easystartup.web.domain.model.Role; import site.easystartup.web.domain.model.User; import site.easystartup.web.domain.request.SignupRequest; -import site.easystartup.web.domain.request.UpdatePassportRequest; import site.easystartup.web.repo.UserRepo; -import java.security.Principal; import java.util.HashSet; import java.util.Set; @@ -18,7 +16,6 @@ import java.util.Set; @RequiredArgsConstructor public class AuthService { private final UserRepo userRepo; - private final UserService userService; private final PasswordEncoder passwordEncoder; public User createUser(SignupRequest signupRequest) { @@ -36,11 +33,4 @@ public class AuthService { return userRepo.save(user); } - - public User updatePassword(UpdatePassportRequest passportRequest, Principal principal) { - User user = userService.getUserByPrincipal(principal); - user.setPassword(passwordEncoder.encode(passportRequest.getPassword())); - - return userRepo.save(user); - } } diff --git a/src/main/resources/static/js/notification.js b/src/main/resources/static/js/notification.js index e69de29..6cfb415 100644 --- a/src/main/resources/static/js/notification.js +++ b/src/main/resources/static/js/notification.js @@ -0,0 +1,34 @@ +function connect() { + let socket = new SockJs(`api/socket`); + + let stompClient = Stomp.over(socket); + + return stompClient; +} + + +let stompClient = this.webSocketService.connect(); + +stompClient.connect({}, frame => { + + stompClient.subscribe('/user/queue/notification', notification => { + console.log('test'+ notification) + JSON.parse(notification.body); + }) +}); + + + const sendMessage = (msg) => { + if (msg.trim() !== "") { + const message = { + senderId: currentUser.id, + recipientId: activeContact.id, + senderName: currentUser.name, + recipientName: activeContact.name, + content: msg, + }; + + stompClient.send("/app/chat", {}, JSON.stringify(message)); + } + }; +