A self hosted chat application with end-to-end encrypted messaging.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

241 lines
7.0 KiB

  1. import * as log from "loglevel";
  2. import { ChatMessageDTO } from "../dto/ChatMessageDTO";
  3. import { EncryptionService } from "../service/EncryptionService";
  4. import { NotificationService } from "../service/NotificationService";
  5. import { JsonAPI } from "../singleton/JsonAPI";
  6. import { Sprintf } from "../singleton/Sprintf";
  7. import { ChatMessageViewModel } from "../viewmodel/ChatMessageViewModel";
  8. import { fetchErrorHandler } from "./FetchErrorHandler";
  9. export class ChatModelHelper {
  10. private readonly _encryptionService: EncryptionService;
  11. private readonly _notificationService: NotificationService;
  12. constructor(
  13. encryptionService: EncryptionService,
  14. notificationService: NotificationService
  15. ) {
  16. this._encryptionService = encryptionService;
  17. this._notificationService = notificationService;
  18. }
  19. public async getMessages(
  20. userName: string,
  21. passphrase: string,
  22. page: number | null,
  23. lastMessageTime: Date | null,
  24. op: string
  25. ): Promise<ChatMessageViewModel[]> {
  26. switch (op) {
  27. case "page": {
  28. const data: ChatMessageDTO[] = await this._getPaginatedMessagesAjax(
  29. userName,
  30. page!
  31. );
  32. const cVMs = Promise.all(
  33. data.map((vm) => this._toChatMessageVMAsync(vm, passphrase)).reverse()
  34. );
  35. return cVMs;
  36. }
  37. case "new": {
  38. const data: ChatMessageDTO[] = await this._getPaginatedMessagesAjax(
  39. userName,
  40. page!
  41. );
  42. const cVMs = Promise.all(
  43. data.map((vm) => this._toChatMessageVMAsync(vm, passphrase)).reverse()
  44. );
  45. return cVMs;
  46. }
  47. case "update": {
  48. const data: ChatMessageDTO[] = await this._getNewMessagesAjax(
  49. userName,
  50. lastMessageTime!
  51. );
  52. const cVMs = Promise.all(
  53. data.map((vm) => this._toChatMessageVMAsync(vm, passphrase)).reverse()
  54. );
  55. return cVMs;
  56. }
  57. default: {
  58. log.error("Invalid operation");
  59. return Array();
  60. }
  61. }
  62. }
  63. public async isPassphraseValid(
  64. passphrase: string,
  65. userName: string
  66. ): Promise<boolean> {
  67. const messages: ChatMessageDTO[] = await this._getPaginatedMessagesAjax(
  68. userName,
  69. 0
  70. );
  71. if (messages.length === 0) return true;
  72. try {
  73. this._encryptionService.decrypt(passphrase, messages[0].messageCipher);
  74. } catch (error) {
  75. return false;
  76. }
  77. return true;
  78. }
  79. private async _toChatMessageVMAsync(
  80. chatMessageDTO: ChatMessageDTO,
  81. passphrase: string
  82. ): Promise<ChatMessageViewModel> {
  83. const vm = new ChatMessageViewModel();
  84. vm.fromUser = chatMessageDTO.fromUser;
  85. vm.toUser = chatMessageDTO.toUser;
  86. // vm.messageTime = chatMessageDTO.messageTime;
  87. chatMessageDTO.messageTime == null
  88. ? log.error("Message time somehow null")
  89. : (vm.messageTime = chatMessageDTO.messageTime);
  90. vm.message = await this._encryptionService.decryptAsPromise(
  91. passphrase,
  92. chatMessageDTO.messageCipher
  93. );
  94. return vm;
  95. }
  96. private _toChatMessageVM(
  97. chatMessageDTO: ChatMessageDTO,
  98. passphrase: string
  99. ): ChatMessageViewModel {
  100. const vm = new ChatMessageViewModel();
  101. vm.fromUser = chatMessageDTO.fromUser;
  102. vm.toUser = chatMessageDTO.toUser;
  103. // vm.messageTime = chatMessageDTO.messageTime;
  104. chatMessageDTO.messageTime == null
  105. ? log.error("Message time somehow null")
  106. : (vm.messageTime = chatMessageDTO.messageTime);
  107. vm.message = this._encryptionService.decrypt(
  108. passphrase,
  109. chatMessageDTO.messageCipher
  110. ) as string;
  111. return vm;
  112. }
  113. private async _getAllMessagesAjax(toUser: string): Promise<any> {
  114. const headers = new Headers();
  115. if (JsonAPI.authToken == null) {
  116. log.error("authToken null");
  117. return;
  118. }
  119. headers.append("X-AUTH-TOKEN", JsonAPI.authToken);
  120. const url = Sprintf(JsonAPI.CHAT_MESSAGES_GET, toUser);
  121. log.debug(url);
  122. const response = await fetch(url, {
  123. method: "GET",
  124. headers: headers,
  125. });
  126. log.debug(response.clone());
  127. if (fetchErrorHandler(response.clone(), this._notificationService)) {
  128. return null;
  129. }
  130. const data: Promise<any> = await response.json();
  131. return data;
  132. }
  133. private async _getPaginatedMessagesAjax(
  134. toUser: string,
  135. page: number
  136. ): Promise<any> {
  137. const headers = new Headers();
  138. if (JsonAPI.authToken == null) {
  139. log.error("authToken null");
  140. return;
  141. }
  142. headers.append("X-AUTH-TOKEN", JsonAPI.authToken);
  143. const url = Sprintf(JsonAPI.CHAT_MESSAGE_PAGE_GET, toUser, page, JsonAPI.CHAT_PAGE_SIZE);
  144. log.debug(url);
  145. const response = await fetch(url, {
  146. method: "GET",
  147. headers: headers,
  148. });
  149. log.debug(response.clone());
  150. if (fetchErrorHandler(response.clone(), this._notificationService)) {
  151. return null;
  152. }
  153. const data: Promise<any> = await response.json();
  154. function func(data: any) {
  155. const d1 = data.map((d: any) => {
  156. if (d.messageTime == null) return null;
  157. d.messageTime = new Date(d.messageTime);
  158. return d;
  159. });
  160. return d1;
  161. }
  162. const data2 = func(data);
  163. return data2;
  164. }
  165. private async _getMessagesAjax(
  166. toUser: string,
  167. lastMessageTimeStamp: Date
  168. ): Promise<any> {
  169. const headers = new Headers();
  170. if (JsonAPI.authToken == null) {
  171. log.error("authToken null");
  172. return;
  173. }
  174. headers.append("X-AUTH-TOKEN", JsonAPI.authToken);
  175. // const url = Sprintf(JsonAPI.CHAT_MESSAGES_GET, toUser, page, 5);
  176. // log.debug(url)
  177. const response = await fetch(`/api/chat/get/messages/${toUser}`, {
  178. method: "GET",
  179. headers: headers,
  180. });
  181. log.debug(response.clone());
  182. if (fetchErrorHandler(response.clone(), this._notificationService)) {
  183. return null;
  184. }
  185. const data: Promise<any> = await response.json();
  186. function func(data: any) {
  187. const d1 = data.map((d: any) => {
  188. if (d.messageTime == null) return null;
  189. d.messageTime = new Date(d.messageTime);
  190. return d;
  191. });
  192. return d1;
  193. }
  194. const data2 = func(data);
  195. return data2;
  196. }
  197. private async _getNewMessagesAjax(
  198. toUser: string,
  199. lastMessageTimeStamp: Date
  200. ): Promise<any> {
  201. const headers = new Headers();
  202. headers.append("X-AUTH-TOKEN", JsonAPI.authToken);
  203. // const url = Sprintf(JsonAPI.CHAT_MESSAGES_GET, toUser, page, 5);
  204. // log.debug(url)
  205. // log.debug(lastMessageTimeStamp);
  206. // log.debug(lastMessageTimeStamp.toISOString())
  207. const response = await fetch(
  208. `/api/chat/get/messages/${toUser}/${lastMessageTimeStamp.toISOString()}`,
  209. {
  210. method: "GET",
  211. headers: headers,
  212. }
  213. );
  214. log.debug(response.clone());
  215. if(!response.ok) location.reload();
  216. const data: Promise<any> = await response.json();
  217. function func(data: any) {
  218. const d1 = data.map((d: any) => {
  219. if (d.messageTime == null) return null;
  220. d.messageTime = new Date(d.messageTime);
  221. return d;
  222. });
  223. return d1;
  224. }
  225. const data2 = func(data);
  226. return data2;
  227. }
  228. }