From 90df65f8f94518a9f1bb4cbf8bb3c4b8b0524f0e Mon Sep 17 00:00:00 2001 From: iRedTea Date: Wed, 31 Aug 2022 12:57:47 +0300 Subject: [PATCH] Try to add online chat --- build.gradle | 2 + src/main/java/site/easystartup/web/chat/a | 0 .../web/chat/config/ChatController.java | 28 -- .../web/chat/config/WebSocketConfig.java | 34 ++- .../web/chat/controller/ChatController.java | 38 +++ .../chat/controller/ChatRestController.java | 52 ++++ .../java/site/easystartup/web/chat/domain/a | 0 .../exception/ChatRoomNotFoundException.java | 14 + .../web/chat/domain/model/ChatMessage.java | 54 +++- .../chat/domain/model/ChatNotification.java | 5 +- .../web/chat/domain/model/ChatRoom.java | 47 +++- .../domain/request/ChatMessageRequest.java | 27 ++ .../web/chat/dto/ChatMessageDto.java | 28 ++ .../easystartup/web/chat/dto/ChatRoomDto.java | 11 + .../chat/event/WebSocketEventListener.java | 35 --- .../web/chat/repo/ChatMessageRepo.java | 8 + .../web/chat/repo/ChatRoomRepo.java | 8 + .../web/chat/service/ChatService.java | 101 +++++++ .../web/forum/controller/ForumController.java | 2 +- .../forum/domain/model/DiscussionMessage.java | 10 +- .../web/forum/dto/DiscussionDto.java | 3 +- .../web/forum/dto/DiscussionMessageDto.java | 3 +- .../easystartup/web/forum/dto/TopicDto.java | 3 +- .../web/forum/repo/DiscussionMessageRepo.java | 3 +- .../web/forum/repo/DiscussionRepo.java | 3 +- .../easystartup/web/forum/repo/TopicRepo.java | 3 +- .../web/forum/service/ForumService.java | 24 +- src/main/resources/static/css/main.css | 251 ------------------ .../resources/static/images/logo_black.png | Bin 18483 -> 0 bytes src/main/resources/static/index.html | 49 ---- src/main/resources/static/js/main.js | 118 ++------ 31 files changed, 472 insertions(+), 492 deletions(-) delete mode 100644 src/main/java/site/easystartup/web/chat/a delete mode 100644 src/main/java/site/easystartup/web/chat/config/ChatController.java create mode 100644 src/main/java/site/easystartup/web/chat/controller/ChatController.java create mode 100644 src/main/java/site/easystartup/web/chat/controller/ChatRestController.java delete mode 100644 src/main/java/site/easystartup/web/chat/domain/a create mode 100644 src/main/java/site/easystartup/web/chat/domain/exception/ChatRoomNotFoundException.java create mode 100644 src/main/java/site/easystartup/web/chat/domain/request/ChatMessageRequest.java create mode 100644 src/main/java/site/easystartup/web/chat/dto/ChatMessageDto.java create mode 100644 src/main/java/site/easystartup/web/chat/dto/ChatRoomDto.java delete mode 100644 src/main/java/site/easystartup/web/chat/event/WebSocketEventListener.java create mode 100644 src/main/java/site/easystartup/web/chat/repo/ChatMessageRepo.java create mode 100644 src/main/java/site/easystartup/web/chat/repo/ChatRoomRepo.java create mode 100644 src/main/java/site/easystartup/web/chat/service/ChatService.java delete mode 100644 src/main/resources/static/css/main.css delete mode 100644 src/main/resources/static/images/logo_black.png delete mode 100644 src/main/resources/static/index.html diff --git a/build.gradle b/build.gradle index 5a234fd..a793497 100644 --- a/build.gradle +++ b/build.gradle @@ -20,6 +20,8 @@ repositories { } dependencies { + implementation 'org.springframework:spring-websocket:5.3.22' + implementation 'org.springframework:spring-messaging:5.3.22' implementation 'org.modelmapper:modelmapper:3.1.0' implementation 'org.springframework.boot:spring-boot-starter-validation' implementation 'com.google.guava:guava:31.1-jre' diff --git a/src/main/java/site/easystartup/web/chat/a b/src/main/java/site/easystartup/web/chat/a deleted file mode 100644 index e69de29..0000000 diff --git a/src/main/java/site/easystartup/web/chat/config/ChatController.java b/src/main/java/site/easystartup/web/chat/config/ChatController.java deleted file mode 100644 index 79ee23d..0000000 --- a/src/main/java/site/easystartup/web/chat/config/ChatController.java +++ /dev/null @@ -1,28 +0,0 @@ -package site.easystartup.web.chat.config; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.messaging.handler.annotation.MessageMapping; -import org.springframework.messaging.handler.annotation.Payload; -import org.springframework.messaging.handler.annotation.SendTo; -import org.springframework.messaging.simp.SimpMessageHeaderAccessor; -import org.springframework.messaging.simp.SimpMessagingTemplate; -import org.springframework.stereotype.Controller; -import site.easystartup.web.chat.domain.model.ChatMessage; -import site.easystartup.web.chat.domain.model.ChatNotification; - -@Controller -public class ChatController { - @MessageMapping("/chat.sendMessage") - @SendTo("/topic/public") - public ChatMessage sendMessage(@Payload ChatMessage chatMessage) { - return chatMessage; - } - @MessageMapping("/chat.addUser") - @SendTo("/topic/public") - public ChatMessage addUser(@Payload ChatMessage chatMessage, - SimpMessageHeaderAccessor headerAccessor) { - // Add username in web socket session - headerAccessor.getSessionAttributes().put("username", chatMessage.getSender()); - return chatMessage; - } -} \ No newline at end of file diff --git a/src/main/java/site/easystartup/web/chat/config/WebSocketConfig.java b/src/main/java/site/easystartup/web/chat/config/WebSocketConfig.java index 34af5de..ea82539 100644 --- a/src/main/java/site/easystartup/web/chat/config/WebSocketConfig.java +++ b/src/main/java/site/easystartup/web/chat/config/WebSocketConfig.java @@ -1,22 +1,46 @@ package site.easystartup.web.chat.config; +import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.context.annotation.Configuration; +import org.springframework.messaging.converter.DefaultContentTypeResolver; +import org.springframework.messaging.converter.MappingJackson2MessageConverter; +import org.springframework.messaging.converter.MessageConverter; import org.springframework.messaging.simp.config.MessageBrokerRegistry; +import org.springframework.util.MimeTypeUtils; import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker; import org.springframework.web.socket.config.annotation.StompEndpointRegistry; import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer; +import java.util.List; @Configuration @EnableWebSocketMessageBroker public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { + + @Override + public void configureMessageBroker(MessageBrokerRegistry config) { + config.enableSimpleBroker( "/user"); + config.setApplicationDestinationPrefixes("/app"); + config.setUserDestinationPrefix("/user"); + } + @Override public void registerStompEndpoints(StompEndpointRegistry registry) { - registry.addEndpoint("/ws").withSockJS(); + registry + .addEndpoint("/ws") + .setAllowedOrigins("*") + .withSockJS(); } + @Override - public void configureMessageBroker(MessageBrokerRegistry registry) { - registry.setApplicationDestinationPrefixes("/app"); - registry.enableSimpleBroker("/topic"); + public boolean configureMessageConverters(List messageConverters) { + DefaultContentTypeResolver resolver = new DefaultContentTypeResolver(); + resolver.setDefaultMimeType(MimeTypeUtils.APPLICATION_JSON); + MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter(); + converter.setObjectMapper(new ObjectMapper()); + converter.setContentTypeResolver(resolver); + messageConverters.add(converter); + return false; } -} \ No newline at end of file +} + diff --git a/src/main/java/site/easystartup/web/chat/controller/ChatController.java b/src/main/java/site/easystartup/web/chat/controller/ChatController.java new file mode 100644 index 0000000..ff710ba --- /dev/null +++ b/src/main/java/site/easystartup/web/chat/controller/ChatController.java @@ -0,0 +1,38 @@ +package site.easystartup.web.chat.controller; + +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.messaging.handler.annotation.MessageMapping; +import org.springframework.messaging.handler.annotation.Payload; +import org.springframework.messaging.simp.SimpMessagingTemplate; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import site.easystartup.web.chat.domain.model.ChatMessage; +import site.easystartup.web.chat.domain.model.ChatNotification; +import site.easystartup.web.chat.domain.request.ChatMessageRequest; +import site.easystartup.web.chat.repo.ChatMessageRepo; +import site.easystartup.web.chat.service.ChatService; + +@Controller +@RequiredArgsConstructor(onConstructor_ = @Autowired) +public class ChatController { + private final SimpMessagingTemplate messagingTemplate; + private final ChatService chatService; + + @MessageMapping("/chat") + public void processMessage(@Payload ChatMessageRequest chatMessageRequest) { + ChatMessage chatMessage = chatService.getByRequest(chatMessageRequest); + var room = chatService + .getChatRoom(chatMessage.getRecipientName(), chatMessage.getRecipientId()); + chatMessage.setChatId(room.getChatId()); + + ChatMessage saved = chatService.save(chatMessage); + + messagingTemplate.convertAndSendToUser( + chatMessage.getRecipientId(),"/queue/messages", + new ChatNotification( + saved.getId(), + saved.getSenderId(), + saved.getSenderName())); + } +} \ No newline at end of file diff --git a/src/main/java/site/easystartup/web/chat/controller/ChatRestController.java b/src/main/java/site/easystartup/web/chat/controller/ChatRestController.java new file mode 100644 index 0000000..0a581e5 --- /dev/null +++ b/src/main/java/site/easystartup/web/chat/controller/ChatRestController.java @@ -0,0 +1,52 @@ +package site.easystartup.web.chat.controller; + +import lombok.RequiredArgsConstructor; +import lombok.val; +import org.modelmapper.ModelMapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import site.easystartup.web.chat.dto.ChatMessageDto; +import site.easystartup.web.chat.dto.ChatRoomDto; +import site.easystartup.web.chat.service.ChatService; +import site.easystartup.web.exception.NoPermissionException; +import site.easystartup.web.service.UserService; + +import java.security.Principal; +import java.util.List; + +@RestController +@RequestMapping("/chat") +@RequiredArgsConstructor(onConstructor_ = @Autowired) +public class ChatRestController { + private final ChatService chatService; + + private final ModelMapper modelMapper; + + private final UserService userService; + + @GetMapping("/{chat_id}") + public ResponseEntity> chatId(@PathVariable String chat_id, + Principal principal) { + var chatMessages = chatService.getByChatId(chat_id, principal) + .stream().map(message -> modelMapper.map(message, ChatMessageDto.class)).toList(); + return ResponseEntity.ok().body(chatMessages); + } + + @GetMapping("/see") + public ResponseEntity> chat(@RequestParam String sender, + @RequestParam String recipient, + Principal principal) { + var chatMessages = chatService.getChatMessages(sender, recipient, principal) + .stream().map(message -> modelMapper.map(message, ChatMessageDto.class)).toList(); + return ResponseEntity.ok().body(chatMessages); + } + + @GetMapping("/user/{username}") + public ResponseEntity> user(@PathVariable String username, + Principal principal) { + var chatMessages = chatService.getChats(username, principal) + .stream().map(message -> modelMapper.map(message, ChatRoomDto.class)).toList(); + return ResponseEntity.ok().body(chatMessages); + } +} diff --git a/src/main/java/site/easystartup/web/chat/domain/a b/src/main/java/site/easystartup/web/chat/domain/a deleted file mode 100644 index e69de29..0000000 diff --git a/src/main/java/site/easystartup/web/chat/domain/exception/ChatRoomNotFoundException.java b/src/main/java/site/easystartup/web/chat/domain/exception/ChatRoomNotFoundException.java new file mode 100644 index 0000000..20de663 --- /dev/null +++ b/src/main/java/site/easystartup/web/chat/domain/exception/ChatRoomNotFoundException.java @@ -0,0 +1,14 @@ +package site.easystartup.web.chat.domain.exception; + +import java.util.function.Supplier; + +public class ChatRoomNotFoundException extends RuntimeException implements Supplier { + public ChatRoomNotFoundException(String message) { + super(message); + } + + @Override + public ChatRoomNotFoundException get() { + return this; + } +} diff --git a/src/main/java/site/easystartup/web/chat/domain/model/ChatMessage.java b/src/main/java/site/easystartup/web/chat/domain/model/ChatMessage.java index a307a78..c19fdd8 100644 --- a/src/main/java/site/easystartup/web/chat/domain/model/ChatMessage.java +++ b/src/main/java/site/easystartup/web/chat/domain/model/ChatMessage.java @@ -1,15 +1,51 @@ package site.easystartup.web.chat.domain.model; -import lombok.Data; +import lombok.*; +import org.hibernate.Hibernate; +import site.easystartup.web.forum.domain.model.DiscussionMessage; -@Data -public class ChatMessage { - private MessageType type; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import java.util.Date; +import java.util.Objects; + +@Getter +@Setter +@ToString +@RequiredArgsConstructor +@Entity +public class ChatMessage implements Comparable { + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + private long id; + private String chatId; + private String senderId; + private String recipientId; + private String senderName; + private String recipientName; private String content; - private String sender; - public enum MessageType { - CHAT, - JOIN, - LEAVE + private Date timestamp; + private MessageStatus status; + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || Hibernate.getClass(this) != Hibernate.getClass(o)) return false; + ChatMessage that = (ChatMessage) o; + return Objects.equals(id, that.id); + } + + @Override + public int hashCode() { + return getClass().hashCode(); + } + + @Override + public int compareTo(ChatMessage o) { + if (getTimestamp() == null || o.getTimestamp() == null) + return 0; + return getTimestamp().compareTo(o.getTimestamp()); } } \ No newline at end of file diff --git a/src/main/java/site/easystartup/web/chat/domain/model/ChatNotification.java b/src/main/java/site/easystartup/web/chat/domain/model/ChatNotification.java index fa60b73..dd7d904 100644 --- a/src/main/java/site/easystartup/web/chat/domain/model/ChatNotification.java +++ b/src/main/java/site/easystartup/web/chat/domain/model/ChatNotification.java @@ -1,11 +1,10 @@ package site.easystartup.web.chat.domain.model; import lombok.AllArgsConstructor; -import lombok.Data; -@Data @AllArgsConstructor public class ChatNotification { - private String id; + private long id; + private String senderId; private String senderName; } \ No newline at end of file diff --git a/src/main/java/site/easystartup/web/chat/domain/model/ChatRoom.java b/src/main/java/site/easystartup/web/chat/domain/model/ChatRoom.java index 70e96b0..18b9f5e 100644 --- a/src/main/java/site/easystartup/web/chat/domain/model/ChatRoom.java +++ b/src/main/java/site/easystartup/web/chat/domain/model/ChatRoom.java @@ -1,11 +1,46 @@ package site.easystartup.web.chat.domain.model; -import lombok.Data; +import lombok.*; +import org.hibernate.Hibernate; -@Data -public class ChatRoom { - private String id; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import java.util.Date; +import java.util.Objects; + +@Getter +@Setter +@ToString +@RequiredArgsConstructor +@Entity +public class ChatRoom implements Comparable { + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + private long id; private String chatId; - private String senderId; - private String recipientId; + private String sender; + private String recipient; + private Date last_update; + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || Hibernate.getClass(this) != Hibernate.getClass(o)) return false; + ChatRoom chatRoom = (ChatRoom) o; + return Objects.equals(id, chatRoom.id); + } + + @Override + public int hashCode() { + return getClass().hashCode(); + } + + @Override + public int compareTo(ChatRoom o) { + if (getLast_update() == null || o.getLast_update() == null) + return 0; + return getLast_update().compareTo(o.getLast_update()); + } } \ No newline at end of file diff --git a/src/main/java/site/easystartup/web/chat/domain/request/ChatMessageRequest.java b/src/main/java/site/easystartup/web/chat/domain/request/ChatMessageRequest.java new file mode 100644 index 0000000..928243a --- /dev/null +++ b/src/main/java/site/easystartup/web/chat/domain/request/ChatMessageRequest.java @@ -0,0 +1,27 @@ +package site.easystartup.web.chat.domain.request; + +import lombok.Data; +import site.easystartup.web.chat.domain.model.MessageStatus; + +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; +import java.util.Date; + +@Data +public class ChatMessageRequest { + private long id; + private String chatId; + @NotNull + private String senderId; + @NotNull + private String recipientId; + @NotNull + private String senderName; + @NotNull + private String recipientName; + @NotNull + @Size(min = 1, max = 255, message = "Text should be no less 1 and no more 255 signs") + private String content; + private Date timestamp; + private MessageStatus status; +} diff --git a/src/main/java/site/easystartup/web/chat/dto/ChatMessageDto.java b/src/main/java/site/easystartup/web/chat/dto/ChatMessageDto.java new file mode 100644 index 0000000..f587a6b --- /dev/null +++ b/src/main/java/site/easystartup/web/chat/dto/ChatMessageDto.java @@ -0,0 +1,28 @@ +package site.easystartup.web.chat.dto; + +import lombok.Data; +import site.easystartup.web.chat.domain.model.MessageStatus; + +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; +import java.io.Serializable; +import java.util.Date; + +@Data +public class ChatMessageDto implements Serializable { + private long id; + private String chatId; + @NotNull + private String senderId; + @NotNull + private String recipientId; + @NotNull + private String senderName; + @NotNull + private String recipientName; + @NotNull + @Size(min = 1, max = 255, message = "Text should be no less 1 and no more 255 signs") + private String content; + private Date timestamp; + private MessageStatus status; +} diff --git a/src/main/java/site/easystartup/web/chat/dto/ChatRoomDto.java b/src/main/java/site/easystartup/web/chat/dto/ChatRoomDto.java new file mode 100644 index 0000000..272cd02 --- /dev/null +++ b/src/main/java/site/easystartup/web/chat/dto/ChatRoomDto.java @@ -0,0 +1,11 @@ +package site.easystartup.web.chat.dto; + +import lombok.Data; + +@Data +public class ChatRoomDto { + private long id; + private String chatId; + private String sender; + private String recipient; +} diff --git a/src/main/java/site/easystartup/web/chat/event/WebSocketEventListener.java b/src/main/java/site/easystartup/web/chat/event/WebSocketEventListener.java deleted file mode 100644 index 4823b97..0000000 --- a/src/main/java/site/easystartup/web/chat/event/WebSocketEventListener.java +++ /dev/null @@ -1,35 +0,0 @@ -package site.easystartup.web.chat.event; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.event.EventListener; -import org.springframework.messaging.simp.SimpMessageSendingOperations; -import org.springframework.messaging.simp.stomp.StompHeaderAccessor; -import org.springframework.stereotype.Component; -import org.springframework.web.socket.messaging.SessionConnectedEvent; -import org.springframework.web.socket.messaging.SessionDisconnectEvent; -import site.easystartup.web.chat.domain.model.ChatMessage; - -@Component -public class WebSocketEventListener { - private static final Logger logger = LoggerFactory.getLogger(WebSocketEventListener.class); - @Autowired - private SimpMessageSendingOperations messagingTemplate; - @EventListener - public void handleWebSocketConnectListener(SessionConnectedEvent event) { - logger.info("Received a new web socket connection"); - } - @EventListener - public void handleWebSocketDisconnectListener(SessionDisconnectEvent event) { - StompHeaderAccessor headerAccessor = StompHeaderAccessor.wrap(event.getMessage()); - String username = (String) headerAccessor.getSessionAttributes().get("username"); - if(username != null) { - logger.info("User Disconnected : " + username); - ChatMessage chatMessage = new ChatMessage(); - chatMessage.setType(ChatMessage.MessageType.LEAVE); - chatMessage.setSender(username); - messagingTemplate.convertAndSend("/topic/public", chatMessage); - } - } -} \ No newline at end of file diff --git a/src/main/java/site/easystartup/web/chat/repo/ChatMessageRepo.java b/src/main/java/site/easystartup/web/chat/repo/ChatMessageRepo.java new file mode 100644 index 0000000..886c513 --- /dev/null +++ b/src/main/java/site/easystartup/web/chat/repo/ChatMessageRepo.java @@ -0,0 +1,8 @@ +package site.easystartup.web.chat.repo; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.repository.CrudRepository; +import site.easystartup.web.chat.domain.model.ChatMessage; + +public interface ChatMessageRepo extends JpaRepository { +} diff --git a/src/main/java/site/easystartup/web/chat/repo/ChatRoomRepo.java b/src/main/java/site/easystartup/web/chat/repo/ChatRoomRepo.java new file mode 100644 index 0000000..a08c895 --- /dev/null +++ b/src/main/java/site/easystartup/web/chat/repo/ChatRoomRepo.java @@ -0,0 +1,8 @@ +package site.easystartup.web.chat.repo; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.repository.CrudRepository; +import site.easystartup.web.chat.domain.model.ChatRoom; + +public interface ChatRoomRepo extends JpaRepository { +} diff --git a/src/main/java/site/easystartup/web/chat/service/ChatService.java b/src/main/java/site/easystartup/web/chat/service/ChatService.java new file mode 100644 index 0000000..5658003 --- /dev/null +++ b/src/main/java/site/easystartup/web/chat/service/ChatService.java @@ -0,0 +1,101 @@ +package site.easystartup.web.chat.service; + +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; +import site.easystartup.web.chat.domain.exception.ChatRoomNotFoundException; +import site.easystartup.web.chat.domain.model.ChatMessage; +import site.easystartup.web.chat.domain.model.ChatRoom; +import site.easystartup.web.chat.domain.request.ChatMessageRequest; +import site.easystartup.web.chat.dto.ChatRoomDto; +import site.easystartup.web.chat.repo.ChatMessageRepo; +import site.easystartup.web.chat.repo.ChatRoomRepo; +import site.easystartup.web.exception.NoPermissionException; +import site.easystartup.web.service.UserService; + +import java.security.Principal; +import java.util.Date; +import java.util.List; +import java.util.UUID; + +@Component +@Scope("singleton") +@RequiredArgsConstructor(onConstructor_ = @Autowired) +public class ChatService { + private final ChatMessageRepo chatMessageRepo; + private final ChatRoomRepo chatRoomRepo; + private final UserService userService; + + public ChatRoom getChatRoom(String sender, String recipient) { + return chatRoomRepo.findAll().stream() + .filter(chatRoom -> chatRoom.getSender().equals(sender) + && chatRoom.getRecipient().equals(recipient)).findAny() + .orElse(createChatRoom(sender, recipient)); + } + + public List getChatMessages(String sender, String recipient, Principal principal) { + if(!getChatRoom(sender, recipient).getSender().equals(principal.getName()) + || !(userService.getUserByPrincipal(principal)).isAdmin()) + throw new NoPermissionException( + String.format("User %s dont have perms to see chat %s / %s", sender, recipient)); + + List result = new java.util.ArrayList<>(chatMessageRepo.findAll().stream().filter( + chatMessage -> chatMessage.getSenderName().equals(sender) && chatMessage.getSenderName().equals(recipient) + ).toList()); + result.sort(ChatMessage::compareTo); + return result; + } + + public List getChats(String sender, Principal principal) { + if(!principal.getName().equals(sender) + || !(userService.getUserByPrincipal(principal)).isAdmin()) + throw new NoPermissionException( + String.format("User %s dont have perms to see chats %s", sender)); + + List result = new java.util.ArrayList<>(chatRoomRepo.findAll().stream().filter( + chatRoom -> chatRoom.getSender().equals(sender) + ).toList()); + result.sort(ChatRoom::compareTo); + return result; + } + + public ChatRoom getChatRoom(String chat_id) { + return chatRoomRepo.findAll().stream() + .filter(chatRoom -> chatRoom.getChatId().equals(chat_id)).findAny() + .orElseThrow(new ChatRoomNotFoundException("Chat with id " + chat_id + " dont exists!")); + } + + public ChatMessage getByRequest(ChatMessageRequest chatMessageRequest) { + ChatMessage result = new ChatMessage(); + result.setContent(chatMessageRequest.getContent()); + result.setRecipientId(chatMessageRequest.getRecipientId()); + result.setRecipientName(chatMessageRequest.getRecipientName()); + result.setSenderId(chatMessageRequest.getSenderId()); + result.setSenderName(chatMessageRequest.getSenderName()); + result.setContent(chatMessageRequest.getContent()); + result.setTimestamp(chatMessageRequest.getTimestamp() != null ? chatMessageRequest.getTimestamp() : new Date()); + return result; + } + + public ChatMessage save(ChatMessage chatMessage) { + return chatMessageRepo.save(chatMessage); + } + + public List getByChatId(String chat_id, Principal principal) { + if(!getChatRoom(chat_id).getSender().equals(principal.getName()) + || !(userService.getUserByPrincipal(principal)).isAdmin()) + throw new NoPermissionException( + String.format("User %s dont have perms to see chat %s", principal.getName(), chat_id)); + + return chatMessageRepo.findAll().stream().filter(chatMessage -> chatMessage.getChatId().equals(chat_id)).toList(); + } + + public ChatRoom createChatRoom(String sender, String recipient) { + ChatRoom chatRoom = new ChatRoom(); + chatRoom.setSender(sender); + chatRoom.setRecipient(recipient); + chatRoom.setChatId(UUID.randomUUID().toString()); + return chatRoomRepo.save(chatRoom); + } +} diff --git a/src/main/java/site/easystartup/web/forum/controller/ForumController.java b/src/main/java/site/easystartup/web/forum/controller/ForumController.java index fec129f..4d17a36 100644 --- a/src/main/java/site/easystartup/web/forum/controller/ForumController.java +++ b/src/main/java/site/easystartup/web/forum/controller/ForumController.java @@ -139,7 +139,7 @@ public class ForumController { return ResponseEntity.ok().body(modelMapper.map(messageUpdated, DiscussionMessageDto.class)); } - @DeleteMapping("/forum/message/{message_id}/delete") + @DeleteMapping("/message/{message_id}/delete") public ResponseEntity messageDelete(@PathVariable long message_id, Principal principal) { DiscussionMessage message = forumService.getDiscussionMessageById(message_id); User user = userService.getUserByPrincipal(principal); diff --git a/src/main/java/site/easystartup/web/forum/domain/model/DiscussionMessage.java b/src/main/java/site/easystartup/web/forum/domain/model/DiscussionMessage.java index d04e4af..9c22fb6 100644 --- a/src/main/java/site/easystartup/web/forum/domain/model/DiscussionMessage.java +++ b/src/main/java/site/easystartup/web/forum/domain/model/DiscussionMessage.java @@ -13,7 +13,7 @@ import java.util.Objects; @ToString @RequiredArgsConstructor @Entity -public class DiscussionMessage { +public class DiscussionMessage implements Comparable{ @Id @GeneratedValue(strategy = GenerationType.AUTO) private long id; @@ -40,4 +40,12 @@ public class DiscussionMessage { public int hashCode() { return getClass().hashCode(); } + + @Override + public int compareTo(DiscussionMessage 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/forum/dto/DiscussionDto.java b/src/main/java/site/easystartup/web/forum/dto/DiscussionDto.java index 4e879b1..38fb834 100644 --- a/src/main/java/site/easystartup/web/forum/dto/DiscussionDto.java +++ b/src/main/java/site/easystartup/web/forum/dto/DiscussionDto.java @@ -5,11 +5,12 @@ import site.easystartup.web.forum.domain.model.DiscussionStatus; 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 DiscussionDto { +public class DiscussionDto implements Serializable { private long id; @Size(min = 3, max = 100, message = "Title should be no less 3 and no more 100 signs") @NotNull diff --git a/src/main/java/site/easystartup/web/forum/dto/DiscussionMessageDto.java b/src/main/java/site/easystartup/web/forum/dto/DiscussionMessageDto.java index ee217ad..caf2c45 100644 --- a/src/main/java/site/easystartup/web/forum/dto/DiscussionMessageDto.java +++ b/src/main/java/site/easystartup/web/forum/dto/DiscussionMessageDto.java @@ -4,10 +4,11 @@ import lombok.Data; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; +import java.io.Serializable; import java.util.Date; @Data -public class DiscussionMessageDto { +public class DiscussionMessageDto implements Serializable { private long id; @Size(min = 3, max = 1024, message = "Text should be no less 3 and no more 1024 signs") @NotNull diff --git a/src/main/java/site/easystartup/web/forum/dto/TopicDto.java b/src/main/java/site/easystartup/web/forum/dto/TopicDto.java index d98d3a0..dd6c4cb 100644 --- a/src/main/java/site/easystartup/web/forum/dto/TopicDto.java +++ b/src/main/java/site/easystartup/web/forum/dto/TopicDto.java @@ -4,10 +4,11 @@ import lombok.Data; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; +import java.io.Serializable; import java.util.Date; @Data -public class TopicDto { +public class TopicDto implements Serializable { private long id; @Size(min = 3, max = 100, message = "Title should be no less 3 and no more 100 signs") @NotNull diff --git a/src/main/java/site/easystartup/web/forum/repo/DiscussionMessageRepo.java b/src/main/java/site/easystartup/web/forum/repo/DiscussionMessageRepo.java index f62e058..1e24b56 100644 --- a/src/main/java/site/easystartup/web/forum/repo/DiscussionMessageRepo.java +++ b/src/main/java/site/easystartup/web/forum/repo/DiscussionMessageRepo.java @@ -1,7 +1,8 @@ package site.easystartup.web.forum.repo; +import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.repository.CrudRepository; import site.easystartup.web.forum.domain.model.DiscussionMessage; -public interface DiscussionMessageRepo extends CrudRepository { +public interface DiscussionMessageRepo extends JpaRepository { } diff --git a/src/main/java/site/easystartup/web/forum/repo/DiscussionRepo.java b/src/main/java/site/easystartup/web/forum/repo/DiscussionRepo.java index a036586..5025b09 100644 --- a/src/main/java/site/easystartup/web/forum/repo/DiscussionRepo.java +++ b/src/main/java/site/easystartup/web/forum/repo/DiscussionRepo.java @@ -1,7 +1,8 @@ package site.easystartup.web.forum.repo; +import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.repository.CrudRepository; import site.easystartup.web.forum.domain.model.Discussion; -public interface DiscussionRepo extends CrudRepository { +public interface DiscussionRepo extends JpaRepository { } diff --git a/src/main/java/site/easystartup/web/forum/repo/TopicRepo.java b/src/main/java/site/easystartup/web/forum/repo/TopicRepo.java index 35b91ce..08c49d4 100644 --- a/src/main/java/site/easystartup/web/forum/repo/TopicRepo.java +++ b/src/main/java/site/easystartup/web/forum/repo/TopicRepo.java @@ -1,7 +1,8 @@ package site.easystartup.web.forum.repo; +import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.repository.CrudRepository; import site.easystartup.web.forum.domain.model.Topic; -public interface TopicRepo extends CrudRepository { +public interface TopicRepo extends JpaRepository { } diff --git a/src/main/java/site/easystartup/web/forum/service/ForumService.java b/src/main/java/site/easystartup/web/forum/service/ForumService.java index d454804..8e020ea 100644 --- a/src/main/java/site/easystartup/web/forum/service/ForumService.java +++ b/src/main/java/site/easystartup/web/forum/service/ForumService.java @@ -45,8 +45,10 @@ public class ForumService { } public List getMessagesByDiscussionId(long discussionId) { - return Lists.newArrayList(discussionMessageRepo.findAll()).stream() - .filter(m -> m.getDiscussion_id() == discussionId).toList(); + List result = new ArrayList(Lists.newArrayList(discussionMessageRepo.findAll()).stream() + .filter(m -> m.getDiscussion_id() == discussionId).toList()); + result.sort(DiscussionMessage::compareTo); + return result; } public Discussion getDiscussionById(long discussion_id) { @@ -55,14 +57,18 @@ public class ForumService { } public List getDiscussionsByAuthor(Principal principal) { - return Lists.newArrayList(discussionRepo.findAll()).stream() - .filter(d -> d.getAuthor().equals(principal.getName())).toList(); + List result = new ArrayList<>(Lists.newArrayList(discussionRepo.findAll()).stream() + .filter(d -> d.getAuthor().equals(principal.getName())).toList()); + result.sort(Discussion::compareTo); + return result; } public List getDiscussionsByMember(Principal principal) { - return Lists.newArrayList(discussionRepo.findAll()).stream() + List result = new ArrayList<>(Lists.newArrayList(discussionRepo.findAll()).stream() .filter(d -> getMessagesByDiscussionId(d.getId()).stream() - .anyMatch(m -> m.getSender().equals(principal.getName()))).toList(); + .anyMatch(m -> m.getSender().equals(principal.getName()))).toList()); + result.sort(Discussion::compareTo); + return result; } public List getPinnedDiscussionsByTopicId(long topic_id) { @@ -72,10 +78,10 @@ public class ForumService { } public List getSortedDiscussionsByTopicId(long topic_id) { - List result = Lists.newArrayList(discussionRepo.findAll()).stream() + List result = new ArrayList<>(Lists.newArrayList(discussionRepo.findAll()).stream() .filter(d -> !d.isPinned()) - .filter(d -> d.getTopic() == topic_id).toList(); - Collections.sort(result, Discussion::compareTo); + .filter(d -> d.getTopic() == topic_id).toList()); + result.sort(Discussion::compareTo); return result; } diff --git a/src/main/resources/static/css/main.css b/src/main/resources/static/css/main.css deleted file mode 100644 index 345f48a..0000000 --- a/src/main/resources/static/css/main.css +++ /dev/null @@ -1,251 +0,0 @@ -* { - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; -} -html,body { - height: 100%; - overflow: hidden; -} -body { - margin: 0; - padding: 0; - font-weight: 400; - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; - font-size: 1rem; - line-height: 1.58; - color: #333; - background-color: #f4f4f4; - height: 100%; -} -body:before { - height: 50%; - width: 100%; - position: absolute; - top: 0; - left: 0; - background: #128ff2; - content: ""; - z-index: 0; -} -.clearfix:after { - display: block; - content: ""; - clear: both; -} -.hidden { - display: none; -} -.form-control { - width: 100%; - min-height: 38px; - font-size: 15px; - border: 1px solid #c8c8c8; -} -.form-group { - margin-bottom: 15px; -} -input { - padding-left: 10px; - outline: none; -} -h1, h2, h3, h4, h5, h6 { - margin-top: 20px; - margin-bottom: 20px; -} -h1 { - font-size: 1.7em; -} -a { - color: #128ff2; -} -button { - box-shadow: none; - border: 1px solid transparent; - font-size: 14px; - outline: none; - line-height: 100%; - white-space: nowrap; - vertical-align: middle; - padding: 0.6rem 1rem; - border-radius: 2px; - transition: all 0.2s ease-in-out; - cursor: pointer; - min-height: 38px; -} -button.default { - background-color: #e8e8e8; - color: #333; - box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.12); -} -button.primary { - background-color: #128ff2; - box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.12); - color: #fff; -} -button.accent { - background-color: #ff4743; - box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.12); - color: #fff; -} -#username-page { - text-align: center; -} -.username-page-container { - background: #fff; - box-shadow: 0 1px 11px rgba(0, 0, 0, 0.27); - border-radius: 2px; - width: 100%; - max-width: 500px; - display: inline-block; - margin-top: 42px; - vertical-align: middle; - position: relative; - padding: 35px 55px 35px; - min-height: 250px; - position: absolute; - top: 50%; - left: 0; - right: 0; - margin: 0 auto; - margin-top: -160px; -} -.username-page-container .username-submit { - margin-top: 10px; -} -#chat-page { - position: relative; - height: 100%; -} -.chat-container { - max-width: 700px; - margin-left: auto; - margin-right: auto; - background-color: #fff; - box-shadow: 0 1px 11px rgba(0, 0, 0, 0.27); - margin-top: 30px; - height: calc(100% - 60px); - max-height: 600px; - position: relative; -} -#chat-page ul { - list-style-type: none; - background-color: #FFF; - margin: 0; - overflow: auto; - overflow-y: scroll; - padding: 0 20px 0px 20px; - height: calc(100% - 150px); -} -#chat-page #messageForm { - padding: 20px; -} -#chat-page ul li { - line-height: 1.5rem; - padding: 10px 20px; - margin: 0; - border-bottom: 1px solid #f4f4f4; -} -#chat-page ul li p { - margin: 0; -} -#chat-page .event-message { - width: 100%; - text-align: center; - clear: both; -} -#chat-page .event-message p { - color: #777; - font-size: 14px; - word-wrap: break-word; -} -#chat-page .chat-message { - padding-left: 68px; - position: relative; -} -#chat-page .chat-message i { - position: absolute; - width: 42px; - height: 42px; - overflow: hidden; - left: 10px; - display: inline-block; - vertical-align: middle; - font-size: 18px; - line-height: 42px; - color: #fff; - text-align: center; - border-radius: 50%; - font-style: normal; - text-transform: uppercase; -} -#chat-page .chat-message span { - color: #333; - font-weight: 600; -} -#chat-page .chat-message p { - color: #43464b; -} -#messageForm .input-group input { - float: left; - width: calc(100% - 85px); -} -#messageForm .input-group button { - float: left; - width: 80px; - height: 38px; - margin-left: 5px; -} -.chat-header { - text-align: center; - padding: 15px; - border-bottom: 1px solid #ececec; -} -.chat-header h2 { - margin: 0; - font-weight: 500; -} -.connecting { - padding-top: 5px; - text-align: center; - color: #777; - position: absolute; - top: 65px; - width: 100%; -} -@media screen and (max-width: 730px) { - .chat-container { - margin-left: 10px; - margin-right: 10px; - margin-top: 10px; - } -} -@media screen and (max-width: 480px) { - .chat-container { - height: calc(100% - 30px); - } - .username-page-container { - width: auto; - margin-left: 15px; - margin-right: 15px; - padding: 25px; - } - #chat-page ul { - height: calc(100% - 120px); - } - #messageForm .input-group button { - width: 65px; - } - #messageForm .input-group input { - width: calc(100% - 70px); - } - .chat-header { - padding: 10px; - } - .connecting { - top: 60px; - } - .chat-header h2 { - font-size: 1.1em; - } -} \ No newline at end of file diff --git a/src/main/resources/static/images/logo_black.png b/src/main/resources/static/images/logo_black.png deleted file mode 100644 index 01e76af68753298f4ca6c7cd4b7c338fe0650953..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18483 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 diff --git a/src/main/resources/static/index.html b/src/main/resources/static/index.html deleted file mode 100644 index 694e4ea..0000000 --- a/src/main/resources/static/index.html +++ /dev/null @@ -1,49 +0,0 @@ - - - - - Spring Boot WebSocket Chat Application - - - - -
-
-

Type your username

-
-
- -
-
- -
-
-
-
- - - - - - \ No newline at end of file diff --git a/src/main/resources/static/js/main.js b/src/main/resources/static/js/main.js index f86bada..be49c38 100644 --- a/src/main/resources/static/js/main.js +++ b/src/main/resources/static/js/main.js @@ -1,90 +1,30 @@ -'use strict'; -var usernamePage = document.querySelector('#username-page'); -var chatPage = document.querySelector('#chat-page'); -var usernameForm = document.querySelector('#usernameForm'); -var messageForm = document.querySelector('#messageForm'); -var messageInput = document.querySelector('#message'); -var messageArea = document.querySelector('#messageArea'); -var connectingElement = document.querySelector('.connecting'); -var stompClient = null; -var username = null; -var colors = [ - '#2196F3', '#32c787', '#00BCD4', '#ff5652', - '#ffc107', '#ff85af', '#FF9800', '#39bbb0' -]; -function connect(event) { - username = document.querySelector('#name').value.trim(); - if(username) { - usernamePage.classList.add('hidden'); - chatPage.classList.remove('hidden'); - var socket = new SockJS('/ws'); - stompClient = Stomp.over(socket); - stompClient.connect({}, onConnected, onError); +const connect = () => { + const Stomp = require("stompjs"); + var SockJS = require("sockjs-client"); + SockJS = new SockJS("http://localhost:8080/ws"); + stompClient = Stomp.over(SockJS); + stompClient.connect({}, onConnected, onError); + }; + + const onConnected = () => { + console.log("connected"); + + stompClient.subscribe( + "/user/" + currentUser.id + "/queue/messages", + onMessageReceived + ); + }; + + 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)); } - event.preventDefault(); -} -function onConnected() { - // Subscribe to the Public Topic - stompClient.subscribe('/topic/public', onMessageReceived); - // Tell your username to the server - stompClient.send("/app/chat.addUser", - {}, - JSON.stringify({sender: username, type: 'JOIN'}) - ) - connectingElement.classList.add('hidden'); -} -function onError(error) { - connectingElement.textContent = 'Could not connect to WebSocket server. Please refresh this page to try again!'; - connectingElement.style.color = 'red'; -} -function sendMessage(event) { - var messageContent = messageInput.value.trim(); - if(messageContent && stompClient) { - var chatMessage = { - sender: username, - content: messageInput.value, - type: 'CHAT' - }; - stompClient.send("/app/chat.sendMessage", {}, JSON.stringify(chatMessage)); - messageInput.value = ''; - } - event.preventDefault(); -} -function onMessageReceived(payload) { - var message = JSON.parse(payload.body); - var messageElement = document.createElement('li'); - if(message.type === 'JOIN') { - messageElement.classList.add('event-message'); - message.content = message.sender + ' joined!'; - } else if (message.type === 'LEAVE') { - messageElement.classList.add('event-message'); - message.content = message.sender + ' left!'; - } else { - messageElement.classList.add('chat-message'); - var avatarElement = document.createElement('i'); - var avatarText = document.createTextNode(message.sender[0]); - avatarElement.appendChild(avatarText); - avatarElement.style['background-color'] = getAvatarColor(message.sender); - messageElement.appendChild(avatarElement); - var usernameElement = document.createElement('span'); - var usernameText = document.createTextNode(message.sender); - usernameElement.appendChild(usernameText); - messageElement.appendChild(usernameElement); - } - var textElement = document.createElement('p'); - var messageText = document.createTextNode(message.content); - textElement.appendChild(messageText); - messageElement.appendChild(textElement); - messageArea.appendChild(messageElement); - messageArea.scrollTop = messageArea.scrollHeight; -} -function getAvatarColor(messageSender) { - var hash = 0; - for (var i = 0; i < messageSender.length; i++) { - hash = 31 * hash + messageSender.charCodeAt(i); - } - var index = Math.abs(hash % colors.length); - return colors[index]; -} -usernameForm.addEventListener('submit', connect, true) -messageForm.addEventListener('submit', sendMessage, true) \ No newline at end of file + };