Compare commits
merge into: nova:staging
nova:development
nova:master
nova:staging
nova:websocket
pull from: nova:websocket
nova:development
nova:master
nova:staging
nova:websocket
7 Commits
Author | SHA1 | Message | Date |
---|---|---|---|
Rohan Sircar | de2ad81f8e |
Improve frontend api part 1
|
4 years ago |
Rohan Sircar | e4041f9e41 |
Updated websocket ts code
ws factory now creates new object on each invocation instead of reusing |
4 years ago |
Rohan Sircar | 00a01bc81b |
Organized imports
|
4 years ago |
Rohan Sircar | 2b3d6009f1 |
First integration of websocket into chat
Added sockjs and stompjs via yarn Added websocket code to chat code New endpoint for chat New endpoint for ping |
4 years ago |
Rohan Sircar | 21fa7b5e64 |
Added new websocket endpoint
|
4 years ago |
Rohan Sircar | 5f01174764 |
Adjusted websocket deps
Added to bundle with yarn Added cdn links to head fragment |
4 years ago |
Rohan Sircar | 56fd5e8c89 |
initial websocket commit
|
4 years ago |
22 changed files with 619 additions and 354 deletions
-
8package.json
-
20pom.xml
-
56src/main/frontend/chat/component/ChatComponent.ts
-
67src/main/frontend/chat/controller/ChatController.ts
-
64src/main/frontend/chat/controller/UserController.ts
-
52src/main/frontend/chat/main.ts
-
60src/main/frontend/chat/model/ChatModel.ts
-
8src/main/frontend/chat/model/ChatModelHelper.ts
-
89src/main/frontend/chat/model/UserModel.ts
-
45src/main/frontend/chat/view/ChatView.ts
-
20src/main/frontend/chat/view/ChatViewDeps.ts
-
106src/main/frontend/chat/view/UserView.ts
-
28src/main/frontend/common/ajax/Messages.ts
-
19src/main/frontend/common/ajax/Users.ts
-
10src/main/frontend/common/dto/ChatMessageDTO.ts
-
50src/main/frontend/common/dto/MessageCipherDTO.ts
-
12src/main/java/org/ros/chatto/websocket/Message.java
-
26src/main/java/org/ros/chatto/websocket/WebSocketConfig.java
-
39src/main/java/org/ros/chatto/websocket/WebSocketController.java
-
7src/main/resources/templates/fragments/head.html
-
83src/main/resources/templates/ws.html
-
104yarn.lock
@ -0,0 +1,56 @@ |
|||||
|
import { UserModel } from "../model/UserModel"; |
||||
|
import { ChatModel } from "../model/ChatModel"; |
||||
|
import { UserView } from "../view/UserView"; |
||||
|
import { ChatView } from "../view/ChatView"; |
||||
|
import { UserController } from "../controller/UserController"; |
||||
|
import { ChatController } from "../controller/ChatController"; |
||||
|
import { Client } from "@stomp/stompjs"; |
||||
|
import { ChatMessageDTO } from "../../common/dto/ChatMessageDTO"; |
||||
|
import log from "loglevel"; |
||||
|
import { ChatMessageViewModel } from "../viewmodel/ChatMessageViewModel"; |
||||
|
|
||||
|
export interface Props { |
||||
|
// userModel: UserModel;
|
||||
|
// chatModel: ChatModel;
|
||||
|
// userView: UserView;
|
||||
|
// chatView: ChatView;
|
||||
|
userController: UserController; |
||||
|
chatController: ChatController; |
||||
|
chatStompClient: Client; |
||||
|
} |
||||
|
|
||||
|
export interface State {} |
||||
|
|
||||
|
export class ChatComponent { |
||||
|
private readonly _props: Props; |
||||
|
private readonly _state: State; |
||||
|
|
||||
|
constructor(props: Props, state: State) { |
||||
|
this._props = props; |
||||
|
this._state = state; |
||||
|
} |
||||
|
|
||||
|
private async wsHandler() { |
||||
|
this._props.chatStompClient.onConnect = () => { |
||||
|
this._props.chatStompClient.subscribe("/user/queue/reply", (reply) => { |
||||
|
const data = JSON.parse(reply.body) as ChatMessageDTO; |
||||
|
const plainText = this._props.userController.decryptForUser( |
||||
|
data, |
||||
|
data.fromUser |
||||
|
); |
||||
|
this._props.chatController.store(data.fromUser, <ChatMessageViewModel>{ |
||||
|
...data, |
||||
|
message: plainText, |
||||
|
messageTime: new Date(data.messageTime), |
||||
|
}); |
||||
|
// log.debug(
|
||||
|
// message,
|
||||
|
// this._props.encryptionService.decrypt(
|
||||
|
// "password",
|
||||
|
// message.messageCipher
|
||||
|
// )
|
||||
|
// );
|
||||
|
}); |
||||
|
}; |
||||
|
} |
||||
|
} |
@ -1,41 +1,42 @@ |
|||||
import { Controller } from "./AbstractController"; |
import { Controller } from "./AbstractController"; |
||||
import "../model/AbstractModel" |
|
||||
import "../model/UserModel" |
|
||||
import "../view/AbstractView" |
|
||||
import "../view/UserView" |
|
||||
|
import "../model/AbstractModel"; |
||||
|
import "../model/UserModel"; |
||||
|
import "../view/AbstractView"; |
||||
|
import "../view/UserView"; |
||||
import { Model } from "../model/AbstractModel"; |
import { Model } from "../model/AbstractModel"; |
||||
import { View } from "../view/AbstractView"; |
import { View } from "../view/AbstractView"; |
||||
import { ChatMessageViewModel } from "../viewmodel/ChatMessageViewModel"; |
import { ChatMessageViewModel } from "../viewmodel/ChatMessageViewModel"; |
||||
import { ChatModel } from "../model/ChatModel"; |
import { ChatModel } from "../model/ChatModel"; |
||||
import { ChatView } from "../view/ChatView"; |
import { ChatView } from "../view/ChatView"; |
||||
|
import { Client } from "@stomp/stompjs"; |
||||
|
import { EncryptionService } from "../../common/service/EncryptionService"; |
||||
|
import { ChatMessageDTO } from "../../common/dto/ChatMessageDTO"; |
||||
|
import log from "loglevel"; |
||||
|
|
||||
|
export interface Props { |
||||
|
model: ChatModel; |
||||
|
view: ChatView; |
||||
|
chatStompClient: Client; |
||||
|
encryptionService: EncryptionService; |
||||
|
} |
||||
|
|
||||
export class ChatController { |
export class ChatController { |
||||
private _model: ChatModel; |
|
||||
private _view: ChatView; |
|
||||
|
|
||||
|
|
||||
constructor(model: ChatModel, view: ChatView) { |
|
||||
this._model = model; |
|
||||
this._view = view; |
|
||||
} |
|
||||
|
|
||||
|
|
||||
/** |
|
||||
* eventHandler |
|
||||
*/ |
|
||||
public eventHandler(vm: ChatMessageViewModel[]): void { |
|
||||
this._model.someBusinessMethod(vm); |
|
||||
} |
|
||||
|
|
||||
public test(): void { |
|
||||
const chatMessageViewModels: ChatMessageViewModel[] = []; |
|
||||
let chatMessageViewModelMock = new ChatMessageViewModel(); |
|
||||
chatMessageViewModelMock.fromUser = "user1"; |
|
||||
chatMessageViewModelMock.toUser = "user2"; |
|
||||
chatMessageViewModelMock.message = ""; |
|
||||
chatMessageViewModelMock.messageTime = new Date(); |
|
||||
chatMessageViewModels.push(chatMessageViewModelMock); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
} |
|
||||
|
private readonly _props: Props; |
||||
|
constructor(props: Props) { |
||||
|
this._props = props; |
||||
|
} |
||||
|
|
||||
|
public test(): void { |
||||
|
const chatMessageViewModels: ChatMessageViewModel[] = []; |
||||
|
let chatMessageViewModelMock = new ChatMessageViewModel(); |
||||
|
chatMessageViewModelMock.fromUser = "user1"; |
||||
|
chatMessageViewModelMock.toUser = "user2"; |
||||
|
chatMessageViewModelMock.message = ""; |
||||
|
chatMessageViewModelMock.messageTime = new Date(); |
||||
|
chatMessageViewModels.push(chatMessageViewModelMock); |
||||
|
} |
||||
|
|
||||
|
public store(userName: string, message: ChatMessageViewModel): void { |
||||
|
this._props.model.storeUserMessages(userName, Array(message), "new"); |
||||
|
} |
||||
|
} |
@ -1,50 +1,30 @@ |
|||||
import { Controller } from "./AbstractController"; |
import { Controller } from "./AbstractController"; |
||||
import "../model/AbstractModel" |
|
||||
import "../model/UserModel" |
|
||||
import "../view/AbstractView" |
|
||||
import "../view/UserView" |
|
||||
|
import "../model/AbstractModel"; |
||||
|
import "../model/UserModel"; |
||||
|
import "../view/AbstractView"; |
||||
|
import "../view/UserView"; |
||||
import { Model } from "../model/AbstractModel"; |
import { Model } from "../model/AbstractModel"; |
||||
import { View } from "../view/AbstractView"; |
import { View } from "../view/AbstractView"; |
||||
import { ActiveUserViewModel } from "../viewmodel/ActiveUserViewModel"; |
import { ActiveUserViewModel } from "../viewmodel/ActiveUserViewModel"; |
||||
import { UserView } from "../view/UserView"; |
import { UserView } from "../view/UserView"; |
||||
import { UserModel } from "../model/UserModel"; |
import { UserModel } from "../model/UserModel"; |
||||
|
import { ChatMessageDTO } from "../../common/dto/ChatMessageDTO"; |
||||
|
import { ChatMessageViewModel } from "../viewmodel/ChatMessageViewModel"; |
||||
|
|
||||
export class UserController { |
export class UserController { |
||||
private _model: UserModel; |
|
||||
private _view: UserView; |
|
||||
|
|
||||
|
|
||||
constructor(model: UserModel, view: UserView) { |
|
||||
this._model = model; |
|
||||
this._view = view; |
|
||||
} |
|
||||
|
|
||||
|
|
||||
/** |
|
||||
* eventHandler |
|
||||
*/ |
|
||||
public eventHandler(vm: ActiveUserViewModel[]): void { |
|
||||
this._model.someBusinessMethod(vm); |
|
||||
} |
|
||||
|
|
||||
public test(): void { |
|
||||
const activeUsersMock: ActiveUserViewModel[] = []; |
|
||||
let activeUserViewModelMock = new ActiveUserViewModel(); |
|
||||
activeUserViewModelMock.userName = "some user"; |
|
||||
// activeUserViewModelMock.lastActive = "3 hrs ago";
|
|
||||
activeUserViewModelMock.online = true; |
|
||||
activeUsersMock.push(activeUserViewModelMock); |
|
||||
activeUserViewModelMock = new ActiveUserViewModel(); |
|
||||
// activeUserViewModelMock.lastActive = "3 hrs ago";
|
|
||||
activeUserViewModelMock.online = true; |
|
||||
activeUserViewModelMock.userName = "some user 2"; |
|
||||
activeUsersMock.push(activeUserViewModelMock); |
|
||||
this.eventHandler(activeUsersMock); |
|
||||
} |
|
||||
|
|
||||
public getActiveUsers(): void { |
|
||||
this._model.getActiveUsers(); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
} |
|
||||
|
private _model: UserModel; |
||||
|
private _view: UserView; |
||||
|
|
||||
|
constructor(model: UserModel, view: UserView) { |
||||
|
this._model = model; |
||||
|
this._view = view; |
||||
|
} |
||||
|
|
||||
|
// public getActiveUsers(): void {
|
||||
|
// this._model.getActiveUsers();
|
||||
|
// }
|
||||
|
|
||||
|
decryptForUser(dto: ChatMessageDTO, userName: string): string { |
||||
|
return this._model.decryptForUser(dto, userName); |
||||
|
} |
||||
|
} |
@ -1,8 +1,8 @@ |
|||||
import { MessageCipherDTO } from "./MessageCipherDTO"; |
import { MessageCipherDTO } from "./MessageCipherDTO"; |
||||
|
|
||||
export class ChatMessageDTO { |
|
||||
public toUser: string = ""; |
|
||||
public fromUser: string = ""; |
|
||||
public messageCipher!: MessageCipherDTO; |
|
||||
public messageTime: Date = new Date(); |
|
||||
|
export interface ChatMessageDTO { |
||||
|
toUser: string; |
||||
|
fromUser: string; |
||||
|
messageCipher: MessageCipherDTO; |
||||
|
messageTime: string; |
||||
} |
} |
@ -1,40 +1,12 @@ |
|||||
// export class MessageCipherDTO {
|
|
||||
// // iv!: string;
|
|
||||
// // v!: number;
|
|
||||
// // iterations!: number;
|
|
||||
// // keySize!: number;
|
|
||||
// // tagSize!: number;
|
|
||||
// // mode!: string;
|
|
||||
// // adata!: string;
|
|
||||
// // cipher!: string;
|
|
||||
// // salt!: string;
|
|
||||
// // cipherText!: string;
|
|
||||
|
|
||||
// iv!: string;
|
|
||||
// v!: number;
|
|
||||
// iter!: number;
|
|
||||
// ks!: number;
|
|
||||
// ts!: number;
|
|
||||
// mode!: string;
|
|
||||
// adata!: string;
|
|
||||
// cipher!: string;
|
|
||||
// salt!: string;
|
|
||||
// ct!: string;
|
|
||||
|
|
||||
// // public toMessageCipherDTO {
|
|
||||
|
|
||||
// // }
|
|
||||
// }
|
|
||||
|
|
||||
export interface MessageCipherDTO { |
export interface MessageCipherDTO { |
||||
iv: string, |
|
||||
v: number, |
|
||||
iter: number, |
|
||||
ks: number, |
|
||||
ts: number, |
|
||||
mode: string, |
|
||||
adata: string, |
|
||||
cipher: string, |
|
||||
salt: string, |
|
||||
ct: string, |
|
||||
} |
|
||||
|
iv: string; |
||||
|
v: number; |
||||
|
iter: number; |
||||
|
ks: number; |
||||
|
ts: number; |
||||
|
mode: string; |
||||
|
adata: string; |
||||
|
cipher: string; |
||||
|
salt: string; |
||||
|
ct: string; |
||||
|
} |
@ -0,0 +1,12 @@ |
|||||
|
package org.ros.chatto.websocket; |
||||
|
|
||||
|
import lombok.Builder; |
||||
|
import lombok.Data; |
||||
|
|
||||
|
@Data |
||||
|
@Builder |
||||
|
public class Message { |
||||
|
private String from; |
||||
|
private String to; |
||||
|
private String text; |
||||
|
} |
@ -0,0 +1,26 @@ |
|||||
|
package org.ros.chatto.websocket; |
||||
|
|
||||
|
import org.springframework.context.annotation.Configuration; |
||||
|
import org.springframework.messaging.simp.config.MessageBrokerRegistry; |
||||
|
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker; |
||||
|
import org.springframework.web.socket.config.annotation.StompEndpointRegistry; |
||||
|
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer; |
||||
|
|
||||
|
@Configuration |
||||
|
@EnableWebSocketMessageBroker |
||||
|
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { |
||||
|
|
||||
|
@Override |
||||
|
public void configureMessageBroker(MessageBrokerRegistry config) { |
||||
|
config.enableSimpleBroker("/topic", "/queue"); |
||||
|
config.setApplicationDestinationPrefixes("/app"); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void registerStompEndpoints(StompEndpointRegistry registry) { |
||||
|
registry.addEndpoint("/ping"); |
||||
|
registry.addEndpoint("/ping").withSockJS(); |
||||
|
registry.addEndpoint("/chat"); |
||||
|
registry.addEndpoint("/chat").withSockJS(); |
||||
|
} |
||||
|
} |
@ -0,0 +1,39 @@ |
|||||
|
package org.ros.chatto.websocket; |
||||
|
|
||||
|
import java.security.Principal; |
||||
|
|
||||
|
import javax.validation.Valid; |
||||
|
|
||||
|
import org.ros.chatto.dto.ChatMessageDTO; |
||||
|
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 lombok.RequiredArgsConstructor; |
||||
|
|
||||
|
@Controller |
||||
|
@RequiredArgsConstructor |
||||
|
public class WebSocketController { |
||||
|
|
||||
|
private final SimpMessagingTemplate smt; |
||||
|
|
||||
|
@MessageMapping("/ping") |
||||
|
public void send2(Principal principal) throws Exception { |
||||
|
smt.convertAndSendToUser(principal.getName(), "/queue/ping", "pong"); |
||||
|
} |
||||
|
|
||||
|
@MessageMapping("/chat") |
||||
|
public void send(@Valid @Payload final ChatMessageDTO message) |
||||
|
throws Exception { |
||||
|
smt.convertAndSendToUser(message.getFromUser(), "/queue/reply", |
||||
|
message); |
||||
|
smt.convertAndSendToUser(message.getToUser(), "/queue/reply", message); |
||||
|
} |
||||
|
|
||||
|
@GetMapping("/ws") |
||||
|
public String wsPage() { |
||||
|
return "ws"; |
||||
|
} |
||||
|
} |
@ -0,0 +1,83 @@ |
|||||
|
<html> |
||||
|
|
||||
|
<head> |
||||
|
<title>Chat WebSocket</title> |
||||
|
<script type="text/javascript"> |
||||
|
var stompClient = null; |
||||
|
|
||||
|
function setConnected(connected) { |
||||
|
document.getElementById('connect').disabled = connected; |
||||
|
document.getElementById('disconnect').disabled = !connected; |
||||
|
document.getElementById('conversationDiv').style.visibility |
||||
|
= connected ? 'visible' : 'hidden'; |
||||
|
document.getElementById('response').innerHTML = ''; |
||||
|
} |
||||
|
|
||||
|
function connect() { |
||||
|
var socket = new SockJS('/chat2'); |
||||
|
stompClient = Stomp.over(socket); |
||||
|
stompClient.connect({}, function (frame) { |
||||
|
setConnected(true); |
||||
|
console.log('Connected: ' + frame); |
||||
|
// stompClient.subscribe('/topic/messages', function (messageOutput) { |
||||
|
// showMessageOutput(JSON.parse(messageOutput.body)); |
||||
|
// }); |
||||
|
stompClient.subscribe('/user/queue/reply', function (messageOutput) { |
||||
|
showMessageOutput(JSON.parse(messageOutput.body)); |
||||
|
}); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
function disconnect() { |
||||
|
if (stompClient != null) { |
||||
|
stompClient.disconnect(); |
||||
|
} |
||||
|
setConnected(false); |
||||
|
console.log("Disconnected"); |
||||
|
} |
||||
|
|
||||
|
function sendMessage() { |
||||
|
var from = document.getElementById('from').value; |
||||
|
var to = document.getElementById('to').value; |
||||
|
var text = document.getElementById('text').value; |
||||
|
stompClient.send("/app/chat", {}, |
||||
|
JSON.stringify({ 'from': from, 'text': text, to: to })); |
||||
|
} |
||||
|
|
||||
|
function showMessageOutput(messageOutput) { |
||||
|
var response = document.getElementById('response'); |
||||
|
var p = document.createElement('p'); |
||||
|
p.style.wordWrap = 'break-word'; |
||||
|
p.appendChild(document.createTextNode(messageOutput.from + ": " |
||||
|
+ messageOutput.text + " (" + messageOutput.time + ")")); |
||||
|
response.appendChild(p); |
||||
|
} |
||||
|
</script> |
||||
|
</head> |
||||
|
|
||||
|
<body onload="disconnect()"> |
||||
|
<div> |
||||
|
<div> |
||||
|
<input type="text" id="from" placeholder="Choose a nickname" /> |
||||
|
</div> |
||||
|
<div> |
||||
|
<input type="text" id="to" placeholder="Send to User" /> |
||||
|
</div> |
||||
|
<br /> |
||||
|
<div> |
||||
|
<button id="connect" onclick="connect();">Connect</button> |
||||
|
<button id="disconnect" disabled="disabled" onclick="disconnect();"> |
||||
|
Disconnect |
||||
|
</button> |
||||
|
</div> |
||||
|
<br /> |
||||
|
<div id="conversationDiv"> |
||||
|
<input type="text" id="text" placeholder="Write a message..." /> |
||||
|
<button id="sendMessage" onclick="sendMessage();">Send</button> |
||||
|
<p id="response"></p> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
</body> |
||||
|
|
||||
|
</html> |
Write
Preview
Loading…
Cancel
Save
Reference in new issue