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 "../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 { View } from "../view/AbstractView"; |
|||
import { ChatMessageViewModel } from "../viewmodel/ChatMessageViewModel"; |
|||
import { ChatModel } from "../model/ChatModel"; |
|||
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 { |
|||
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 "../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 { View } from "../view/AbstractView"; |
|||
import { ActiveUserViewModel } from "../viewmodel/ActiveUserViewModel"; |
|||
import { UserView } from "../view/UserView"; |
|||
import { UserModel } from "../model/UserModel"; |
|||
import { ChatMessageDTO } from "../../common/dto/ChatMessageDTO"; |
|||
import { ChatMessageViewModel } from "../viewmodel/ChatMessageViewModel"; |
|||
|
|||
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"; |
|||
|
|||
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 { |
|||
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