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.

238 lines
6.9 KiB

  1. import * as log from "loglevel";
  2. import { ChatMessageDTO } from "../../chat/dto/ChatMessageDTO";
  3. import { EncryptionService } from "../../chat/service/EncryptionService";
  4. import { NotificationService } from "../../chat/service/NotificationService";
  5. import { JsonAPI } from "../../chat/singleton/JsonAPI";
  6. import { Sprintf } from "../../chat/singleton/Sprintf";
  7. import { ChatMessageViewModel } from "../../chat/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. vm.message = await this._encryptionService.decryptAsPromise(
  88. passphrase,
  89. chatMessageDTO.messageCipher
  90. );
  91. return vm;
  92. }
  93. private _toChatMessageVM(
  94. chatMessageDTO: ChatMessageDTO,
  95. passphrase: string
  96. ): ChatMessageViewModel {
  97. const vm = new ChatMessageViewModel();
  98. vm.fromUser = chatMessageDTO.fromUser;
  99. vm.toUser = chatMessageDTO.toUser;
  100. // vm.messageTime = chatMessageDTO.messageTime;
  101. chatMessageDTO.messageTime == null
  102. ? log.error("Message time somehow null")
  103. : (vm.messageTime = chatMessageDTO.messageTime);
  104. vm.message = this._encryptionService.decrypt(
  105. passphrase,
  106. chatMessageDTO.messageCipher
  107. ) as string;
  108. return vm;
  109. }
  110. private async _getAllMessagesAjax(toUser: string): Promise<any> {
  111. const headers = new Headers();
  112. if (JsonAPI.authToken == null) {
  113. log.error("authToken null");
  114. return;
  115. }
  116. headers.append("X-AUTH-TOKEN", JsonAPI.authToken);
  117. const url = Sprintf(JsonAPI.CHAT_MESSAGES_GET, toUser);
  118. log.debug(url);
  119. const response = await fetch(url, {
  120. method: "GET",
  121. headers: headers,
  122. });
  123. log.debug(response.clone());
  124. if (fetchErrorHandler(response.clone(), this._notificationService)) {
  125. return null;
  126. }
  127. const data: Promise<any> = await response.json();
  128. return data;
  129. }
  130. private async _getPaginatedMessagesAjax(
  131. toUser: string,
  132. page: number
  133. ): Promise<any> {
  134. const headers = new Headers();
  135. if (JsonAPI.authToken == null) {
  136. log.error("authToken null");
  137. return;
  138. }
  139. headers.append("X-AUTH-TOKEN", JsonAPI.authToken);
  140. const url = Sprintf(JsonAPI.CHAT_MESSAGE_PAGE_GET, toUser, page, JsonAPI.CHAT_PAGE_SIZE);
  141. log.debug(url);
  142. const response = await fetch(url, {
  143. method: "GET",
  144. headers: headers,
  145. });
  146. log.debug(response.clone());
  147. if (fetchErrorHandler(response.clone(), this._notificationService)) {
  148. return null;
  149. }
  150. const data: Promise<any> = await response.json();
  151. function func(data: any) {
  152. const d1 = data.map((d: any) => {
  153. if (d.messageTime == null) return null;
  154. d.messageTime = new Date(d.messageTime);
  155. return d;
  156. });
  157. return d1;
  158. }
  159. const data2 = func(data);
  160. return data2;
  161. }
  162. private async _getMessagesAjax(
  163. toUser: string,
  164. lastMessageTimeStamp: Date
  165. ): Promise<any> {
  166. const headers = new Headers();
  167. if (JsonAPI.authToken == null) {
  168. log.error("authToken null");
  169. return;
  170. }
  171. headers.append("X-AUTH-TOKEN", JsonAPI.authToken);
  172. // const url = Sprintf(JsonAPI.CHAT_MESSAGES_GET, toUser, page, 5);
  173. // log.debug(url)
  174. const response = await fetch(`/api/chat/get/messages/${toUser}`, {
  175. method: "GET",
  176. headers: headers,
  177. });
  178. log.debug(response.clone());
  179. if (fetchErrorHandler(response.clone(), this._notificationService)) {
  180. return null;
  181. }
  182. const data: Promise<any> = await response.json();
  183. function func(data: any) {
  184. const d1 = data.map((d: any) => {
  185. if (d.messageTime == null) return null;
  186. d.messageTime = new Date(d.messageTime);
  187. return d;
  188. });
  189. return d1;
  190. }
  191. const data2 = func(data);
  192. return data2;
  193. }
  194. private async _getNewMessagesAjax(
  195. toUser: string,
  196. lastMessageTimeStamp: Date
  197. ): Promise<any> {
  198. const headers = new Headers();
  199. headers.append("X-AUTH-TOKEN", JsonAPI.authToken);
  200. // const url = Sprintf(JsonAPI.CHAT_MESSAGES_GET, toUser, page, 5);
  201. // log.debug(url)
  202. // log.debug(lastMessageTimeStamp);
  203. // log.debug(lastMessageTimeStamp.toISOString())
  204. const response = await fetch(
  205. `/api/chat/get/messages/${toUser}/${lastMessageTimeStamp.toISOString()}`,
  206. {
  207. method: "GET",
  208. headers: headers,
  209. }
  210. );
  211. log.debug(response.clone());
  212. if (!response.ok) location.reload();
  213. const data: Promise<any> = await response.json();
  214. function func(data: any) {
  215. const d1 = data.map((d: any) => {
  216. if (d.messageTime == null) return null;
  217. d.messageTime = new Date(d.messageTime);
  218. return d;
  219. });
  220. return d1;
  221. }
  222. const data2 = func(data);
  223. return data2;
  224. }
  225. }