implemented message post in ts + some optimizations

This commit is contained in:
Rohan Sircar 2019-12-07 20:45:50 +05:30
parent 874683ad0c
commit 767d7d7b03
11 changed files with 330 additions and 57 deletions

View File

@ -4,5 +4,5 @@ export class ChatMessageDTO {
public toUser: string | undefined;
public fromUser: string | undefined;
public messageCipher!: MessageCipherDTO;
public messageTime!: Date;
public messageTime: Date | undefined | null;
}

View File

@ -8,7 +8,6 @@ import { ModelFactory } from "./model/ModelFactory";
import { ActiveUserViewModel } from "./viewmodel/ActiveUserViewModel";
import { ChatMessageViewModel } from "./viewmodel/ChatMessageViewModel";
import * as Handlebars from "handlebars";
import markdownit = require('markdown-it');
import { ChatModel } from "./model/ChatModel";
import { ChatView } from "./view/ChatView";
import { ChatController } from "./controller/ChatController";
@ -18,8 +17,9 @@ import * as log from 'loglevel';
// import log from 'loglevel';
import { EncryptionService } from "./service/EncryptionService";
import { SJCLEncryptionService } from "./service/SJCLEncryptionService";
import { MessageCipherDTO } from "./dto/MessageCipherDTO";
// var markdownit = require('markdown-it');
var md = new markdownit();
// var md = new markdownit();
const userBox = document.getElementById('contacts-box');
@ -72,8 +72,8 @@ var msgContainerTemplate = Handlebars.compile(source);
JsonAPI.ACTIVE_USERS_GET + 'aef';
const encryptionService: EncryptionService = new SJCLEncryptionService();
let ct = encryptionService.encrypt("password","data");
console.log(encryptionService.decrypt("password", JSON.parse(ct as string)));
let messageCipherDTO: MessageCipherDTO = encryptionService.encrypt("password","data");
console.log(encryptionService.decrypt("password", messageCipherDTO));
Handlebars.registerHelper('avatar', function() {
return '<div class="img_cont_msg"> <img src="https://static.turbosquid.com/Preview/001292/481/WV/_D.jpg" class="rounded-circle user_img_msg"> </div>';

View File

@ -39,7 +39,7 @@ export class ChatModel implements Subject {
console.log('Subject: Detached an observer.');
}
public setUserMessages(username: string, messages: ChatMessageViewModel[]) {
private setUserMessages(username: string, messages: ChatMessageViewModel[]) {
this._messagesMap.set(username, messages);
}
@ -61,13 +61,15 @@ export class ChatModel implements Subject {
this.notify("some user");
}
public async getmessages(userName: string, passphrase: string, lastMessageTime: string | null): Promise<ChatMessageViewModel[]> {
const cVMs = await ChatModelHelper.getMessages(userName, passphrase, lastMessageTime, this);
public async getmessages(contactName: string, passphrase: string, lastMessageTime: string | null): Promise<ChatMessageViewModel[]> {
const cVMs = await ChatModelHelper.getMessages(contactName, passphrase, lastMessageTime, this);
if (cVMs != null) {
log.info('Subject: My state has just changed')
log.debug(cVMs);
this._messagesMap.set(userName, cVMs);
this.notify(userName);
// this._messagesMap.set(userName, cVMs);
this.setUserMessages(contactName, cVMs);
JsonAPI.contactName = contactName;
this.notify(contactName);
}
else {
log.error('Messages were null');

View File

@ -12,7 +12,7 @@ import { EncryptionService } from "../service/EncryptionService";
import { SJCLEncryptionService } from "../service/SJCLEncryptionService";
import { ChatModel } from "./ChatModel"
export class ChatModelHelper {
private static readonly encryptionService = new SJCLEncryptionService();
private static readonly _encryptionService: EncryptionService = new SJCLEncryptionService();
public static async getMessages(userName: string, passphrase: string, lastMessageTime: string | null, chatModel: ChatModel): Promise<ChatMessageViewModel[]> {
switch (lastMessageTime) {
@ -54,8 +54,9 @@ export class ChatModelHelper {
const vm = new ChatMessageViewModel();
vm.fromUser = chatMessageDTO.fromUser;
vm.toUser = chatMessageDTO.toUser;
vm.messageTime = chatMessageDTO.messageTime;
vm.message = this.encryptionService.decrypt(passphrase, chatMessageDTO.messageCipher) as string;
// vm.messageTime = chatMessageDTO.messageTime;
chatMessageDTO.messageTime == null ? log.error("Message time somehow null") : vm.messageTime = chatMessageDTO.messageTime;
vm.message = this._encryptionService.decrypt(passphrase, chatMessageDTO.messageCipher) as string;
return vm;
}

View File

@ -1,4 +1,6 @@
import { MessageCipherDTO } from "../dto/MessageCipherDTO";
export interface EncryptionService {
encrypt(passphrase: string, plainText: string): Object,
decrypt(passphrase: string, cipher: Object): Object
encrypt(passphrase: string, plainText: string): any,
decrypt(passphrase: string, cipher: MessageCipherDTO): string
}

View File

@ -1,14 +1,15 @@
import { EncryptionService } from "./EncryptionService";
import * as sjcl from "sjcl";
import { MessageCipherDTO } from "../dto/MessageCipherDTO";
export class SJCLEncryptionService implements EncryptionService {
private readonly params: any = { mode: "gcm", ts: 128, adata: "", iter: 10000}
public encrypt(passphrase: string, plainText: string): Object {
return sjcl.encrypt(passphrase, plainText, this.params);
private readonly params = { mode: "gcm", ts: 128, adata: "", iter: 10000}
public encrypt(passphrase: string, plainText: string): MessageCipherDTO {
// @ts-ignore
return JSON.parse(sjcl.encrypt(passphrase, plainText, this.params) as string) as MessageCipherDTO;
}
public decrypt(passphrase: string, cipher: Object): Object {
// return sjcl.decrypt(passphrase, cipher as sjcl.SjclCipherEncrypted, undefined, undefined);
public decrypt(passphrase: string, cipher: MessageCipherDTO): string {
return sjcl.decrypt(passphrase, JSON.stringify(cipher), undefined, undefined);
}
}

View File

@ -1,8 +1,10 @@
export namespace JsonAPI {
// @ts-ignore: Cannot find name 'hostAddress'.
export let userName: string | null = localStorage.getItem('userName');
export let principleName: string | null = localStorage.getItem('username');
export let contactName: string | null;
export let authToken: string | null = localStorage.getItem('authToken');
export const ACTIVE_USERS_GET = `/api/chat/get/active-users`;
export const CHAT_MESSAGES_GET = `/api/chat/get/messages`;
export const MESSAGE_POST = '/api/chat/post/message';
}

View File

@ -7,35 +7,51 @@ import * as DOMPurify from 'dompurify';
import { MarkDownService } from "../service/MarkDownService";
import { MarkDownItMarkDownService } from "../service/MarkDownItMarkDownService";
import { JsonAPI } from "../singleton/JsonAPI";
import { MessageCipherDTO } from "../dto/MessageCipherDTO";
import { SJCLEncryptionService } from "../service/SJCLEncryptionService";
import { EncryptionService } from "../service/EncryptionService";
import { ChatMessageDTO } from "../dto/ChatMessageDTO";
import { fetchHandler } from "./FetchHandler";
// var md = new markdownit();
export class ChatView implements Observer {
private readonly _element: HTMLElement;
private readonly _chatModel: ChatModel;
private readonly _messageContainer: HTMLElement;
// private readonly _messageSendButton: HTMLElement;
private readonly _messageSendTemplate = TemplateFactory.getTemplate('msg_container_send_template');
private readonly _messageReceiveTemplate = TemplateFactory.getTemplate('msg_container_template');
private readonly _markdownService: MarkDownService = new MarkDownItMarkDownService();
private readonly _encryptionService: EncryptionService = new SJCLEncryptionService();
constructor(model: ChatModel, element: HTMLElement) {
this._element = element;
constructor(chatModel: ChatModel, messageContainer: HTMLElement) {
this._messageContainer = messageContainer;
this._chatModel = chatModel;
// this._messageSendButton = messageSendButton;
this.addEventListeners();
}
update(data: ChatMessageViewModel[]): void {
log.info('ChatView: updating view');
// let html: string = "";
this._messageContainer.innerHTML = "";
data.forEach((vm: ChatMessageViewModel) => {
const vmTemp = vm;
vmTemp.message = this._markdownService.render(vm.message);
/** Very Important!!!
* Sanitizing HTML before displaying on webpage to prevent XSS attacks!!
*/
if (vmTemp.fromUser == JsonAPI.userName) {
$(this._element).append(DOMPurify.sanitize(this._messageSendTemplate(vmTemp)));
let rendered;
if (vmTemp.fromUser == JsonAPI.principleName) {
rendered = DOMPurify.sanitize(this._messageSendTemplate(vmTemp));
}
else {
$(this._element).append(DOMPurify.sanitize(this._messageReceiveTemplate(vmTemp)));
rendered = DOMPurify.sanitize(this._messageReceiveTemplate(vmTemp));
}
$(this._messageContainer).append(rendered);
log.debug()
// html += this._messageSendTemplate(vm);
});
@ -43,4 +59,94 @@ export class ChatView implements Observer {
// this._element.innerHTML = html;
// log.debug(this._element.innerHTML);
}
private addEventListeners(): void {
this.addChatFormEL();
}
private addChatFormEL() {
const chatForm = document.getElementById('chatMessageForm') as HTMLSelectElement;
if (chatForm == null) {
log.error("Chat form is null");
}
else {
chatForm.addEventListener('submit', (e) => this.createChatMessageDTO(e, chatForm))
}
}
private createChatMessageDTO(e: Event, chatForm: HTMLSelectElement): void {
e.preventDefault();
let contactName = JsonAPI.contactName;
if(contactName == null)
{
log.error("Contact name is null");
return;
}
if (!chatForm.checkValidity()) {
console.log("error");
chatForm.classList.add('was-validated');
return;
}
chatForm.classList.add('was-validated');
const chatInput = document.getElementById('chatInput') as HTMLInputElement;
const passphraseInput = document.getElementById('passphrase') as HTMLInputElement;
if (chatInput.value == '' || chatInput.value == null) {
log.error("Chat input is null.");
return;
}
if (passphraseInput.value == '' || passphraseInput.value == null) {
log.error("Chat input is null.");
return;
}
// @ts-ignore
const messageContent = chatInput.value;
const context = { fromUser: JsonAPI.principleName, message: this._markdownService.render(messageContent), messageTime: new Date().toLocaleString() };
const msgContainer: string = this._messageSendTemplate(context);
$(this._messageContainer).append(DOMPurify.sanitize(msgContainer));
// scrollChatAreaAnimated(2400);
// let messageCipher = sjcl.encrypt(passphraseInput.value, messageContent, { mode: "gcm", ts: 128, adata: "", iter: iterations });
let messageCipher: MessageCipherDTO = this._encryptionService.encrypt(passphraseInput.value, messageContent)
// let messageCipherJson = JSON.parse(messageCipher);
let chatMessageDTO = {
"fromUser": JsonAPI.principleName,
"toUser": contactName,
"messageCipher": messageCipher,
// "messageTime": null
}
// @ts-ignore
this.sendMessageAJAX(chatMessageDTO);
}
private sendMessageAJAX(chatMessageDTO: ChatMessageDTO): void {
let headers = new Headers();
// console.log("Token = " + btoa("hmm" + ":" + "hmm"))
// headers.append('Accept','application/json')
headers.append('Content-Type', 'application/json');
// headers.append('Authorization', basicAuthToken);
// @ts-ignore
headers.append('X-AUTH-TOKEN', JsonAPI.authToken);
fetch(JsonAPI.MESSAGE_POST, {
method: 'POST',
headers: headers,
body: JSON.stringify(chatMessageDTO)
})
.then(response => {
log.debug(response);
return response.clone();
})
.then(response => fetchHandler(response));
}
}

View File

@ -0,0 +1,33 @@
import { sprintf } from "sprintf-js";
export function fetchHandler(response: any) {
if (response.ok) {
return response.json().then((json: any) => {
// the status was ok and there is a json body
// return Promise.resolve({ json: json, response: response });
// alertify.success('Message sent succesfully' + sprintf(" (http code %d)", response.status));
}).catch((err: any) => {
// the status was ok but there is no json body
// return Promise.resolve({ response: response });
// alertify.success('Message sent succesfully' + sprintf(" (http code %d)", response.status));
});
} else {
return response.json().catch((err: any) => {
// the status was not ok and there is no json body
// throw new Error(response.statusText);
// alertify.error('Some error occured. Please try again.');
}).then((json: any) => {
// the status was not ok but there is a json body
// throw new Error(json.error.message); // example error message returned by a REST
// let delay = alertify.get('notifier', 'delay');
// alertify.set('notifier', 'delay', 30);
let errorMessage = "";
json.errors.forEach(function(data: any) {
errorMessage += sprintf("Field Name: %s \n Rejected value: %s \n Reason: %s \n", data.field_name, data.rejected_value, data.error_message);
});
// alertify.error(sprintf('There were errors in your message - %s', errorMessage));
// alertify.set('notifier', 'delay', delay);
});
}
}

File diff suppressed because one or more lines are too long

View File

@ -234,7 +234,7 @@
</div>
<div class="msg_container">
{{{message}}}
<span class="msg_time">{{time}}</span>
<span class="msg_time">{{messageTime}}</span>
</div>
</div>
</template>
@ -243,7 +243,7 @@
<div class="d-flex justify-content-end mb-4">
<div class="msg_container_send">
{{{message}}}
<span class="msg_time_send">{{time}}</span>
<span class="msg_time_send">{{messageTime}}</span>
</div>
<!-- <div class="img_cont_msg">
<img src="https://static.turbosquid.com/Preview/001292/481/WV/_D.jpg"