| 
									
										
										
										
											2021-01-14 12:07:05 -06:00
										 |  |  | // Copyright 2020-2021 Signal Messenger, LLC
 | 
					
						
							| 
									
										
										
										
											2020-10-30 15:34:04 -05:00
										 |  |  | // SPDX-License-Identifier: AGPL-3.0-only
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-24 14:53:21 -07:00
										 |  |  | /* eslint-disable no-nested-ternary */ | 
					
						
							|  |  |  | /* eslint-disable camelcase */ | 
					
						
							|  |  |  | /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ | 
					
						
							|  |  |  | /* eslint-disable no-await-in-loop */ | 
					
						
							|  |  |  | /* eslint-disable no-restricted-syntax */ | 
					
						
							|  |  |  | /* eslint-disable no-console */ | 
					
						
							|  |  |  | /* eslint-disable @typescript-eslint/no-explicit-any */ | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | import { join } from 'path'; | 
					
						
							|  |  |  | import mkdirp from 'mkdirp'; | 
					
						
							|  |  |  | import rimraf from 'rimraf'; | 
					
						
							|  |  |  | import PQueue from 'p-queue'; | 
					
						
							|  |  |  | import sql from '@journeyapps/sqlcipher'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import pify from 'pify'; | 
					
						
							|  |  |  | import { v4 as generateUUID } from 'uuid'; | 
					
						
							|  |  |  | import { | 
					
						
							|  |  |  |   Dictionary, | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  |   forEach, | 
					
						
							|  |  |  |   fromPairs, | 
					
						
							| 
									
										
										
										
											2021-03-18 12:09:27 -05:00
										 |  |  |   isNil, | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  |   isNumber, | 
					
						
							|  |  |  |   isObject, | 
					
						
							|  |  |  |   isString, | 
					
						
							| 
									
										
										
										
											2020-03-05 13:14:58 -08:00
										 |  |  |   keyBy, | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  |   last, | 
					
						
							|  |  |  |   map, | 
					
						
							| 
									
										
										
										
											2019-05-31 15:42:01 -07:00
										 |  |  |   pick, | 
					
						
							| 
									
										
										
										
											2021-03-18 12:09:27 -05:00
										 |  |  |   omit, | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | } from 'lodash'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-18 12:09:27 -05:00
										 |  |  | import { assert } from '../util/assert'; | 
					
						
							|  |  |  | import { isNormalNumber } from '../util/isNormalNumber'; | 
					
						
							| 
									
										
										
										
											2020-09-24 14:53:21 -07:00
										 |  |  | import { combineNames } from '../util/combineNames'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import { GroupV2MemberType } from '../model-types.d'; | 
					
						
							|  |  |  | import { LocaleMessagesType } from '../types/I18N'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | import { | 
					
						
							|  |  |  |   AttachmentDownloadJobType, | 
					
						
							|  |  |  |   ConversationType, | 
					
						
							|  |  |  |   IdentityKeyType, | 
					
						
							|  |  |  |   ItemType, | 
					
						
							|  |  |  |   MessageType, | 
					
						
							|  |  |  |   PreKeyType, | 
					
						
							|  |  |  |   SearchResultMessageType, | 
					
						
							|  |  |  |   ServerInterface, | 
					
						
							|  |  |  |   SessionType, | 
					
						
							|  |  |  |   SignedPreKeyType, | 
					
						
							|  |  |  |   StickerPackStatusType, | 
					
						
							|  |  |  |   StickerPackType, | 
					
						
							|  |  |  |   StickerType, | 
					
						
							|  |  |  |   UnprocessedType, | 
					
						
							|  |  |  | } from './Interface'; | 
					
						
							| 
									
										
										
										
											2021-03-04 16:44:57 -05:00
										 |  |  | import { applyQueueing } from './Queueing'; | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | declare global { | 
					
						
							| 
									
										
										
										
											2021-01-14 12:07:05 -06:00
										 |  |  |   // We want to extend `Function`'s properties, so we need to use an interface.
 | 
					
						
							|  |  |  |   // eslint-disable-next-line no-restricted-syntax
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   interface Function { | 
					
						
							|  |  |  |     needsSerial?: boolean; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Because we can't force this module to conform to an interface, we narrow our exports
 | 
					
						
							|  |  |  | //   to this one default export, which does conform to the interface.
 | 
					
						
							|  |  |  | // Note: In Javascript, you need to access the .default property when requiring it
 | 
					
						
							|  |  |  | // https://github.com/microsoft/TypeScript/issues/420
 | 
					
						
							|  |  |  | const dataInterface: ServerInterface = { | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |   close, | 
					
						
							|  |  |  |   removeDB, | 
					
						
							| 
									
										
										
										
											2018-11-01 16:40:19 -07:00
										 |  |  |   removeIndexedDBFiles, | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |   createOrUpdateIdentityKey, | 
					
						
							|  |  |  |   getIdentityKeyById, | 
					
						
							|  |  |  |   bulkAddIdentityKeys, | 
					
						
							|  |  |  |   removeIdentityKeyById, | 
					
						
							|  |  |  |   removeAllIdentityKeys, | 
					
						
							| 
									
										
										
										
											2019-02-04 15:54:37 -08:00
										 |  |  |   getAllIdentityKeys, | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   createOrUpdatePreKey, | 
					
						
							|  |  |  |   getPreKeyById, | 
					
						
							|  |  |  |   bulkAddPreKeys, | 
					
						
							|  |  |  |   removePreKeyById, | 
					
						
							|  |  |  |   removeAllPreKeys, | 
					
						
							| 
									
										
										
										
											2019-02-04 15:54:37 -08:00
										 |  |  |   getAllPreKeys, | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   createOrUpdateSignedPreKey, | 
					
						
							|  |  |  |   getSignedPreKeyById, | 
					
						
							|  |  |  |   getAllSignedPreKeys, | 
					
						
							|  |  |  |   bulkAddSignedPreKeys, | 
					
						
							|  |  |  |   removeSignedPreKeyById, | 
					
						
							|  |  |  |   removeAllSignedPreKeys, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   createOrUpdateItem, | 
					
						
							|  |  |  |   getItemById, | 
					
						
							|  |  |  |   getAllItems, | 
					
						
							|  |  |  |   bulkAddItems, | 
					
						
							|  |  |  |   removeItemById, | 
					
						
							|  |  |  |   removeAllItems, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   createOrUpdateSession, | 
					
						
							| 
									
										
										
										
											2019-09-26 12:56:31 -07:00
										 |  |  |   createOrUpdateSessions, | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |   getSessionById, | 
					
						
							| 
									
										
										
										
											2020-03-05 13:14:58 -08:00
										 |  |  |   getSessionsById, | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |   bulkAddSessions, | 
					
						
							|  |  |  |   removeSessionById, | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   removeSessionsByConversation, | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |   removeAllSessions, | 
					
						
							| 
									
										
										
										
											2019-02-04 15:54:37 -08:00
										 |  |  |   getAllSessions, | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  |   getConversationCount, | 
					
						
							|  |  |  |   saveConversation, | 
					
						
							|  |  |  |   saveConversations, | 
					
						
							|  |  |  |   getConversationById, | 
					
						
							|  |  |  |   updateConversation, | 
					
						
							| 
									
										
										
										
											2019-09-26 12:56:31 -07:00
										 |  |  |   updateConversations, | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  |   removeConversation, | 
					
						
							| 
									
										
										
										
											2020-09-08 20:56:23 -04:00
										 |  |  |   eraseStorageServiceStateFromConversations, | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  |   getAllConversations, | 
					
						
							|  |  |  |   getAllConversationIds, | 
					
						
							|  |  |  |   getAllPrivateConversations, | 
					
						
							|  |  |  |   getAllGroupsInvolvingId, | 
					
						
							| 
									
										
										
										
											2019-01-14 13:47:19 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  |   searchConversations, | 
					
						
							| 
									
										
										
										
											2019-01-14 13:47:19 -08:00
										 |  |  |   searchMessages, | 
					
						
							|  |  |  |   searchMessagesInConversation, | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-06 16:18:58 -07:00
										 |  |  |   getMessageCount, | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |   saveMessage, | 
					
						
							|  |  |  |   saveMessages, | 
					
						
							|  |  |  |   removeMessage, | 
					
						
							| 
									
										
										
										
											2021-01-12 16:42:15 -08:00
										 |  |  |   removeMessages, | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |   getUnreadByConversation, | 
					
						
							|  |  |  |   getMessageBySender, | 
					
						
							|  |  |  |   getMessageById, | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   _getAllMessages, | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |   getAllMessageIds, | 
					
						
							|  |  |  |   getMessagesBySentAt, | 
					
						
							|  |  |  |   getExpiredMessages, | 
					
						
							| 
									
										
										
										
											2018-08-07 12:33:56 -07:00
										 |  |  |   getOutgoingWithoutExpiresAt, | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |   getNextExpiringMessage, | 
					
						
							| 
									
										
										
										
											2019-06-26 12:33:13 -07:00
										 |  |  |   getNextTapToViewMessageToAgeOut, | 
					
						
							|  |  |  |   getTapToViewMessagesNeedingErase, | 
					
						
							| 
									
										
										
										
											2019-05-31 15:42:01 -07:00
										 |  |  |   getOlderMessagesByConversation, | 
					
						
							|  |  |  |   getNewerMessagesByConversation, | 
					
						
							|  |  |  |   getMessageMetricsForConversation, | 
					
						
							| 
									
										
										
										
											2020-08-06 17:50:54 -07:00
										 |  |  |   getLastConversationActivity, | 
					
						
							|  |  |  |   getLastConversationPreview, | 
					
						
							| 
									
										
										
										
											2020-12-07 14:43:19 -06:00
										 |  |  |   hasGroupCallHistoryMessage, | 
					
						
							| 
									
										
										
										
											2020-07-10 11:28:49 -07:00
										 |  |  |   migrateConversationMessages, | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-28 15:51:26 -07:00
										 |  |  |   getUnprocessedCount, | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |   getAllUnprocessed, | 
					
						
							|  |  |  |   saveUnprocessed, | 
					
						
							| 
									
										
										
										
											2019-02-04 17:23:50 -08:00
										 |  |  |   updateUnprocessedAttempts, | 
					
						
							|  |  |  |   updateUnprocessedWithData, | 
					
						
							| 
									
										
										
										
											2019-09-26 12:56:31 -07:00
										 |  |  |   updateUnprocessedsWithData, | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |   getUnprocessedById, | 
					
						
							|  |  |  |   saveUnprocesseds, | 
					
						
							|  |  |  |   removeUnprocessed, | 
					
						
							|  |  |  |   removeAllUnprocessed, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-30 12:15:07 -08:00
										 |  |  |   getNextAttachmentDownloadJobs, | 
					
						
							|  |  |  |   saveAttachmentDownloadJob, | 
					
						
							|  |  |  |   setAttachmentDownloadJobPending, | 
					
						
							|  |  |  |   resetAttachmentDownloadPending, | 
					
						
							|  |  |  |   removeAttachmentDownloadJob, | 
					
						
							|  |  |  |   removeAllAttachmentDownloadJobs, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  |   createOrUpdateStickerPack, | 
					
						
							|  |  |  |   updateStickerPackStatus, | 
					
						
							|  |  |  |   createOrUpdateSticker, | 
					
						
							|  |  |  |   updateStickerLastUsed, | 
					
						
							|  |  |  |   addStickerPackReference, | 
					
						
							|  |  |  |   deleteStickerPackReference, | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   getStickerCount, | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  |   deleteStickerPack, | 
					
						
							|  |  |  |   getAllStickerPacks, | 
					
						
							|  |  |  |   getAllStickers, | 
					
						
							|  |  |  |   getRecentStickers, | 
					
						
							| 
									
										
										
										
											2021-01-27 14:39:45 -08:00
										 |  |  |   clearAllErrorStickerPackAttempts, | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-24 16:58:27 -07:00
										 |  |  |   updateEmojiUsage, | 
					
						
							|  |  |  |   getRecentEmojis, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |   removeAll, | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |   removeAllConfiguration, | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   getMessagesNeedingUpgrade, | 
					
						
							|  |  |  |   getMessagesWithVisualMediaAttachments, | 
					
						
							|  |  |  |   getMessagesWithFileAttachments, | 
					
						
							| 
									
										
										
										
											2018-08-06 16:18:58 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   // Server-only
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   initialize, | 
					
						
							| 
									
										
										
										
											2021-03-04 16:44:57 -05:00
										 |  |  |   initializeRenderer, | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-06 16:18:58 -07:00
										 |  |  |   removeKnownAttachments, | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  |   removeKnownStickers, | 
					
						
							| 
									
										
										
										
											2019-08-06 17:40:25 -07:00
										 |  |  |   removeKnownDraftAttachments, | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-04 16:44:57 -05:00
										 |  |  | export default applyQueueing(dataInterface); | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | function objectToJSON(data: any) { | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |   return JSON.stringify(data); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | function jsonToObject(json: string): any { | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |   return JSON.parse(json); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2021-03-18 12:09:27 -05:00
										 |  |  | function rowToConversation( | 
					
						
							|  |  |  |   row: Readonly<{ | 
					
						
							|  |  |  |     json: string; | 
					
						
							|  |  |  |     profileLastFetchedAt: null | number; | 
					
						
							|  |  |  |   }> | 
					
						
							|  |  |  | ): ConversationType { | 
					
						
							|  |  |  |   const parsedJson = JSON.parse(row.json); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   let profileLastFetchedAt: undefined | number; | 
					
						
							|  |  |  |   if (isNormalNumber(row.profileLastFetchedAt)) { | 
					
						
							|  |  |  |     profileLastFetchedAt = row.profileLastFetchedAt; | 
					
						
							|  |  |  |   } else { | 
					
						
							|  |  |  |     assert( | 
					
						
							|  |  |  |       isNil(row.profileLastFetchedAt), | 
					
						
							|  |  |  |       'profileLastFetchedAt contained invalid data; defaulting to undefined' | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |     profileLastFetchedAt = undefined; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return { | 
					
						
							|  |  |  |     ...parsedJson, | 
					
						
							|  |  |  |     profileLastFetchedAt, | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-04 16:44:57 -05:00
										 |  |  | function isRenderer() { | 
					
						
							|  |  |  |   if (typeof process === 'undefined' || !process) { | 
					
						
							|  |  |  |     return true; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return process.type === 'renderer'; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function openDatabase(filePath: string): Promise<sql.Database> { | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |   return new Promise((resolve, reject) => { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |     let instance: sql.Database | undefined; | 
					
						
							|  |  |  |     const callback = (error: Error | null) => { | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |       if (error) { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |         reject(error); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return; | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |       } | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |       if (!instance) { | 
					
						
							|  |  |  |         reject(new Error('openDatabase: Unable to get database instance')); | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |         return; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       resolve(instance); | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     instance = new sql.Database(filePath, callback); | 
					
						
							| 
									
										
										
										
											2021-03-12 14:18:24 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     // See: https://github.com/mapbox/node-sqlite3/issues/1395
 | 
					
						
							|  |  |  |     instance.serialize(); | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |   }); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | type PromisifiedSQLDatabase = { | 
					
						
							|  |  |  |   close: () => Promise<void>; | 
					
						
							|  |  |  |   run: (statement: string, params?: { [key: string]: any }) => Promise<void>; | 
					
						
							|  |  |  |   get: (statement: string, params?: { [key: string]: any }) => Promise<any>; | 
					
						
							|  |  |  |   all: ( | 
					
						
							|  |  |  |     statement: string, | 
					
						
							|  |  |  |     params?: { [key: string]: any } | 
					
						
							|  |  |  |   ) => Promise<Array<any>>; | 
					
						
							| 
									
										
										
										
											2021-01-12 16:42:15 -08:00
										 |  |  |   on: (event: 'trace', handler: (sql: string) => void) => void; | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | }; | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | function promisify(rawInstance: sql.Database): PromisifiedSQLDatabase { | 
					
						
							|  |  |  |   return { | 
					
						
							|  |  |  |     close: pify(rawInstance.close.bind(rawInstance)), | 
					
						
							|  |  |  |     run: pify(rawInstance.run.bind(rawInstance)), | 
					
						
							|  |  |  |     get: pify(rawInstance.get.bind(rawInstance)), | 
					
						
							|  |  |  |     all: pify(rawInstance.all.bind(rawInstance)), | 
					
						
							| 
									
										
										
										
											2021-01-12 16:42:15 -08:00
										 |  |  |     on: rawInstance.on.bind(rawInstance), | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   }; | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function getSQLiteVersion(instance: PromisifiedSQLDatabase) { | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |   const row = await instance.get('select sqlite_version() AS sqlite_version'); | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |   return row.sqlite_version; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function getSchemaVersion(instance: PromisifiedSQLDatabase) { | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |   const row = await instance.get('PRAGMA schema_version;'); | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |   return row.schema_version; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function setUserVersion( | 
					
						
							|  |  |  |   instance: PromisifiedSQLDatabase, | 
					
						
							|  |  |  |   version: number | 
					
						
							|  |  |  | ) { | 
					
						
							| 
									
										
										
										
											2019-08-20 06:24:43 -07:00
										 |  |  |   if (!isNumber(version)) { | 
					
						
							|  |  |  |     throw new Error(`setUserVersion: version ${version} is not a number`); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   await instance.get(`PRAGMA user_version = ${version};`); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function keyDatabase(instance: PromisifiedSQLDatabase, key: string) { | 
					
						
							| 
									
										
										
										
											2019-08-20 06:24:43 -07:00
										 |  |  |   // https://www.zetetic.net/sqlcipher/sqlcipher-api/#key
 | 
					
						
							|  |  |  |   await instance.run(`PRAGMA key = "x'${key}'";`); | 
					
						
							| 
									
										
										
										
											2021-03-24 14:35:06 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   // https://sqlite.org/wal.html
 | 
					
						
							|  |  |  |   await instance.run('PRAGMA journal_mode = WAL;'); | 
					
						
							|  |  |  |   await instance.run('PRAGMA synchronous = NORMAL;'); | 
					
						
							| 
									
										
										
										
											2019-08-20 06:24:43 -07:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function getUserVersion(instance: PromisifiedSQLDatabase) { | 
					
						
							| 
									
										
										
										
											2019-08-20 06:24:43 -07:00
										 |  |  |   const row = await instance.get('PRAGMA user_version;'); | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-20 06:24:43 -07:00
										 |  |  |   return row.user_version; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function getSQLCipherVersion(instance: PromisifiedSQLDatabase) { | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |   const row = await instance.get('PRAGMA cipher_version;'); | 
					
						
							|  |  |  |   try { | 
					
						
							|  |  |  |     return row.cipher_version; | 
					
						
							|  |  |  |   } catch (e) { | 
					
						
							|  |  |  |     return null; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function getSQLCipherIntegrityCheck(instance: PromisifiedSQLDatabase) { | 
					
						
							| 
									
										
										
										
											2019-08-19 10:59:30 -07:00
										 |  |  |   const row = await instance.get('PRAGMA cipher_integrity_check;'); | 
					
						
							|  |  |  |   if (row) { | 
					
						
							|  |  |  |     return row.cipher_integrity_check; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return null; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function getSQLIntegrityCheck(instance: PromisifiedSQLDatabase) { | 
					
						
							| 
									
										
										
										
											2019-09-23 14:08:33 -07:00
										 |  |  |   const row = await instance.get('PRAGMA integrity_check;'); | 
					
						
							|  |  |  |   if (row && row.integrity_check !== 'ok') { | 
					
						
							|  |  |  |     return row.integrity_check; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return null; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function migrateSchemaVersion(instance: PromisifiedSQLDatabase) { | 
					
						
							| 
									
										
										
										
											2019-08-20 06:24:43 -07:00
										 |  |  |   const userVersion = await getUserVersion(instance); | 
					
						
							|  |  |  |   if (userVersion > 0) { | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const schemaVersion = await getSchemaVersion(instance); | 
					
						
							|  |  |  |   const newUserVersion = schemaVersion > 18 ? 16 : schemaVersion; | 
					
						
							|  |  |  |   console.log( | 
					
						
							|  |  |  |     `migrateSchemaVersion: Migrating from schema_version ${schemaVersion} to user_version ${newUserVersion}` | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   await setUserVersion(instance, newUserVersion); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function openAndMigrateDatabase(filePath: string, key: string) { | 
					
						
							|  |  |  |   let promisified: PromisifiedSQLDatabase | undefined; | 
					
						
							| 
									
										
										
										
											2019-08-20 06:24:43 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   // First, we try to open the database without any cipher changes
 | 
					
						
							|  |  |  |   try { | 
					
						
							|  |  |  |     const instance = await openDatabase(filePath); | 
					
						
							|  |  |  |     promisified = promisify(instance); | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |     await keyDatabase(promisified, key); | 
					
						
							| 
									
										
										
										
											2019-08-20 06:24:43 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     await migrateSchemaVersion(promisified); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return promisified; | 
					
						
							|  |  |  |   } catch (error) { | 
					
						
							|  |  |  |     if (promisified) { | 
					
						
							|  |  |  |       await promisified.close(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     console.log('migrateDatabase: Migration without cipher change failed'); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // If that fails, we try to open the database with 3.x compatibility to extract the
 | 
					
						
							|  |  |  |   //   user_version (previously stored in schema_version, blown away by cipher_migrate).
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const instance1 = await openDatabase(filePath); | 
					
						
							|  |  |  |   promisified = promisify(instance1); | 
					
						
							|  |  |  |   await keyDatabase(promisified, key); | 
					
						
							| 
									
										
										
										
											2019-08-20 06:24:43 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   // https://www.zetetic.net/blog/2018/11/30/sqlcipher-400-release/#compatability-sqlcipher-4-0-0
 | 
					
						
							|  |  |  |   await promisified.run('PRAGMA cipher_compatibility = 3;'); | 
					
						
							|  |  |  |   await migrateSchemaVersion(promisified); | 
					
						
							|  |  |  |   await promisified.close(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // After migrating user_version -> schema_version, we reopen database, because we can't
 | 
					
						
							|  |  |  |   //   migrate to the latest ciphers after we've modified the defaults.
 | 
					
						
							|  |  |  |   const instance2 = await openDatabase(filePath); | 
					
						
							|  |  |  |   promisified = promisify(instance2); | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   await keyDatabase(promisified, key); | 
					
						
							| 
									
										
										
										
											2019-08-20 06:24:43 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   await promisified.run('PRAGMA cipher_migrate;'); | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-20 06:24:43 -07:00
										 |  |  |   return promisified; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | const INVALID_KEY = /[^0-9A-Fa-f]/; | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function openAndSetUpSQLCipher( | 
					
						
							|  |  |  |   filePath: string, | 
					
						
							|  |  |  |   { key }: { key: string } | 
					
						
							|  |  |  | ) { | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |   const match = INVALID_KEY.exec(key); | 
					
						
							|  |  |  |   if (match) { | 
					
						
							|  |  |  |     throw new Error(`setupSQLCipher: key '${key}' is not valid`); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-20 06:24:43 -07:00
										 |  |  |   const instance = await openAndMigrateDatabase(filePath, key); | 
					
						
							| 
									
										
										
										
											2019-08-19 10:59:30 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  |   // Because foreign key support is not enabled by default!
 | 
					
						
							|  |  |  |   await instance.run('PRAGMA foreign_keys = ON;'); | 
					
						
							| 
									
										
										
										
											2019-08-20 06:24:43 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   return instance; | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function updateToSchemaVersion1( | 
					
						
							|  |  |  |   currentVersion: number, | 
					
						
							|  |  |  |   instance: PromisifiedSQLDatabase | 
					
						
							|  |  |  | ) { | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |   if (currentVersion >= 1) { | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   console.log('updateToSchemaVersion1: starting...'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   await instance.run('BEGIN TRANSACTION;'); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |   try { | 
					
						
							|  |  |  |     await instance.run( | 
					
						
							|  |  |  |       `CREATE TABLE messages(
 | 
					
						
							| 
									
										
										
										
											2018-08-07 12:33:56 -07:00
										 |  |  |       id STRING PRIMARY KEY ASC, | 
					
						
							|  |  |  |       json TEXT, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       unread INTEGER, | 
					
						
							|  |  |  |       expires_at INTEGER, | 
					
						
							|  |  |  |       sent_at INTEGER, | 
					
						
							|  |  |  |       schemaVersion INTEGER, | 
					
						
							|  |  |  |       conversationId STRING, | 
					
						
							|  |  |  |       received_at INTEGER, | 
					
						
							|  |  |  |       source STRING, | 
					
						
							|  |  |  |       sourceDevice STRING, | 
					
						
							|  |  |  |       hasAttachments INTEGER, | 
					
						
							|  |  |  |       hasFileAttachments INTEGER, | 
					
						
							|  |  |  |       hasVisualMediaAttachments INTEGER | 
					
						
							|  |  |  |     );`
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     ); | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     await instance.run(`CREATE INDEX messages_unread ON messages (
 | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |       unread | 
					
						
							|  |  |  |     );`);
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     await instance.run(`CREATE INDEX messages_expires_at ON messages (
 | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |       expires_at | 
					
						
							|  |  |  |     );`);
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     await instance.run(`CREATE INDEX messages_receipt ON messages (
 | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |       sent_at | 
					
						
							|  |  |  |     );`);
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     await instance.run(`CREATE INDEX messages_schemaVersion ON messages (
 | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |       schemaVersion | 
					
						
							|  |  |  |     );`);
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     await instance.run(`CREATE INDEX messages_conversation ON messages (
 | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |       conversationId, | 
					
						
							|  |  |  |       received_at | 
					
						
							|  |  |  |     );`);
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     await instance.run(`CREATE INDEX messages_duplicate_check ON messages (
 | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |       source, | 
					
						
							|  |  |  |       sourceDevice, | 
					
						
							|  |  |  |       sent_at | 
					
						
							|  |  |  |     );`);
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     await instance.run(`CREATE INDEX messages_hasAttachments ON messages (
 | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |       conversationId, | 
					
						
							|  |  |  |       hasAttachments, | 
					
						
							|  |  |  |       received_at | 
					
						
							|  |  |  |     );`);
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     await instance.run(`CREATE INDEX messages_hasFileAttachments ON messages (
 | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |       conversationId, | 
					
						
							|  |  |  |       hasFileAttachments, | 
					
						
							|  |  |  |       received_at | 
					
						
							|  |  |  |     );`);
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     await instance.run(`CREATE INDEX messages_hasVisualMediaAttachments ON messages (
 | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |       conversationId, | 
					
						
							|  |  |  |       hasVisualMediaAttachments, | 
					
						
							|  |  |  |       received_at | 
					
						
							|  |  |  |     );`);
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     await instance.run(`CREATE TABLE unprocessed(
 | 
					
						
							| 
									
										
										
										
											2021-01-13 08:32:18 -08:00
										 |  |  |       id STRING, | 
					
						
							|  |  |  |       timestamp INTEGER, | 
					
						
							|  |  |  |       json TEXT | 
					
						
							|  |  |  |     );`);
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     await instance.run(`CREATE INDEX unprocessed_id ON unprocessed (
 | 
					
						
							| 
									
										
										
										
											2021-01-13 08:32:18 -08:00
										 |  |  |       id | 
					
						
							|  |  |  |     );`);
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     await instance.run(`CREATE INDEX unprocessed_timestamp ON unprocessed (
 | 
					
						
							| 
									
										
										
										
											2021-01-13 08:32:18 -08:00
										 |  |  |       timestamp | 
					
						
							|  |  |  |     );`);
 | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-20 06:24:43 -07:00
										 |  |  |     await instance.run('PRAGMA user_version = 1;'); | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     await instance.run('COMMIT TRANSACTION;'); | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     console.log('updateToSchemaVersion1: success!'); | 
					
						
							|  |  |  |   } catch (error) { | 
					
						
							|  |  |  |     await instance.run('ROLLBACK;'); | 
					
						
							|  |  |  |     throw error; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function updateToSchemaVersion2( | 
					
						
							|  |  |  |   currentVersion: number, | 
					
						
							|  |  |  |   instance: PromisifiedSQLDatabase | 
					
						
							|  |  |  | ) { | 
					
						
							| 
									
										
										
										
											2018-08-07 12:33:56 -07:00
										 |  |  |   if (currentVersion >= 2) { | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   console.log('updateToSchemaVersion2: starting...'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   await instance.run('BEGIN TRANSACTION;'); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |   try { | 
					
						
							|  |  |  |     await instance.run( | 
					
						
							|  |  |  |       `ALTER TABLE messages
 | 
					
						
							| 
									
										
										
										
											2018-08-07 12:33:56 -07:00
										 |  |  |      ADD COLUMN expireTimer INTEGER;`
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     ); | 
					
						
							| 
									
										
										
										
											2018-08-07 12:33:56 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     await instance.run( | 
					
						
							|  |  |  |       `ALTER TABLE messages
 | 
					
						
							| 
									
										
										
										
											2018-08-07 12:33:56 -07:00
										 |  |  |      ADD COLUMN expirationStartTimestamp INTEGER;`
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     ); | 
					
						
							| 
									
										
										
										
											2018-08-07 12:33:56 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     await instance.run( | 
					
						
							|  |  |  |       `ALTER TABLE messages
 | 
					
						
							| 
									
										
										
										
											2018-08-07 12:33:56 -07:00
										 |  |  |      ADD COLUMN type STRING;`
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     ); | 
					
						
							| 
									
										
										
										
											2018-08-07 12:33:56 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     await instance.run(`CREATE INDEX messages_expiring ON messages (
 | 
					
						
							| 
									
										
										
										
											2018-08-07 12:33:56 -07:00
										 |  |  |       expireTimer, | 
					
						
							|  |  |  |       expirationStartTimestamp, | 
					
						
							|  |  |  |       expires_at | 
					
						
							|  |  |  |     );`);
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     await instance.run( | 
					
						
							|  |  |  |       `UPDATE messages SET
 | 
					
						
							| 
									
										
										
										
											2018-08-07 12:33:56 -07:00
										 |  |  |       expirationStartTimestamp = json_extract(json, '$.expirationStartTimestamp'), | 
					
						
							|  |  |  |       expireTimer = json_extract(json, '$.expireTimer'), | 
					
						
							|  |  |  |       type = json_extract(json, '$.type');`
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     ); | 
					
						
							| 
									
										
										
										
											2018-08-07 12:33:56 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-20 06:24:43 -07:00
										 |  |  |     await instance.run('PRAGMA user_version = 2;'); | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     await instance.run('COMMIT TRANSACTION;'); | 
					
						
							| 
									
										
										
										
											2018-08-07 12:33:56 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     console.log('updateToSchemaVersion2: success!'); | 
					
						
							|  |  |  |   } catch (error) { | 
					
						
							|  |  |  |     await instance.run('ROLLBACK;'); | 
					
						
							|  |  |  |     throw error; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-08-07 12:33:56 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function updateToSchemaVersion3( | 
					
						
							|  |  |  |   currentVersion: number, | 
					
						
							|  |  |  |   instance: PromisifiedSQLDatabase | 
					
						
							|  |  |  | ) { | 
					
						
							| 
									
										
										
										
											2018-08-08 18:32:10 -07:00
										 |  |  |   if (currentVersion >= 3) { | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   console.log('updateToSchemaVersion3: starting...'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   await instance.run('BEGIN TRANSACTION;'); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |   try { | 
					
						
							|  |  |  |     await instance.run('DROP INDEX messages_expiring;'); | 
					
						
							|  |  |  |     await instance.run('DROP INDEX messages_unread;'); | 
					
						
							| 
									
										
										
										
											2018-08-08 18:32:10 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     await instance.run(`CREATE INDEX messages_without_timer ON messages (
 | 
					
						
							| 
									
										
										
										
											2018-08-08 18:32:10 -07:00
										 |  |  |       expireTimer, | 
					
						
							|  |  |  |       expires_at, | 
					
						
							|  |  |  |       type | 
					
						
							|  |  |  |     ) WHERE expires_at IS NULL AND expireTimer IS NOT NULL;`);
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     await instance.run(`CREATE INDEX messages_unread ON messages (
 | 
					
						
							| 
									
										
										
										
											2018-08-08 18:32:10 -07:00
										 |  |  |       conversationId, | 
					
						
							|  |  |  |       unread | 
					
						
							|  |  |  |     ) WHERE unread IS NOT NULL;`);
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     await instance.run('ANALYZE;'); | 
					
						
							| 
									
										
										
										
											2019-08-20 06:24:43 -07:00
										 |  |  |     await instance.run('PRAGMA user_version = 3;'); | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     await instance.run('COMMIT TRANSACTION;'); | 
					
						
							| 
									
										
										
										
											2018-08-08 18:32:10 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     console.log('updateToSchemaVersion3: success!'); | 
					
						
							|  |  |  |   } catch (error) { | 
					
						
							|  |  |  |     await instance.run('ROLLBACK;'); | 
					
						
							|  |  |  |     throw error; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-08-08 18:32:10 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function updateToSchemaVersion4( | 
					
						
							|  |  |  |   currentVersion: number, | 
					
						
							|  |  |  |   instance: PromisifiedSQLDatabase | 
					
						
							|  |  |  | ) { | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  |   if (currentVersion >= 4) { | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   console.log('updateToSchemaVersion4: starting...'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   await instance.run('BEGIN TRANSACTION;'); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |   try { | 
					
						
							|  |  |  |     await instance.run( | 
					
						
							|  |  |  |       `CREATE TABLE conversations(
 | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  |       id STRING PRIMARY KEY ASC, | 
					
						
							|  |  |  |       json TEXT, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       active_at INTEGER, | 
					
						
							|  |  |  |       type STRING, | 
					
						
							|  |  |  |       members TEXT, | 
					
						
							|  |  |  |       name TEXT, | 
					
						
							|  |  |  |       profileName TEXT | 
					
						
							|  |  |  |     );`
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     ); | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     await instance.run(`CREATE INDEX conversations_active ON conversations (
 | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  |       active_at | 
					
						
							|  |  |  |     ) WHERE active_at IS NOT NULL;`);
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     await instance.run(`CREATE INDEX conversations_type ON conversations (
 | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  |       type | 
					
						
							|  |  |  |     ) WHERE type IS NOT NULL;`);
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-20 06:24:43 -07:00
										 |  |  |     await instance.run('PRAGMA user_version = 4;'); | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     await instance.run('COMMIT TRANSACTION;'); | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     console.log('updateToSchemaVersion4: success!'); | 
					
						
							|  |  |  |   } catch (error) { | 
					
						
							|  |  |  |     await instance.run('ROLLBACK;'); | 
					
						
							|  |  |  |     throw error; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function updateToSchemaVersion6( | 
					
						
							|  |  |  |   currentVersion: number, | 
					
						
							|  |  |  |   instance: PromisifiedSQLDatabase | 
					
						
							|  |  |  | ) { | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |   if (currentVersion >= 6) { | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   console.log('updateToSchemaVersion6: starting...'); | 
					
						
							|  |  |  |   await instance.run('BEGIN TRANSACTION;'); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |   try { | 
					
						
							|  |  |  |     // key-value, ids are strings, one extra column
 | 
					
						
							|  |  |  |     await instance.run( | 
					
						
							|  |  |  |       `CREATE TABLE sessions(
 | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |       id STRING PRIMARY KEY ASC, | 
					
						
							|  |  |  |       number STRING, | 
					
						
							|  |  |  |       json TEXT | 
					
						
							|  |  |  |     );`
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     ); | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     await instance.run(`CREATE INDEX sessions_number ON sessions (
 | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |     number | 
					
						
							|  |  |  |   ) WHERE number IS NOT NULL;`);
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     // key-value, ids are strings
 | 
					
						
							|  |  |  |     await instance.run( | 
					
						
							|  |  |  |       `CREATE TABLE groups(
 | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |       id STRING PRIMARY KEY ASC, | 
					
						
							|  |  |  |       json TEXT | 
					
						
							|  |  |  |     );`
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     ); | 
					
						
							|  |  |  |     await instance.run( | 
					
						
							|  |  |  |       `CREATE TABLE identityKeys(
 | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |       id STRING PRIMARY KEY ASC, | 
					
						
							|  |  |  |       json TEXT | 
					
						
							|  |  |  |     );`
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     ); | 
					
						
							|  |  |  |     await instance.run( | 
					
						
							|  |  |  |       `CREATE TABLE items(
 | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |       id STRING PRIMARY KEY ASC, | 
					
						
							|  |  |  |       json TEXT | 
					
						
							|  |  |  |     );`
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     ); | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     // key-value, ids are integers
 | 
					
						
							|  |  |  |     await instance.run( | 
					
						
							|  |  |  |       `CREATE TABLE preKeys(
 | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |       id INTEGER PRIMARY KEY ASC, | 
					
						
							|  |  |  |       json TEXT | 
					
						
							|  |  |  |     );`
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     ); | 
					
						
							|  |  |  |     await instance.run( | 
					
						
							|  |  |  |       `CREATE TABLE signedPreKeys(
 | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |       id INTEGER PRIMARY KEY ASC, | 
					
						
							|  |  |  |       json TEXT | 
					
						
							|  |  |  |     );`
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     ); | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-20 06:24:43 -07:00
										 |  |  |     await instance.run('PRAGMA user_version = 6;'); | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     await instance.run('COMMIT TRANSACTION;'); | 
					
						
							|  |  |  |     console.log('updateToSchemaVersion6: success!'); | 
					
						
							|  |  |  |   } catch (error) { | 
					
						
							|  |  |  |     await instance.run('ROLLBACK;'); | 
					
						
							|  |  |  |     throw error; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function updateToSchemaVersion7( | 
					
						
							|  |  |  |   currentVersion: number, | 
					
						
							|  |  |  |   instance: PromisifiedSQLDatabase | 
					
						
							|  |  |  | ) { | 
					
						
							| 
									
										
										
										
											2018-11-19 17:38:55 -08:00
										 |  |  |   if (currentVersion >= 7) { | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   console.log('updateToSchemaVersion7: starting...'); | 
					
						
							|  |  |  |   await instance.run('BEGIN TRANSACTION;'); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |   try { | 
					
						
							|  |  |  |     // SQLite has been coercing our STRINGs into numbers, so we force it with TEXT
 | 
					
						
							|  |  |  |     // We create a new table then copy the data into it, since we can't modify columns
 | 
					
						
							| 
									
										
										
										
											2018-11-19 17:38:55 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     await instance.run('DROP INDEX sessions_number;'); | 
					
						
							|  |  |  |     await instance.run('ALTER TABLE sessions RENAME TO sessions_old;'); | 
					
						
							| 
									
										
										
										
											2018-11-19 17:38:55 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     await instance.run( | 
					
						
							|  |  |  |       `CREATE TABLE sessions(
 | 
					
						
							| 
									
										
										
										
											2021-01-13 08:32:18 -08:00
										 |  |  |         id TEXT PRIMARY KEY, | 
					
						
							|  |  |  |         number TEXT, | 
					
						
							|  |  |  |         json TEXT | 
					
						
							|  |  |  |       );`
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     ); | 
					
						
							| 
									
										
										
										
											2018-11-19 17:38:55 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     await instance.run(`CREATE INDEX sessions_number ON sessions (
 | 
					
						
							| 
									
										
										
										
											2021-01-13 08:32:18 -08:00
										 |  |  |       number | 
					
						
							|  |  |  |     ) WHERE number IS NOT NULL;`);
 | 
					
						
							| 
									
										
										
										
											2018-11-19 17:38:55 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     await instance.run(`INSERT INTO sessions(id, number, json)
 | 
					
						
							| 
									
										
										
										
											2021-01-13 08:32:18 -08:00
										 |  |  |       SELECT "+" || id, number, json FROM sessions_old; | 
					
						
							|  |  |  |     `);
 | 
					
						
							| 
									
										
										
										
											2018-11-19 17:38:55 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     await instance.run('DROP TABLE sessions_old;'); | 
					
						
							| 
									
										
										
										
											2018-11-19 17:38:55 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-20 06:24:43 -07:00
										 |  |  |     await instance.run('PRAGMA user_version = 7;'); | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     await instance.run('COMMIT TRANSACTION;'); | 
					
						
							|  |  |  |     console.log('updateToSchemaVersion7: success!'); | 
					
						
							|  |  |  |   } catch (error) { | 
					
						
							|  |  |  |     await instance.run('ROLLBACK;'); | 
					
						
							|  |  |  |     throw error; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-11-19 17:38:55 -08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function updateToSchemaVersion8( | 
					
						
							|  |  |  |   currentVersion: number, | 
					
						
							|  |  |  |   instance: PromisifiedSQLDatabase | 
					
						
							|  |  |  | ) { | 
					
						
							| 
									
										
										
										
											2019-01-14 13:47:19 -08:00
										 |  |  |   if (currentVersion >= 8) { | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   console.log('updateToSchemaVersion8: starting...'); | 
					
						
							|  |  |  |   await instance.run('BEGIN TRANSACTION;'); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |   try { | 
					
						
							|  |  |  |     // First, we pull a new body field out of the message table's json blob
 | 
					
						
							|  |  |  |     await instance.run( | 
					
						
							|  |  |  |       `ALTER TABLE messages
 | 
					
						
							| 
									
										
										
										
											2019-01-14 13:47:19 -08:00
										 |  |  |      ADD COLUMN body TEXT;`
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     ); | 
					
						
							|  |  |  |     await instance.run( | 
					
						
							|  |  |  |       "UPDATE messages SET body = json_extract(json, '$.body')" | 
					
						
							|  |  |  |     ); | 
					
						
							| 
									
										
										
										
											2019-01-14 13:47:19 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     // Then we create our full-text search table and populate it
 | 
					
						
							|  |  |  |     await instance.run(`
 | 
					
						
							| 
									
										
										
										
											2021-01-13 08:32:18 -08:00
										 |  |  |       CREATE VIRTUAL TABLE messages_fts | 
					
						
							|  |  |  |       USING fts5(id UNINDEXED, body); | 
					
						
							|  |  |  |     `);
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     await instance.run(`
 | 
					
						
							| 
									
										
										
										
											2021-01-13 08:32:18 -08:00
										 |  |  |       INSERT INTO messages_fts(id, body) | 
					
						
							|  |  |  |       SELECT id, body FROM messages; | 
					
						
							|  |  |  |     `);
 | 
					
						
							| 
									
										
										
										
											2019-01-14 13:47:19 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     // Then we set up triggers to keep the full-text search table up to date
 | 
					
						
							|  |  |  |     await instance.run(`
 | 
					
						
							| 
									
										
										
										
											2021-01-13 08:32:18 -08:00
										 |  |  |       CREATE TRIGGER messages_on_insert AFTER INSERT ON messages BEGIN | 
					
						
							|  |  |  |         INSERT INTO messages_fts ( | 
					
						
							|  |  |  |           id, | 
					
						
							|  |  |  |           body | 
					
						
							|  |  |  |         ) VALUES ( | 
					
						
							|  |  |  |           new.id, | 
					
						
							|  |  |  |           new.body | 
					
						
							|  |  |  |         ); | 
					
						
							|  |  |  |       END; | 
					
						
							|  |  |  |     `);
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     await instance.run(`
 | 
					
						
							| 
									
										
										
										
											2021-01-13 08:32:18 -08:00
										 |  |  |       CREATE TRIGGER messages_on_delete AFTER DELETE ON messages BEGIN | 
					
						
							|  |  |  |         DELETE FROM messages_fts WHERE id = old.id; | 
					
						
							|  |  |  |       END; | 
					
						
							|  |  |  |     `);
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     await instance.run(`
 | 
					
						
							| 
									
										
										
										
											2021-01-13 08:32:18 -08:00
										 |  |  |       CREATE TRIGGER messages_on_update AFTER UPDATE ON messages BEGIN | 
					
						
							|  |  |  |         DELETE FROM messages_fts WHERE id = old.id; | 
					
						
							|  |  |  |         INSERT INTO messages_fts( | 
					
						
							|  |  |  |           id, | 
					
						
							|  |  |  |           body | 
					
						
							|  |  |  |         ) VALUES ( | 
					
						
							|  |  |  |           new.id, | 
					
						
							|  |  |  |           new.body | 
					
						
							|  |  |  |         ); | 
					
						
							|  |  |  |       END; | 
					
						
							|  |  |  |     `);
 | 
					
						
							| 
									
										
										
										
											2019-01-14 13:47:19 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     // For formatting search results:
 | 
					
						
							|  |  |  |     //   https://sqlite.org/fts5.html#the_highlight_function
 | 
					
						
							|  |  |  |     //   https://sqlite.org/fts5.html#the_snippet_function
 | 
					
						
							| 
									
										
										
										
											2019-01-14 13:47:19 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-20 06:24:43 -07:00
										 |  |  |     await instance.run('PRAGMA user_version = 8;'); | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     await instance.run('COMMIT TRANSACTION;'); | 
					
						
							|  |  |  |     console.log('updateToSchemaVersion8: success!'); | 
					
						
							|  |  |  |   } catch (error) { | 
					
						
							|  |  |  |     await instance.run('ROLLBACK;'); | 
					
						
							|  |  |  |     throw error; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2019-01-14 13:47:19 -08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function updateToSchemaVersion9( | 
					
						
							|  |  |  |   currentVersion: number, | 
					
						
							|  |  |  |   instance: PromisifiedSQLDatabase | 
					
						
							|  |  |  | ) { | 
					
						
							| 
									
										
										
										
											2019-01-30 12:15:07 -08:00
										 |  |  |   if (currentVersion >= 9) { | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   console.log('updateToSchemaVersion9: starting...'); | 
					
						
							|  |  |  |   await instance.run('BEGIN TRANSACTION;'); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |   try { | 
					
						
							|  |  |  |     await instance.run(`CREATE TABLE attachment_downloads(
 | 
					
						
							| 
									
										
										
										
											2021-01-13 08:32:18 -08:00
										 |  |  |       id STRING primary key, | 
					
						
							|  |  |  |       timestamp INTEGER, | 
					
						
							|  |  |  |       pending INTEGER, | 
					
						
							|  |  |  |       json TEXT | 
					
						
							|  |  |  |     );`);
 | 
					
						
							| 
									
										
										
										
											2019-01-30 12:15:07 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     await instance.run(`CREATE INDEX attachment_downloads_timestamp
 | 
					
						
							| 
									
										
										
										
											2021-01-13 08:32:18 -08:00
										 |  |  |       ON attachment_downloads ( | 
					
						
							|  |  |  |         timestamp | 
					
						
							|  |  |  |     ) WHERE pending = 0;`);
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     await instance.run(`CREATE INDEX attachment_downloads_pending
 | 
					
						
							| 
									
										
										
										
											2021-01-13 08:32:18 -08:00
										 |  |  |       ON attachment_downloads ( | 
					
						
							|  |  |  |         pending | 
					
						
							|  |  |  |     ) WHERE pending != 0;`);
 | 
					
						
							| 
									
										
										
										
											2019-01-30 12:15:07 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-20 06:24:43 -07:00
										 |  |  |     await instance.run('PRAGMA user_version = 9;'); | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     await instance.run('COMMIT TRANSACTION;'); | 
					
						
							|  |  |  |     console.log('updateToSchemaVersion9: success!'); | 
					
						
							|  |  |  |   } catch (error) { | 
					
						
							|  |  |  |     await instance.run('ROLLBACK;'); | 
					
						
							|  |  |  |     throw error; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2019-01-30 12:15:07 -08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function updateToSchemaVersion10( | 
					
						
							|  |  |  |   currentVersion: number, | 
					
						
							|  |  |  |   instance: PromisifiedSQLDatabase | 
					
						
							|  |  |  | ) { | 
					
						
							| 
									
										
										
										
											2019-02-04 17:23:50 -08:00
										 |  |  |   if (currentVersion >= 10) { | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   console.log('updateToSchemaVersion10: starting...'); | 
					
						
							|  |  |  |   await instance.run('BEGIN TRANSACTION;'); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |   try { | 
					
						
							|  |  |  |     await instance.run('DROP INDEX unprocessed_id;'); | 
					
						
							|  |  |  |     await instance.run('DROP INDEX unprocessed_timestamp;'); | 
					
						
							|  |  |  |     await instance.run('ALTER TABLE unprocessed RENAME TO unprocessed_old;'); | 
					
						
							| 
									
										
										
										
											2019-02-04 17:23:50 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     await instance.run(`CREATE TABLE unprocessed(
 | 
					
						
							| 
									
										
										
										
											2021-01-13 08:32:18 -08:00
										 |  |  |       id STRING, | 
					
						
							|  |  |  |       timestamp INTEGER, | 
					
						
							|  |  |  |       version INTEGER, | 
					
						
							|  |  |  |       attempts INTEGER, | 
					
						
							|  |  |  |       envelope TEXT, | 
					
						
							|  |  |  |       decrypted TEXT, | 
					
						
							|  |  |  |       source TEXT, | 
					
						
							|  |  |  |       sourceDevice TEXT, | 
					
						
							|  |  |  |       serverTimestamp INTEGER | 
					
						
							|  |  |  |     );`);
 | 
					
						
							| 
									
										
										
										
											2019-02-04 17:23:50 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     await instance.run(`CREATE INDEX unprocessed_id ON unprocessed (
 | 
					
						
							| 
									
										
										
										
											2021-01-13 08:32:18 -08:00
										 |  |  |       id | 
					
						
							|  |  |  |     );`);
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     await instance.run(`CREATE INDEX unprocessed_timestamp ON unprocessed (
 | 
					
						
							| 
									
										
										
										
											2021-01-13 08:32:18 -08:00
										 |  |  |       timestamp | 
					
						
							|  |  |  |     );`);
 | 
					
						
							| 
									
										
										
										
											2019-02-04 17:23:50 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     await instance.run(`INSERT INTO unprocessed (
 | 
					
						
							| 
									
										
										
										
											2021-01-13 08:32:18 -08:00
										 |  |  |       id, | 
					
						
							|  |  |  |       timestamp, | 
					
						
							|  |  |  |       version, | 
					
						
							|  |  |  |       attempts, | 
					
						
							|  |  |  |       envelope, | 
					
						
							|  |  |  |       decrypted, | 
					
						
							|  |  |  |       source, | 
					
						
							|  |  |  |       sourceDevice, | 
					
						
							|  |  |  |       serverTimestamp | 
					
						
							|  |  |  |     ) SELECT | 
					
						
							|  |  |  |       id, | 
					
						
							|  |  |  |       timestamp, | 
					
						
							|  |  |  |       json_extract(json, '$.version'), | 
					
						
							|  |  |  |       json_extract(json, '$.attempts'), | 
					
						
							|  |  |  |       json_extract(json, '$.envelope'), | 
					
						
							|  |  |  |       json_extract(json, '$.decrypted'), | 
					
						
							|  |  |  |       json_extract(json, '$.source'), | 
					
						
							|  |  |  |       json_extract(json, '$.sourceDevice'), | 
					
						
							|  |  |  |       json_extract(json, '$.serverTimestamp') | 
					
						
							|  |  |  |     FROM unprocessed_old; | 
					
						
							|  |  |  |     `);
 | 
					
						
							| 
									
										
										
										
											2019-02-04 17:23:50 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     await instance.run('DROP TABLE unprocessed_old;'); | 
					
						
							| 
									
										
										
										
											2019-02-04 17:23:50 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-20 06:24:43 -07:00
										 |  |  |     await instance.run('PRAGMA user_version = 10;'); | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     await instance.run('COMMIT TRANSACTION;'); | 
					
						
							|  |  |  |     console.log('updateToSchemaVersion10: success!'); | 
					
						
							|  |  |  |   } catch (error) { | 
					
						
							|  |  |  |     await instance.run('ROLLBACK;'); | 
					
						
							|  |  |  |     throw error; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2019-02-04 17:23:50 -08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function updateToSchemaVersion11( | 
					
						
							|  |  |  |   currentVersion: number, | 
					
						
							|  |  |  |   instance: PromisifiedSQLDatabase | 
					
						
							|  |  |  | ) { | 
					
						
							| 
									
										
										
										
											2019-02-11 15:59:21 -08:00
										 |  |  |   if (currentVersion >= 11) { | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   console.log('updateToSchemaVersion11: starting...'); | 
					
						
							|  |  |  |   await instance.run('BEGIN TRANSACTION;'); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |   try { | 
					
						
							|  |  |  |     await instance.run('DROP TABLE groups;'); | 
					
						
							| 
									
										
										
										
											2019-02-11 15:59:21 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-20 06:24:43 -07:00
										 |  |  |     await instance.run('PRAGMA user_version = 11;'); | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     await instance.run('COMMIT TRANSACTION;'); | 
					
						
							|  |  |  |     console.log('updateToSchemaVersion11: success!'); | 
					
						
							|  |  |  |   } catch (error) { | 
					
						
							|  |  |  |     await instance.run('ROLLBACK;'); | 
					
						
							|  |  |  |     throw error; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2019-02-11 15:59:21 -08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function updateToSchemaVersion12( | 
					
						
							|  |  |  |   currentVersion: number, | 
					
						
							|  |  |  |   instance: PromisifiedSQLDatabase | 
					
						
							|  |  |  | ) { | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  |   if (currentVersion >= 12) { | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   console.log('updateToSchemaVersion12: starting...'); | 
					
						
							|  |  |  |   await instance.run('BEGIN TRANSACTION;'); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |   try { | 
					
						
							|  |  |  |     await instance.run(`CREATE TABLE sticker_packs(
 | 
					
						
							| 
									
										
										
										
											2021-01-12 16:42:15 -08:00
										 |  |  |       id TEXT PRIMARY KEY, | 
					
						
							|  |  |  |       key TEXT NOT NULL, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       author STRING, | 
					
						
							|  |  |  |       coverStickerId INTEGER, | 
					
						
							|  |  |  |       createdAt INTEGER, | 
					
						
							|  |  |  |       downloadAttempts INTEGER, | 
					
						
							|  |  |  |       installedAt INTEGER, | 
					
						
							|  |  |  |       lastUsed INTEGER, | 
					
						
							|  |  |  |       status STRING, | 
					
						
							|  |  |  |       stickerCount INTEGER, | 
					
						
							|  |  |  |       title STRING | 
					
						
							|  |  |  |     );`);
 | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     await instance.run(`CREATE TABLE stickers(
 | 
					
						
							| 
									
										
										
										
											2021-01-12 16:42:15 -08:00
										 |  |  |       id INTEGER NOT NULL, | 
					
						
							|  |  |  |       packId TEXT NOT NULL, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       emoji STRING, | 
					
						
							|  |  |  |       height INTEGER, | 
					
						
							|  |  |  |       isCoverOnly INTEGER, | 
					
						
							|  |  |  |       lastUsed INTEGER, | 
					
						
							|  |  |  |       path STRING, | 
					
						
							|  |  |  |       width INTEGER, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       PRIMARY KEY (id, packId), | 
					
						
							|  |  |  |       CONSTRAINT stickers_fk | 
					
						
							|  |  |  |         FOREIGN KEY (packId) | 
					
						
							|  |  |  |         REFERENCES sticker_packs(id) | 
					
						
							|  |  |  |         ON DELETE CASCADE | 
					
						
							|  |  |  |     );`);
 | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     await instance.run(`CREATE INDEX stickers_recents
 | 
					
						
							| 
									
										
										
										
											2021-01-12 16:42:15 -08:00
										 |  |  |       ON stickers ( | 
					
						
							|  |  |  |         lastUsed | 
					
						
							|  |  |  |     ) WHERE lastUsed IS NOT NULL;`);
 | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     await instance.run(`CREATE TABLE sticker_references(
 | 
					
						
							| 
									
										
										
										
											2021-01-12 16:42:15 -08:00
										 |  |  |       messageId STRING, | 
					
						
							|  |  |  |       packId TEXT, | 
					
						
							|  |  |  |       CONSTRAINT sticker_references_fk | 
					
						
							|  |  |  |         FOREIGN KEY(packId) | 
					
						
							|  |  |  |         REFERENCES sticker_packs(id) | 
					
						
							|  |  |  |         ON DELETE CASCADE | 
					
						
							|  |  |  |     );`);
 | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-20 06:24:43 -07:00
										 |  |  |     await instance.run('PRAGMA user_version = 12;'); | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     await instance.run('COMMIT TRANSACTION;'); | 
					
						
							|  |  |  |     console.log('updateToSchemaVersion12: success!'); | 
					
						
							|  |  |  |   } catch (error) { | 
					
						
							|  |  |  |     await instance.run('ROLLBACK;'); | 
					
						
							|  |  |  |     throw error; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function updateToSchemaVersion13( | 
					
						
							|  |  |  |   currentVersion: number, | 
					
						
							|  |  |  |   instance: PromisifiedSQLDatabase | 
					
						
							|  |  |  | ) { | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  |   if (currentVersion >= 13) { | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   console.log('updateToSchemaVersion13: starting...'); | 
					
						
							|  |  |  |   await instance.run('BEGIN TRANSACTION;'); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |   try { | 
					
						
							|  |  |  |     await instance.run( | 
					
						
							|  |  |  |       'ALTER TABLE sticker_packs ADD COLUMN attemptedStatus STRING;' | 
					
						
							|  |  |  |     ); | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-20 06:24:43 -07:00
										 |  |  |     await instance.run('PRAGMA user_version = 13;'); | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     await instance.run('COMMIT TRANSACTION;'); | 
					
						
							|  |  |  |     console.log('updateToSchemaVersion13: success!'); | 
					
						
							|  |  |  |   } catch (error) { | 
					
						
							|  |  |  |     await instance.run('ROLLBACK;'); | 
					
						
							|  |  |  |     throw error; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function updateToSchemaVersion14( | 
					
						
							|  |  |  |   currentVersion: number, | 
					
						
							|  |  |  |   instance: PromisifiedSQLDatabase | 
					
						
							|  |  |  | ) { | 
					
						
							| 
									
										
										
										
											2019-05-24 16:58:27 -07:00
										 |  |  |   if (currentVersion >= 14) { | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   console.log('updateToSchemaVersion14: starting...'); | 
					
						
							|  |  |  |   await instance.run('BEGIN TRANSACTION;'); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |   try { | 
					
						
							|  |  |  |     await instance.run(`CREATE TABLE emojis(
 | 
					
						
							| 
									
										
										
										
											2021-01-13 08:32:18 -08:00
										 |  |  |       shortName STRING PRIMARY KEY, | 
					
						
							|  |  |  |       lastUsage INTEGER | 
					
						
							|  |  |  |     );`);
 | 
					
						
							| 
									
										
										
										
											2019-05-24 16:58:27 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     await instance.run(`CREATE INDEX emojis_lastUsage
 | 
					
						
							| 
									
										
										
										
											2021-01-13 08:32:18 -08:00
										 |  |  |       ON emojis ( | 
					
						
							|  |  |  |         lastUsage | 
					
						
							|  |  |  |     );`);
 | 
					
						
							| 
									
										
										
										
											2019-05-24 16:58:27 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-20 06:24:43 -07:00
										 |  |  |     await instance.run('PRAGMA user_version = 14;'); | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     await instance.run('COMMIT TRANSACTION;'); | 
					
						
							|  |  |  |     console.log('updateToSchemaVersion14: success!'); | 
					
						
							|  |  |  |   } catch (error) { | 
					
						
							|  |  |  |     await instance.run('ROLLBACK;'); | 
					
						
							|  |  |  |     throw error; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2019-05-24 16:58:27 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function updateToSchemaVersion15( | 
					
						
							|  |  |  |   currentVersion: number, | 
					
						
							|  |  |  |   instance: PromisifiedSQLDatabase | 
					
						
							|  |  |  | ) { | 
					
						
							| 
									
										
										
										
											2019-05-29 17:56:29 -07:00
										 |  |  |   if (currentVersion >= 15) { | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   console.log('updateToSchemaVersion15: starting...'); | 
					
						
							|  |  |  |   await instance.run('BEGIN TRANSACTION;'); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |   try { | 
					
						
							|  |  |  |     // SQLite has again coerced our STRINGs into numbers, so we force it with TEXT
 | 
					
						
							|  |  |  |     // We create a new table then copy the data into it, since we can't modify columns
 | 
					
						
							| 
									
										
										
										
											2019-05-29 17:56:29 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     await instance.run('DROP INDEX emojis_lastUsage;'); | 
					
						
							|  |  |  |     await instance.run('ALTER TABLE emojis RENAME TO emojis_old;'); | 
					
						
							| 
									
										
										
										
											2019-05-29 17:56:29 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     await instance.run(`CREATE TABLE emojis(
 | 
					
						
							| 
									
										
										
										
											2020-01-13 14:28:28 -08:00
										 |  |  |       shortName TEXT PRIMARY KEY, | 
					
						
							|  |  |  |       lastUsage INTEGER | 
					
						
							|  |  |  |     );`);
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     await instance.run(`CREATE INDEX emojis_lastUsage
 | 
					
						
							| 
									
										
										
										
											2020-01-13 14:28:28 -08:00
										 |  |  |       ON emojis ( | 
					
						
							|  |  |  |         lastUsage | 
					
						
							|  |  |  |     );`);
 | 
					
						
							| 
									
										
										
										
											2019-05-29 17:56:29 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     await instance.run('DELETE FROM emojis WHERE shortName = 1'); | 
					
						
							|  |  |  |     await instance.run(`INSERT INTO emojis(shortName, lastUsage)
 | 
					
						
							| 
									
										
										
										
											2020-01-13 14:28:28 -08:00
										 |  |  |       SELECT shortName, lastUsage FROM emojis_old; | 
					
						
							|  |  |  |     `);
 | 
					
						
							| 
									
										
										
										
											2019-05-29 17:56:29 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     await instance.run('DROP TABLE emojis_old;'); | 
					
						
							| 
									
										
										
										
											2019-05-29 17:56:29 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-20 06:24:43 -07:00
										 |  |  |     await instance.run('PRAGMA user_version = 15;'); | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     await instance.run('COMMIT TRANSACTION;'); | 
					
						
							|  |  |  |     console.log('updateToSchemaVersion15: success!'); | 
					
						
							|  |  |  |   } catch (error) { | 
					
						
							|  |  |  |     await instance.run('ROLLBACK;'); | 
					
						
							|  |  |  |     throw error; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2019-05-29 17:56:29 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function updateToSchemaVersion16( | 
					
						
							|  |  |  |   currentVersion: number, | 
					
						
							|  |  |  |   instance: PromisifiedSQLDatabase | 
					
						
							|  |  |  | ) { | 
					
						
							| 
									
										
										
										
											2019-06-26 12:33:13 -07:00
										 |  |  |   if (currentVersion >= 16) { | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   console.log('updateToSchemaVersion16: starting...'); | 
					
						
							|  |  |  |   await instance.run('BEGIN TRANSACTION;'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   try { | 
					
						
							|  |  |  |     await instance.run( | 
					
						
							|  |  |  |       `ALTER TABLE messages
 | 
					
						
							|  |  |  |       ADD COLUMN messageTimer INTEGER;`
 | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |     await instance.run( | 
					
						
							|  |  |  |       `ALTER TABLE messages
 | 
					
						
							|  |  |  |       ADD COLUMN messageTimerStart INTEGER;`
 | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |     await instance.run( | 
					
						
							|  |  |  |       `ALTER TABLE messages
 | 
					
						
							|  |  |  |       ADD COLUMN messageTimerExpiresAt INTEGER;`
 | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |     await instance.run( | 
					
						
							|  |  |  |       `ALTER TABLE messages
 | 
					
						
							|  |  |  |       ADD COLUMN isErased INTEGER;`
 | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     await instance.run(`CREATE INDEX messages_message_timer ON messages (
 | 
					
						
							|  |  |  |       messageTimer, | 
					
						
							|  |  |  |       messageTimerStart, | 
					
						
							|  |  |  |       messageTimerExpiresAt, | 
					
						
							|  |  |  |       isErased | 
					
						
							|  |  |  |     ) WHERE messageTimer IS NOT NULL;`);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Updating full-text triggers to avoid anything with a messageTimer set
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     await instance.run('DROP TRIGGER messages_on_insert;'); | 
					
						
							|  |  |  |     await instance.run('DROP TRIGGER messages_on_delete;'); | 
					
						
							|  |  |  |     await instance.run('DROP TRIGGER messages_on_update;'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     await instance.run(`
 | 
					
						
							|  |  |  |       CREATE TRIGGER messages_on_insert AFTER INSERT ON messages | 
					
						
							|  |  |  |       WHEN new.messageTimer IS NULL | 
					
						
							|  |  |  |       BEGIN | 
					
						
							|  |  |  |         INSERT INTO messages_fts ( | 
					
						
							|  |  |  |           id, | 
					
						
							|  |  |  |           body | 
					
						
							|  |  |  |         ) VALUES ( | 
					
						
							|  |  |  |           new.id, | 
					
						
							|  |  |  |           new.body | 
					
						
							|  |  |  |         ); | 
					
						
							|  |  |  |       END; | 
					
						
							|  |  |  |     `);
 | 
					
						
							|  |  |  |     await instance.run(`
 | 
					
						
							|  |  |  |       CREATE TRIGGER messages_on_delete AFTER DELETE ON messages BEGIN | 
					
						
							|  |  |  |         DELETE FROM messages_fts WHERE id = old.id; | 
					
						
							|  |  |  |       END; | 
					
						
							|  |  |  |     `);
 | 
					
						
							|  |  |  |     await instance.run(`
 | 
					
						
							|  |  |  |       CREATE TRIGGER messages_on_update AFTER UPDATE ON messages | 
					
						
							|  |  |  |       WHEN new.messageTimer IS NULL | 
					
						
							|  |  |  |       BEGIN | 
					
						
							|  |  |  |         DELETE FROM messages_fts WHERE id = old.id; | 
					
						
							|  |  |  |         INSERT INTO messages_fts( | 
					
						
							|  |  |  |           id, | 
					
						
							|  |  |  |           body | 
					
						
							|  |  |  |         ) VALUES ( | 
					
						
							|  |  |  |           new.id, | 
					
						
							|  |  |  |           new.body | 
					
						
							|  |  |  |         ); | 
					
						
							|  |  |  |       END; | 
					
						
							|  |  |  |     `);
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-20 06:24:43 -07:00
										 |  |  |     await instance.run('PRAGMA user_version = 16;'); | 
					
						
							| 
									
										
										
										
											2019-06-26 12:33:13 -07:00
										 |  |  |     await instance.run('COMMIT TRANSACTION;'); | 
					
						
							|  |  |  |     console.log('updateToSchemaVersion16: success!'); | 
					
						
							|  |  |  |   } catch (error) { | 
					
						
							|  |  |  |     await instance.run('ROLLBACK;'); | 
					
						
							|  |  |  |     throw error; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function updateToSchemaVersion17( | 
					
						
							|  |  |  |   currentVersion: number, | 
					
						
							|  |  |  |   instance: PromisifiedSQLDatabase | 
					
						
							|  |  |  | ) { | 
					
						
							| 
									
										
										
										
											2019-08-05 13:53:15 -07:00
										 |  |  |   if (currentVersion >= 17) { | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   console.log('updateToSchemaVersion17: starting...'); | 
					
						
							|  |  |  |   await instance.run('BEGIN TRANSACTION;'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   try { | 
					
						
							| 
									
										
										
										
											2019-08-20 06:24:43 -07:00
										 |  |  |     try { | 
					
						
							|  |  |  |       await instance.run( | 
					
						
							|  |  |  |         `ALTER TABLE messages
 | 
					
						
							|  |  |  |         ADD COLUMN isViewOnce INTEGER;`
 | 
					
						
							|  |  |  |       ); | 
					
						
							| 
									
										
										
										
											2019-08-05 13:53:15 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-20 06:24:43 -07:00
										 |  |  |       await instance.run('DROP INDEX messages_message_timer;'); | 
					
						
							|  |  |  |     } catch (error) { | 
					
						
							|  |  |  |       console.log( | 
					
						
							|  |  |  |         'updateToSchemaVersion17: Message table already had isViewOnce column' | 
					
						
							|  |  |  |       ); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-08-05 13:53:15 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-20 06:24:43 -07:00
										 |  |  |     try { | 
					
						
							|  |  |  |       await instance.run('DROP INDEX messages_view_once;'); | 
					
						
							|  |  |  |     } catch (error) { | 
					
						
							|  |  |  |       console.log( | 
					
						
							|  |  |  |         'updateToSchemaVersion17: Index messages_view_once did not already exist' | 
					
						
							|  |  |  |       ); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-08-05 13:53:15 -07:00
										 |  |  |     await instance.run(`CREATE INDEX messages_view_once ON messages (
 | 
					
						
							|  |  |  |       isErased | 
					
						
							|  |  |  |     ) WHERE isViewOnce = 1;`);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Updating full-text triggers to avoid anything with isViewOnce = 1
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     await instance.run('DROP TRIGGER messages_on_insert;'); | 
					
						
							|  |  |  |     await instance.run('DROP TRIGGER messages_on_update;'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     await instance.run(`
 | 
					
						
							|  |  |  |       CREATE TRIGGER messages_on_insert AFTER INSERT ON messages | 
					
						
							|  |  |  |       WHEN new.isViewOnce != 1 | 
					
						
							|  |  |  |       BEGIN | 
					
						
							|  |  |  |         INSERT INTO messages_fts ( | 
					
						
							|  |  |  |           id, | 
					
						
							|  |  |  |           body | 
					
						
							|  |  |  |         ) VALUES ( | 
					
						
							|  |  |  |           new.id, | 
					
						
							|  |  |  |           new.body | 
					
						
							|  |  |  |         ); | 
					
						
							|  |  |  |       END; | 
					
						
							|  |  |  |     `);
 | 
					
						
							|  |  |  |     await instance.run(`
 | 
					
						
							|  |  |  |       CREATE TRIGGER messages_on_update AFTER UPDATE ON messages | 
					
						
							|  |  |  |       WHEN new.isViewOnce != 1 | 
					
						
							|  |  |  |       BEGIN | 
					
						
							|  |  |  |         DELETE FROM messages_fts WHERE id = old.id; | 
					
						
							|  |  |  |         INSERT INTO messages_fts( | 
					
						
							|  |  |  |           id, | 
					
						
							|  |  |  |           body | 
					
						
							|  |  |  |         ) VALUES ( | 
					
						
							|  |  |  |           new.id, | 
					
						
							|  |  |  |           new.body | 
					
						
							|  |  |  |         ); | 
					
						
							|  |  |  |       END; | 
					
						
							|  |  |  |     `);
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-20 06:24:43 -07:00
										 |  |  |     await instance.run('PRAGMA user_version = 17;'); | 
					
						
							| 
									
										
										
										
											2019-08-05 13:53:15 -07:00
										 |  |  |     await instance.run('COMMIT TRANSACTION;'); | 
					
						
							|  |  |  |     console.log('updateToSchemaVersion17: success!'); | 
					
						
							|  |  |  |   } catch (error) { | 
					
						
							|  |  |  |     await instance.run('ROLLBACK;'); | 
					
						
							|  |  |  |     throw error; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function updateToSchemaVersion18( | 
					
						
							|  |  |  |   currentVersion: number, | 
					
						
							|  |  |  |   instance: PromisifiedSQLDatabase | 
					
						
							|  |  |  | ) { | 
					
						
							| 
									
										
										
										
											2019-08-08 17:46:49 -07:00
										 |  |  |   if (currentVersion >= 18) { | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   console.log('updateToSchemaVersion18: starting...'); | 
					
						
							|  |  |  |   await instance.run('BEGIN TRANSACTION;'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   try { | 
					
						
							|  |  |  |     // Delete and rebuild full-text search index to capture everything
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     await instance.run('DELETE FROM messages_fts;'); | 
					
						
							|  |  |  |     await instance.run( | 
					
						
							|  |  |  |       "INSERT INTO messages_fts(messages_fts) VALUES('rebuild');" | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     await instance.run(`
 | 
					
						
							|  |  |  |       INSERT INTO messages_fts(id, body) | 
					
						
							|  |  |  |       SELECT id, body FROM messages WHERE isViewOnce IS NULL OR isViewOnce != 1; | 
					
						
							|  |  |  |     `);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Fixing full-text triggers
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     await instance.run('DROP TRIGGER messages_on_insert;'); | 
					
						
							|  |  |  |     await instance.run('DROP TRIGGER messages_on_update;'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     await instance.run(`
 | 
					
						
							|  |  |  |       CREATE TRIGGER messages_on_insert AFTER INSERT ON messages | 
					
						
							|  |  |  |       WHEN new.isViewOnce IS NULL OR new.isViewOnce != 1 | 
					
						
							|  |  |  |       BEGIN | 
					
						
							|  |  |  |         INSERT INTO messages_fts ( | 
					
						
							|  |  |  |           id, | 
					
						
							|  |  |  |           body | 
					
						
							|  |  |  |         ) VALUES ( | 
					
						
							|  |  |  |           new.id, | 
					
						
							|  |  |  |           new.body | 
					
						
							|  |  |  |         ); | 
					
						
							|  |  |  |       END; | 
					
						
							|  |  |  |     `);
 | 
					
						
							|  |  |  |     await instance.run(`
 | 
					
						
							|  |  |  |       CREATE TRIGGER messages_on_update AFTER UPDATE ON messages | 
					
						
							|  |  |  |       WHEN new.isViewOnce IS NULL OR new.isViewOnce != 1 | 
					
						
							|  |  |  |       BEGIN | 
					
						
							|  |  |  |         DELETE FROM messages_fts WHERE id = old.id; | 
					
						
							|  |  |  |         INSERT INTO messages_fts( | 
					
						
							|  |  |  |           id, | 
					
						
							|  |  |  |           body | 
					
						
							|  |  |  |         ) VALUES ( | 
					
						
							|  |  |  |           new.id, | 
					
						
							|  |  |  |           new.body | 
					
						
							|  |  |  |         ); | 
					
						
							|  |  |  |       END; | 
					
						
							|  |  |  |     `);
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-20 06:24:43 -07:00
										 |  |  |     await instance.run('PRAGMA user_version = 18;'); | 
					
						
							| 
									
										
										
										
											2019-08-08 17:46:49 -07:00
										 |  |  |     await instance.run('COMMIT TRANSACTION;'); | 
					
						
							|  |  |  |     console.log('updateToSchemaVersion18: success!'); | 
					
						
							|  |  |  |   } catch (error) { | 
					
						
							|  |  |  |     await instance.run('ROLLBACK;'); | 
					
						
							|  |  |  |     throw error; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2020-03-05 13:14:58 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function updateToSchemaVersion19( | 
					
						
							|  |  |  |   currentVersion: number, | 
					
						
							|  |  |  |   instance: PromisifiedSQLDatabase | 
					
						
							|  |  |  | ) { | 
					
						
							| 
									
										
										
										
											2020-01-13 14:28:28 -08:00
										 |  |  |   if (currentVersion >= 19) { | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   console.log('updateToSchemaVersion19: starting...'); | 
					
						
							|  |  |  |   await instance.run('BEGIN TRANSACTION;'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   await instance.run( | 
					
						
							|  |  |  |     `ALTER TABLE conversations
 | 
					
						
							|  |  |  |      ADD COLUMN profileFamilyName TEXT;`
 | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  |   await instance.run( | 
					
						
							|  |  |  |     `ALTER TABLE conversations
 | 
					
						
							|  |  |  |      ADD COLUMN profileFullName TEXT;`
 | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // Preload new field with the profileName we already have
 | 
					
						
							|  |  |  |   await instance.run('UPDATE conversations SET profileFullName = profileName'); | 
					
						
							| 
									
										
										
										
											2019-08-08 17:46:49 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-13 14:28:28 -08:00
										 |  |  |   try { | 
					
						
							|  |  |  |     await instance.run('PRAGMA user_version = 19;'); | 
					
						
							|  |  |  |     await instance.run('COMMIT TRANSACTION;'); | 
					
						
							|  |  |  |     console.log('updateToSchemaVersion19: success!'); | 
					
						
							|  |  |  |   } catch (error) { | 
					
						
							|  |  |  |     await instance.run('ROLLBACK;'); | 
					
						
							|  |  |  |     throw error; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2020-03-05 13:14:58 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function updateToSchemaVersion20( | 
					
						
							|  |  |  |   currentVersion: number, | 
					
						
							|  |  |  |   instance: PromisifiedSQLDatabase | 
					
						
							|  |  |  | ) { | 
					
						
							| 
									
										
										
										
											2020-03-05 13:14:58 -08:00
										 |  |  |   if (currentVersion >= 20) { | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   console.log('updateToSchemaVersion20: starting...'); | 
					
						
							|  |  |  |   await instance.run('BEGIN TRANSACTION;'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   try { | 
					
						
							| 
									
										
										
										
											2020-09-18 13:40:41 -07:00
										 |  |  |     const migrationJobQueue = new PQueue({ | 
					
						
							|  |  |  |       concurrency: 10, | 
					
						
							|  |  |  |       timeout: 1000 * 60 * 5, | 
					
						
							|  |  |  |       throwOnTimeout: true, | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2020-03-05 13:14:58 -08:00
										 |  |  |     // The triggers on the messages table slow down this migration
 | 
					
						
							|  |  |  |     // significantly, so we drop them and recreate them later.
 | 
					
						
							|  |  |  |     // Drop triggers
 | 
					
						
							|  |  |  |     const triggers = await instance.all( | 
					
						
							|  |  |  |       'SELECT * FROM sqlite_master WHERE type = "trigger" AND tbl_name = "messages"' | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for (const trigger of triggers) { | 
					
						
							|  |  |  |       await instance.run(`DROP TRIGGER ${trigger.name}`); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Create new columns and indices
 | 
					
						
							|  |  |  |     await instance.run('ALTER TABLE conversations ADD COLUMN e164 TEXT;'); | 
					
						
							|  |  |  |     await instance.run('ALTER TABLE conversations ADD COLUMN uuid TEXT;'); | 
					
						
							|  |  |  |     await instance.run('ALTER TABLE conversations ADD COLUMN groupId TEXT;'); | 
					
						
							|  |  |  |     await instance.run('ALTER TABLE messages ADD COLUMN sourceUuid TEXT;'); | 
					
						
							|  |  |  |     await instance.run( | 
					
						
							|  |  |  |       'ALTER TABLE sessions RENAME COLUMN number TO conversationId;' | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |     await instance.run( | 
					
						
							|  |  |  |       'CREATE INDEX conversations_e164 ON conversations(e164);' | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |     await instance.run( | 
					
						
							|  |  |  |       'CREATE INDEX conversations_uuid ON conversations(uuid);' | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |     await instance.run( | 
					
						
							|  |  |  |       'CREATE INDEX conversations_groupId ON conversations(groupId);' | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |     await instance.run( | 
					
						
							|  |  |  |       'CREATE INDEX messages_sourceUuid on messages(sourceUuid);' | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Migrate existing IDs
 | 
					
						
							|  |  |  |     await instance.run( | 
					
						
							|  |  |  |       "UPDATE conversations SET e164 = '+' || id WHERE type = 'private';" | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |     await instance.run( | 
					
						
							|  |  |  |       "UPDATE conversations SET groupId = id WHERE type = 'group';" | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Drop invalid groups and any associated messages
 | 
					
						
							|  |  |  |     const maybeInvalidGroups = await instance.all( | 
					
						
							|  |  |  |       "SELECT * FROM conversations WHERE type = 'group' AND members IS NULL;" | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |     for (const group of maybeInvalidGroups) { | 
					
						
							|  |  |  |       const json = JSON.parse(group.json); | 
					
						
							|  |  |  |       if (!json.members || !json.members.length) { | 
					
						
							|  |  |  |         await instance.run('DELETE FROM conversations WHERE id = $id;', { | 
					
						
							|  |  |  |           $id: json.id, | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |         await instance.run('DELETE FROM messages WHERE conversationId = $id;', { | 
					
						
							|  |  |  |           $id: json.id, | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |         // await instance.run('DELETE FROM sessions WHERE conversationId = $id;', {
 | 
					
						
							|  |  |  |         //   $id: json.id,
 | 
					
						
							|  |  |  |         // });
 | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Generate new IDs and alter data
 | 
					
						
							|  |  |  |     const allConversations = await instance.all('SELECT * FROM conversations;'); | 
					
						
							|  |  |  |     const allConversationsByOldId = keyBy(allConversations, 'id'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for (const row of allConversations) { | 
					
						
							|  |  |  |       const oldId = row.id; | 
					
						
							|  |  |  |       const newId = generateUUID(); | 
					
						
							|  |  |  |       allConversationsByOldId[oldId].id = newId; | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |       const patchObj: any = { id: newId }; | 
					
						
							| 
									
										
										
										
											2020-03-05 13:14:58 -08:00
										 |  |  |       if (row.type === 'private') { | 
					
						
							|  |  |  |         patchObj.e164 = `+${oldId}`; | 
					
						
							|  |  |  |       } else if (row.type === 'group') { | 
					
						
							|  |  |  |         patchObj.groupId = oldId; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       const patch = JSON.stringify(patchObj); | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-05 13:14:58 -08:00
										 |  |  |       await instance.run( | 
					
						
							|  |  |  |         'UPDATE conversations SET id = $newId, json = JSON_PATCH(json, $patch) WHERE id = $oldId', | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           $newId: newId, | 
					
						
							|  |  |  |           $oldId: oldId, | 
					
						
							|  |  |  |           $patch: patch, | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       ); | 
					
						
							|  |  |  |       const messagePatch = JSON.stringify({ conversationId: newId }); | 
					
						
							|  |  |  |       await instance.run( | 
					
						
							|  |  |  |         'UPDATE messages SET conversationId = $newId, json = JSON_PATCH(json, $patch) WHERE conversationId = $oldId', | 
					
						
							|  |  |  |         { $newId: newId, $oldId: oldId, $patch: messagePatch } | 
					
						
							|  |  |  |       ); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const groupConverations = await instance.all( | 
					
						
							|  |  |  |       "SELECT * FROM conversations WHERE type = 'group';" | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Update group conversations, point members at new conversation ids
 | 
					
						
							|  |  |  |     migrationJobQueue.addAll( | 
					
						
							|  |  |  |       groupConverations.map(groupRow => async () => { | 
					
						
							|  |  |  |         const members = groupRow.members.split(/\s?\+/).filter(Boolean); | 
					
						
							|  |  |  |         const newMembers = []; | 
					
						
							|  |  |  |         for (const m of members) { | 
					
						
							|  |  |  |           const memberRow = allConversationsByOldId[m]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           if (memberRow) { | 
					
						
							|  |  |  |             newMembers.push(memberRow.id); | 
					
						
							|  |  |  |           } else { | 
					
						
							|  |  |  |             // We didn't previously have a private conversation for this member,
 | 
					
						
							|  |  |  |             // we need to create one
 | 
					
						
							|  |  |  |             const id = generateUUID(); | 
					
						
							|  |  |  |             await saveConversation( | 
					
						
							|  |  |  |               { | 
					
						
							|  |  |  |                 id, | 
					
						
							|  |  |  |                 e164: m, | 
					
						
							|  |  |  |                 type: 'private', | 
					
						
							|  |  |  |                 version: 2, | 
					
						
							|  |  |  |                 unreadCount: 0, | 
					
						
							|  |  |  |                 verified: 0, | 
					
						
							|  |  |  |               }, | 
					
						
							|  |  |  |               instance | 
					
						
							|  |  |  |             ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             newMembers.push(id); | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         const json = { ...jsonToObject(groupRow.json), members: newMembers }; | 
					
						
							|  |  |  |         const newMembersValue = newMembers.join(' '); | 
					
						
							|  |  |  |         await instance.run( | 
					
						
							|  |  |  |           'UPDATE conversations SET members = $newMembersValue, json = $newJsonValue WHERE id = $id', | 
					
						
							|  |  |  |           { | 
					
						
							|  |  |  |             $id: groupRow.id, | 
					
						
							|  |  |  |             $newMembersValue: newMembersValue, | 
					
						
							|  |  |  |             $newJsonValue: objectToJSON(json), | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         ); | 
					
						
							|  |  |  |       }) | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |     // Wait for group conversation updates to finish
 | 
					
						
							|  |  |  |     await migrationJobQueue.onEmpty(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Update sessions to stable IDs
 | 
					
						
							|  |  |  |     const allSessions = await instance.all('SELECT * FROM sessions;'); | 
					
						
							|  |  |  |     for (const session of allSessions) { | 
					
						
							|  |  |  |       // Not using patch here so we can explicitly delete a property rather than
 | 
					
						
							|  |  |  |       // implicitly delete via null
 | 
					
						
							|  |  |  |       const newJson = JSON.parse(session.json); | 
					
						
							|  |  |  |       const conversation = allConversationsByOldId[newJson.number.substr(1)]; | 
					
						
							|  |  |  |       if (conversation) { | 
					
						
							|  |  |  |         newJson.conversationId = conversation.id; | 
					
						
							|  |  |  |         newJson.id = `${newJson.conversationId}.${newJson.deviceId}`; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       delete newJson.number; | 
					
						
							|  |  |  |       await instance.run( | 
					
						
							|  |  |  |         `
 | 
					
						
							|  |  |  |         UPDATE sessions | 
					
						
							|  |  |  |         SET id = $newId, json = $newJson, conversationId = $newConversationId | 
					
						
							|  |  |  |         WHERE id = $oldId | 
					
						
							|  |  |  |       `,
 | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           $newId: newJson.id, | 
					
						
							|  |  |  |           $newJson: objectToJSON(newJson), | 
					
						
							|  |  |  |           $oldId: session.id, | 
					
						
							|  |  |  |           $newConversationId: newJson.conversationId, | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       ); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Update identity keys to stable IDs
 | 
					
						
							|  |  |  |     const allIdentityKeys = await instance.all('SELECT * FROM identityKeys;'); | 
					
						
							|  |  |  |     for (const identityKey of allIdentityKeys) { | 
					
						
							|  |  |  |       const newJson = JSON.parse(identityKey.json); | 
					
						
							|  |  |  |       newJson.id = allConversationsByOldId[newJson.id]; | 
					
						
							|  |  |  |       await instance.run( | 
					
						
							|  |  |  |         `
 | 
					
						
							|  |  |  |         UPDATE identityKeys | 
					
						
							|  |  |  |         SET id = $newId, json = $newJson | 
					
						
							|  |  |  |         WHERE id = $oldId | 
					
						
							|  |  |  |       `,
 | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           $newId: newJson.id, | 
					
						
							|  |  |  |           $newJson: objectToJSON(newJson), | 
					
						
							|  |  |  |           $oldId: identityKey.id, | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       ); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Recreate triggers
 | 
					
						
							|  |  |  |     for (const trigger of triggers) { | 
					
						
							|  |  |  |       await instance.run(trigger.sql); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     await instance.run('PRAGMA user_version = 20;'); | 
					
						
							|  |  |  |     await instance.run('COMMIT TRANSACTION;'); | 
					
						
							| 
									
										
										
										
											2020-04-13 10:37:29 -07:00
										 |  |  |     console.log('updateToSchemaVersion20: success!'); | 
					
						
							| 
									
										
										
										
											2020-03-05 13:14:58 -08:00
										 |  |  |   } catch (error) { | 
					
						
							|  |  |  |     await instance.run('ROLLBACK;'); | 
					
						
							|  |  |  |     throw error; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-27 17:37:06 -04:00
										 |  |  | async function updateToSchemaVersion21( | 
					
						
							|  |  |  |   currentVersion: number, | 
					
						
							|  |  |  |   instance: PromisifiedSQLDatabase | 
					
						
							|  |  |  | ) { | 
					
						
							|  |  |  |   if (currentVersion >= 21) { | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   try { | 
					
						
							|  |  |  |     await instance.run('BEGIN TRANSACTION;'); | 
					
						
							|  |  |  |     await instance.run(`
 | 
					
						
							|  |  |  |       UPDATE conversations | 
					
						
							|  |  |  |       SET json = json_set( | 
					
						
							|  |  |  |         json, | 
					
						
							|  |  |  |         '$.messageCount', | 
					
						
							|  |  |  |         (SELECT count(*) FROM messages WHERE messages.conversationId = conversations.id) | 
					
						
							|  |  |  |       ); | 
					
						
							|  |  |  |     `);
 | 
					
						
							|  |  |  |     await instance.run(`
 | 
					
						
							|  |  |  |       UPDATE conversations | 
					
						
							|  |  |  |       SET json = json_set( | 
					
						
							|  |  |  |         json, | 
					
						
							|  |  |  |         '$.sentMessageCount', | 
					
						
							|  |  |  |         (SELECT count(*) FROM messages WHERE messages.conversationId = conversations.id AND messages.type = 'outgoing') | 
					
						
							|  |  |  |       ); | 
					
						
							|  |  |  |     `);
 | 
					
						
							|  |  |  |     await instance.run('PRAGMA user_version = 21;'); | 
					
						
							|  |  |  |     await instance.run('COMMIT TRANSACTION;'); | 
					
						
							|  |  |  |     console.log('updateToSchemaVersion21: success!'); | 
					
						
							|  |  |  |   } catch (error) { | 
					
						
							|  |  |  |     await instance.run('ROLLBACK'); | 
					
						
							|  |  |  |     throw error; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 11:43:10 -08:00
										 |  |  | async function updateToSchemaVersion22( | 
					
						
							|  |  |  |   currentVersion: number, | 
					
						
							|  |  |  |   instance: PromisifiedSQLDatabase | 
					
						
							|  |  |  | ) { | 
					
						
							|  |  |  |   if (currentVersion >= 22) { | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   try { | 
					
						
							|  |  |  |     await instance.run('BEGIN TRANSACTION;'); | 
					
						
							|  |  |  |     await instance.run( | 
					
						
							|  |  |  |       `ALTER TABLE unprocessed
 | 
					
						
							|  |  |  |      ADD COLUMN sourceUuid STRING;`
 | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     await instance.run('PRAGMA user_version = 22;'); | 
					
						
							|  |  |  |     await instance.run('COMMIT TRANSACTION;'); | 
					
						
							|  |  |  |     console.log('updateToSchemaVersion22: success!'); | 
					
						
							|  |  |  |   } catch (error) { | 
					
						
							|  |  |  |     await instance.run('ROLLBACK'); | 
					
						
							|  |  |  |     throw error; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 12:46:55 -08:00
										 |  |  | async function updateToSchemaVersion23( | 
					
						
							|  |  |  |   currentVersion: number, | 
					
						
							|  |  |  |   instance: PromisifiedSQLDatabase | 
					
						
							|  |  |  | ) { | 
					
						
							|  |  |  |   if (currentVersion >= 23) { | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   try { | 
					
						
							|  |  |  |     await instance.run('BEGIN TRANSACTION;'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Remove triggers which keep full-text search up to date
 | 
					
						
							|  |  |  |     await instance.run('DROP TRIGGER messages_on_insert;'); | 
					
						
							|  |  |  |     await instance.run('DROP TRIGGER messages_on_update;'); | 
					
						
							|  |  |  |     await instance.run('DROP TRIGGER messages_on_delete;'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     await instance.run('PRAGMA user_version = 23;'); | 
					
						
							|  |  |  |     await instance.run('COMMIT TRANSACTION;'); | 
					
						
							|  |  |  |     console.log('updateToSchemaVersion23: success!'); | 
					
						
							|  |  |  |   } catch (error) { | 
					
						
							|  |  |  |     await instance.run('ROLLBACK'); | 
					
						
							|  |  |  |     throw error; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-18 12:09:27 -05:00
										 |  |  | async function updateToSchemaVersion24( | 
					
						
							|  |  |  |   currentVersion: number, | 
					
						
							|  |  |  |   instance: PromisifiedSQLDatabase | 
					
						
							|  |  |  | ) { | 
					
						
							|  |  |  |   if (currentVersion >= 24) { | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   await instance.run('BEGIN TRANSACTION;'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   try { | 
					
						
							|  |  |  |     await instance.run(`
 | 
					
						
							|  |  |  |       ALTER TABLE conversations | 
					
						
							|  |  |  |       ADD COLUMN profileLastFetchedAt INTEGER; | 
					
						
							|  |  |  |     `);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     await instance.run('PRAGMA user_version = 24;'); | 
					
						
							|  |  |  |     await instance.run('COMMIT TRANSACTION;'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     console.log('updateToSchemaVersion24: success!'); | 
					
						
							|  |  |  |   } catch (error) { | 
					
						
							|  |  |  |     await instance.run('ROLLBACK;'); | 
					
						
							|  |  |  |     throw error; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-08 18:32:10 -07:00
										 |  |  | const SCHEMA_VERSIONS = [ | 
					
						
							|  |  |  |   updateToSchemaVersion1, | 
					
						
							|  |  |  |   updateToSchemaVersion2, | 
					
						
							|  |  |  |   updateToSchemaVersion3, | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  |   updateToSchemaVersion4, | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   (_v: number, _i: PromisifiedSQLDatabase) => null, // version 5 was dropped
 | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |   updateToSchemaVersion6, | 
					
						
							| 
									
										
										
										
											2018-11-19 17:38:55 -08:00
										 |  |  |   updateToSchemaVersion7, | 
					
						
							| 
									
										
										
										
											2019-01-14 13:47:19 -08:00
										 |  |  |   updateToSchemaVersion8, | 
					
						
							| 
									
										
										
										
											2019-01-30 12:15:07 -08:00
										 |  |  |   updateToSchemaVersion9, | 
					
						
							| 
									
										
										
										
											2019-02-04 17:23:50 -08:00
										 |  |  |   updateToSchemaVersion10, | 
					
						
							| 
									
										
										
										
											2019-02-11 15:59:21 -08:00
										 |  |  |   updateToSchemaVersion11, | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  |   updateToSchemaVersion12, | 
					
						
							|  |  |  |   updateToSchemaVersion13, | 
					
						
							| 
									
										
										
										
											2019-05-24 16:58:27 -07:00
										 |  |  |   updateToSchemaVersion14, | 
					
						
							| 
									
										
										
										
											2019-05-29 17:56:29 -07:00
										 |  |  |   updateToSchemaVersion15, | 
					
						
							| 
									
										
										
										
											2019-06-26 12:33:13 -07:00
										 |  |  |   updateToSchemaVersion16, | 
					
						
							| 
									
										
										
										
											2019-08-05 13:53:15 -07:00
										 |  |  |   updateToSchemaVersion17, | 
					
						
							| 
									
										
										
										
											2019-08-08 17:46:49 -07:00
										 |  |  |   updateToSchemaVersion18, | 
					
						
							| 
									
										
										
										
											2020-01-13 14:28:28 -08:00
										 |  |  |   updateToSchemaVersion19, | 
					
						
							| 
									
										
										
										
											2020-03-05 13:14:58 -08:00
										 |  |  |   updateToSchemaVersion20, | 
					
						
							| 
									
										
										
										
											2020-05-27 17:37:06 -04:00
										 |  |  |   updateToSchemaVersion21, | 
					
						
							| 
									
										
										
										
											2021-02-04 11:43:10 -08:00
										 |  |  |   updateToSchemaVersion22, | 
					
						
							| 
									
										
										
										
											2021-02-04 12:46:55 -08:00
										 |  |  |   updateToSchemaVersion23, | 
					
						
							| 
									
										
										
										
											2021-03-18 12:09:27 -05:00
										 |  |  |   updateToSchemaVersion24, | 
					
						
							| 
									
										
										
										
											2018-08-08 18:32:10 -07:00
										 |  |  | ]; | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function updateSchema(instance: PromisifiedSQLDatabase) { | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |   const sqliteVersion = await getSQLiteVersion(instance); | 
					
						
							| 
									
										
										
										
											2019-08-20 06:24:43 -07:00
										 |  |  |   const sqlcipherVersion = await getSQLCipherVersion(instance); | 
					
						
							|  |  |  |   const userVersion = await getUserVersion(instance); | 
					
						
							| 
									
										
										
										
											2020-03-31 12:22:14 -04:00
										 |  |  |   const maxUserVersion = SCHEMA_VERSIONS.length; | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |   const schemaVersion = await getSchemaVersion(instance); | 
					
						
							| 
									
										
										
										
											2019-08-20 06:24:43 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |   console.log( | 
					
						
							| 
									
										
										
										
											2019-08-20 06:24:43 -07:00
										 |  |  |     'updateSchema:\n', | 
					
						
							|  |  |  |     ` Current user_version: ${userVersion};\n`, | 
					
						
							| 
									
										
										
										
											2020-03-31 12:22:14 -04:00
										 |  |  |     ` Most recent db schema: ${maxUserVersion};\n`, | 
					
						
							| 
									
										
										
										
											2019-08-20 06:24:43 -07:00
										 |  |  |     ` SQLite version: ${sqliteVersion};\n`, | 
					
						
							|  |  |  |     ` SQLCipher version: ${sqlcipherVersion};\n`, | 
					
						
							|  |  |  |     ` (deprecated) schema_version: ${schemaVersion};\n` | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |   ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-31 12:22:14 -04:00
										 |  |  |   if (userVersion > maxUserVersion) { | 
					
						
							|  |  |  |     throw new Error( | 
					
						
							|  |  |  |       `SQL: User version is ${userVersion} but the expected maximum version is ${maxUserVersion}. Did you try to start an old version of Signal?` | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   for (let index = 0; index < maxUserVersion; index += 1) { | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |     const runSchemaUpdate = SCHEMA_VERSIONS[index]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Yes, we really want to do this asynchronously, in order
 | 
					
						
							| 
									
										
										
										
											2019-08-20 06:24:43 -07:00
										 |  |  |     await runSchemaUpdate(userVersion, instance); | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | let globalInstance: PromisifiedSQLDatabase | undefined; | 
					
						
							| 
									
										
										
										
											2021-03-04 16:44:57 -05:00
										 |  |  | let globalInstanceRenderer: PromisifiedSQLDatabase | undefined; | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | let databaseFilePath: string | undefined; | 
					
						
							|  |  |  | let indexedDBPath: string | undefined; | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function initialize({ | 
					
						
							|  |  |  |   configDir, | 
					
						
							|  |  |  |   key, | 
					
						
							|  |  |  |   messages, | 
					
						
							|  |  |  | }: { | 
					
						
							|  |  |  |   configDir: string; | 
					
						
							|  |  |  |   key: string; | 
					
						
							|  |  |  |   messages: LocaleMessagesType; | 
					
						
							|  |  |  | }) { | 
					
						
							|  |  |  |   if (globalInstance) { | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |     throw new Error('Cannot initialize more than once!'); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (!isString(configDir)) { | 
					
						
							|  |  |  |     throw new Error('initialize: configDir is required!'); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   if (!isString(key)) { | 
					
						
							| 
									
										
										
										
											2019-02-19 17:32:44 -08:00
										 |  |  |     throw new Error('initialize: key is required!'); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   if (!isObject(messages)) { | 
					
						
							|  |  |  |     throw new Error('initialize: message is required!'); | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  |   indexedDBPath = join(configDir, 'IndexedDB'); | 
					
						
							| 
									
										
										
										
											2018-11-01 16:40:19 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  |   const dbDir = join(configDir, 'sql'); | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |   mkdirp.sync(dbDir); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   databaseFilePath = join(dbDir, 'db.sqlite'); | 
					
						
							| 
									
										
										
										
											2018-11-01 16:40:19 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   let promisified: PromisifiedSQLDatabase | undefined; | 
					
						
							| 
									
										
										
										
											2019-08-19 15:26:45 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-27 10:17:22 -08:00
										 |  |  |   try { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |     promisified = await openAndSetUpSQLCipher(databaseFilePath, { key }); | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-12 16:42:15 -08:00
										 |  |  |     // if (promisified) {
 | 
					
						
							|  |  |  |     //   promisified.on('trace', async statement => {
 | 
					
						
							|  |  |  |     //     if (
 | 
					
						
							|  |  |  |     //       !globalInstance ||
 | 
					
						
							|  |  |  |     //       statement.startsWith('--') ||
 | 
					
						
							|  |  |  |     //       statement.includes('COMMIT') ||
 | 
					
						
							|  |  |  |     //       statement.includes('BEGIN') ||
 | 
					
						
							|  |  |  |     //       statement.includes('ROLLBACK')
 | 
					
						
							|  |  |  |     //     ) {
 | 
					
						
							|  |  |  |     //       return;
 | 
					
						
							|  |  |  |     //     }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     //     // Note that this causes problems when attempting to commit transactions - this
 | 
					
						
							|  |  |  |     //     //   statement is running, and we get at SQLITE_BUSY error. So we delay.
 | 
					
						
							|  |  |  |     //     await new Promise(resolve => setTimeout(resolve, 1000));
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     //     const data = await promisified.get(`EXPLAIN QUERY PLAN ${statement}`);
 | 
					
						
							|  |  |  |     //     console._log(`EXPLAIN QUERY PLAN ${statement}\n`, data && data.detail);
 | 
					
						
							|  |  |  |     //   });
 | 
					
						
							|  |  |  |     // }
 | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-27 10:17:22 -08:00
										 |  |  |     await updateSchema(promisified); | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-27 10:17:22 -08:00
										 |  |  |     // test database
 | 
					
						
							| 
									
										
										
										
											2019-08-19 10:59:30 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-23 14:08:33 -07:00
										 |  |  |     const cipherIntegrityResult = await getSQLCipherIntegrityCheck(promisified); | 
					
						
							|  |  |  |     if (cipherIntegrityResult) { | 
					
						
							|  |  |  |       console.log( | 
					
						
							|  |  |  |         'Database cipher integrity check failed:', | 
					
						
							|  |  |  |         cipherIntegrityResult | 
					
						
							|  |  |  |       ); | 
					
						
							|  |  |  |       throw new Error( | 
					
						
							|  |  |  |         `Cipher integrity check failed: ${cipherIntegrityResult}` | 
					
						
							|  |  |  |       ); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     const integrityResult = await getSQLIntegrityCheck(promisified); | 
					
						
							|  |  |  |     if (integrityResult) { | 
					
						
							|  |  |  |       console.log('Database integrity check failed:', integrityResult); | 
					
						
							|  |  |  |       throw new Error(`Integrity check failed: ${integrityResult}`); | 
					
						
							| 
									
										
										
										
											2019-08-19 10:59:30 -07:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-19 15:26:45 -07:00
										 |  |  |     // At this point we can allow general access to the database
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |     globalInstance = promisified; | 
					
						
							| 
									
										
										
										
											2019-08-19 15:26:45 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     // test database
 | 
					
						
							| 
									
										
										
										
											2019-02-19 17:32:44 -08:00
										 |  |  |     await getMessageCount(); | 
					
						
							|  |  |  |   } catch (error) { | 
					
						
							|  |  |  |     console.log('Database startup error:', error.stack); | 
					
						
							| 
									
										
										
										
											2021-03-04 16:44:57 -05:00
										 |  |  |     if (promisified) { | 
					
						
							|  |  |  |       await promisified.close(); | 
					
						
							| 
									
										
										
										
											2019-02-19 17:32:44 -08:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-03-04 16:44:57 -05:00
										 |  |  |     throw error; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2019-02-19 17:32:44 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-04 16:44:57 -05:00
										 |  |  | async function initializeRenderer({ | 
					
						
							|  |  |  |   configDir, | 
					
						
							|  |  |  |   key, | 
					
						
							|  |  |  | }: { | 
					
						
							|  |  |  |   configDir: string; | 
					
						
							|  |  |  |   key: string; | 
					
						
							|  |  |  | }) { | 
					
						
							|  |  |  |   if (!isRenderer()) { | 
					
						
							|  |  |  |     throw new Error('Cannot call from main process.'); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   if (globalInstanceRenderer) { | 
					
						
							|  |  |  |     throw new Error('Cannot initialize more than once!'); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   if (!isString(configDir)) { | 
					
						
							|  |  |  |     throw new Error('initialize: configDir is required!'); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   if (!isString(key)) { | 
					
						
							|  |  |  |     throw new Error('initialize: key is required!'); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (!indexedDBPath) { | 
					
						
							|  |  |  |     indexedDBPath = join(configDir, 'IndexedDB'); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-04 16:44:57 -05:00
										 |  |  |   const dbDir = join(configDir, 'sql'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (!databaseFilePath) { | 
					
						
							|  |  |  |     databaseFilePath = join(dbDir, 'db.sqlite'); | 
					
						
							| 
									
										
										
										
											2019-02-19 17:32:44 -08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-04 16:44:57 -05:00
										 |  |  |   let promisified: PromisifiedSQLDatabase | undefined; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   try { | 
					
						
							|  |  |  |     promisified = await openAndSetUpSQLCipher(databaseFilePath, { key }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // At this point we can allow general access to the database
 | 
					
						
							|  |  |  |     globalInstanceRenderer = promisified; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // test database
 | 
					
						
							|  |  |  |     await getMessageCount(); | 
					
						
							|  |  |  |   } catch (error) { | 
					
						
							|  |  |  |     window.log.error('Database startup error:', error.stack); | 
					
						
							|  |  |  |     throw error; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | async function close() { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   if (!globalInstance) { | 
					
						
							| 
									
										
										
										
											2019-03-28 10:51:19 -07:00
										 |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const dbRef = globalInstance; | 
					
						
							|  |  |  |   globalInstance = undefined; | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |   await dbRef.close(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | async function removeDB() { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   if (globalInstance) { | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |     throw new Error('removeDB: Cannot erase database when it is open!'); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   if (!databaseFilePath) { | 
					
						
							|  |  |  |     throw new Error( | 
					
						
							|  |  |  |       'removeDB: Cannot erase database without a databaseFilePath!' | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   rimraf.sync(databaseFilePath); | 
					
						
							| 
									
										
										
										
											2021-03-24 14:35:06 -07:00
										 |  |  |   rimraf.sync(`${databaseFilePath}-shm`); | 
					
						
							|  |  |  |   rimraf.sync(`${databaseFilePath}-wal`); | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-01 16:40:19 -07:00
										 |  |  | async function removeIndexedDBFiles() { | 
					
						
							|  |  |  |   if (!indexedDBPath) { | 
					
						
							|  |  |  |     throw new Error( | 
					
						
							|  |  |  |       'removeIndexedDBFiles: Need to initialize and set indexedDBPath first!' | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  |   const pattern = join(indexedDBPath, '*.leveldb'); | 
					
						
							| 
									
										
										
										
											2018-11-01 16:40:19 -07:00
										 |  |  |   rimraf.sync(pattern); | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   indexedDBPath = undefined; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function getInstance(): PromisifiedSQLDatabase { | 
					
						
							| 
									
										
										
										
											2021-03-04 16:44:57 -05:00
										 |  |  |   if (isRenderer()) { | 
					
						
							|  |  |  |     if (!globalInstanceRenderer) { | 
					
						
							|  |  |  |       throw new Error('getInstance: globalInstanceRenderer not set!'); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return globalInstanceRenderer; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   if (!globalInstance) { | 
					
						
							|  |  |  |     throw new Error('getInstance: globalInstance not set!'); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return globalInstance; | 
					
						
							| 
									
										
										
										
											2018-11-01 16:40:19 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  | const IDENTITY_KEYS_TABLE = 'identityKeys'; | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function createOrUpdateIdentityKey(data: IdentityKeyType) { | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |   return createOrUpdate(IDENTITY_KEYS_TABLE, data); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function getIdentityKeyById(id: string) { | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |   return getById(IDENTITY_KEYS_TABLE, id); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function bulkAddIdentityKeys(array: Array<IdentityKeyType>) { | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |   return bulkAdd(IDENTITY_KEYS_TABLE, array); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2021-02-04 12:46:55 -08:00
										 |  |  | bulkAddIdentityKeys.needsSerial = true; | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function removeIdentityKeyById(id: string) { | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |   return removeById(IDENTITY_KEYS_TABLE, id); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | async function removeAllIdentityKeys() { | 
					
						
							|  |  |  |   return removeAllFromTable(IDENTITY_KEYS_TABLE); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2019-02-04 15:54:37 -08:00
										 |  |  | async function getAllIdentityKeys() { | 
					
						
							|  |  |  |   return getAllFromTable(IDENTITY_KEYS_TABLE); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | const PRE_KEYS_TABLE = 'preKeys'; | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function createOrUpdatePreKey(data: PreKeyType) { | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |   return createOrUpdate(PRE_KEYS_TABLE, data); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function getPreKeyById(id: number) { | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |   return getById(PRE_KEYS_TABLE, id); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function bulkAddPreKeys(array: Array<PreKeyType>) { | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |   return bulkAdd(PRE_KEYS_TABLE, array); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2021-02-04 12:46:55 -08:00
										 |  |  | bulkAddPreKeys.needsSerial = true; | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function removePreKeyById(id: number) { | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |   return removeById(PRE_KEYS_TABLE, id); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | async function removeAllPreKeys() { | 
					
						
							|  |  |  |   return removeAllFromTable(PRE_KEYS_TABLE); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2019-02-04 15:54:37 -08:00
										 |  |  | async function getAllPreKeys() { | 
					
						
							|  |  |  |   return getAllFromTable(PRE_KEYS_TABLE); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | const SIGNED_PRE_KEYS_TABLE = 'signedPreKeys'; | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function createOrUpdateSignedPreKey(data: SignedPreKeyType) { | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |   return createOrUpdate(SIGNED_PRE_KEYS_TABLE, data); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function getSignedPreKeyById(id: number) { | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |   return getById(SIGNED_PRE_KEYS_TABLE, id); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function bulkAddSignedPreKeys(array: Array<SignedPreKeyType>) { | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |   return bulkAdd(SIGNED_PRE_KEYS_TABLE, array); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2021-02-04 12:46:55 -08:00
										 |  |  | bulkAddSignedPreKeys.needsSerial = true; | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function removeSignedPreKeyById(id: number) { | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |   return removeById(SIGNED_PRE_KEYS_TABLE, id); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | async function removeAllSignedPreKeys() { | 
					
						
							|  |  |  |   return removeAllFromTable(SIGNED_PRE_KEYS_TABLE); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function getAllSignedPreKeys() { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							|  |  |  |   const rows = await db.all('SELECT json FROM signedPreKeys ORDER BY id ASC;'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return map(rows, row => jsonToObject(row.json)); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | const ITEMS_TABLE = 'items'; | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function createOrUpdateItem(data: ItemType) { | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |   return createOrUpdate(ITEMS_TABLE, data); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function getItemById(id: string) { | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |   return getById(ITEMS_TABLE, id); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | async function getAllItems() { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |   const rows = await db.all('SELECT json FROM items ORDER BY id ASC;'); | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |   return map(rows, row => jsonToObject(row.json)); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function bulkAddItems(array: Array<ItemType>) { | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |   return bulkAdd(ITEMS_TABLE, array); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2021-02-04 12:46:55 -08:00
										 |  |  | bulkAddItems.needsSerial = true; | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function removeItemById(id: string) { | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |   return removeById(ITEMS_TABLE, id); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | async function removeAllItems() { | 
					
						
							|  |  |  |   return removeAllFromTable(ITEMS_TABLE); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const SESSIONS_TABLE = 'sessions'; | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function createOrUpdateSession(data: SessionType) { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2020-03-05 13:14:58 -08:00
										 |  |  |   const { id, conversationId } = data; | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |   if (!id) { | 
					
						
							|  |  |  |     throw new Error( | 
					
						
							|  |  |  |       'createOrUpdateSession: Provided data did not have a truthy id' | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2020-03-05 13:14:58 -08:00
										 |  |  |   if (!conversationId) { | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |     throw new Error( | 
					
						
							| 
									
										
										
										
											2020-03-05 13:14:58 -08:00
										 |  |  |       'createOrUpdateSession: Provided data did not have a truthy conversationId' | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   await db.run( | 
					
						
							|  |  |  |     `INSERT OR REPLACE INTO sessions (
 | 
					
						
							|  |  |  |       id, | 
					
						
							| 
									
										
										
										
											2020-03-05 13:14:58 -08:00
										 |  |  |       conversationId, | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |       json | 
					
						
							|  |  |  |     ) values ( | 
					
						
							|  |  |  |       $id, | 
					
						
							| 
									
										
										
										
											2020-03-05 13:14:58 -08:00
										 |  |  |       $conversationId, | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |       $json | 
					
						
							|  |  |  |     )`,
 | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       $id: id, | 
					
						
							| 
									
										
										
										
											2020-03-05 13:14:58 -08:00
										 |  |  |       $conversationId: conversationId, | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |       $json: objectToJSON(data), | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function createOrUpdateSessions(array: Array<SessionType>) { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2019-09-26 12:56:31 -07:00
										 |  |  |   await db.run('BEGIN TRANSACTION;'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   try { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |     await Promise.all([ | 
					
						
							|  |  |  |       ...map(array, async item => createOrUpdateSession(item)), | 
					
						
							|  |  |  |     ]); | 
					
						
							| 
									
										
										
										
											2019-09-26 12:56:31 -07:00
										 |  |  |     await db.run('COMMIT TRANSACTION;'); | 
					
						
							|  |  |  |   } catch (error) { | 
					
						
							|  |  |  |     await db.run('ROLLBACK;'); | 
					
						
							|  |  |  |     throw error; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | createOrUpdateSessions.needsSerial = true; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function getSessionById(id: string) { | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |   return getById(SESSIONS_TABLE, id); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function getSessionsById(conversationId: string) { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2020-03-05 13:14:58 -08:00
										 |  |  |   const rows = await db.all( | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |     'SELECT * FROM sessions WHERE conversationId = $conversationId;', | 
					
						
							| 
									
										
										
										
											2020-03-05 13:14:58 -08:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |       $conversationId: conversationId, | 
					
						
							| 
									
										
										
										
											2020-03-05 13:14:58 -08:00
										 |  |  |     } | 
					
						
							|  |  |  |   ); | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |   return map(rows, row => jsonToObject(row.json)); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function bulkAddSessions(array: Array<SessionType>) { | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |   return bulkAdd(SESSIONS_TABLE, array); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2021-02-04 12:46:55 -08:00
										 |  |  | bulkAddSessions.needsSerial = true; | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function removeSessionById(id: string) { | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |   return removeById(SESSIONS_TABLE, id); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function removeSessionsByConversation(conversationId: string) { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							|  |  |  |   await db.run('DELETE FROM sessions WHERE conversationId = $conversationId;', { | 
					
						
							|  |  |  |     $conversationId: conversationId, | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |   }); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | async function removeAllSessions() { | 
					
						
							|  |  |  |   return removeAllFromTable(SESSIONS_TABLE); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2019-02-04 15:54:37 -08:00
										 |  |  | async function getAllSessions() { | 
					
						
							|  |  |  |   return getAllFromTable(SESSIONS_TABLE); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function createOrUpdate(table: string, data: any) { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |   const { id } = data; | 
					
						
							|  |  |  |   if (!id) { | 
					
						
							|  |  |  |     throw new Error('createOrUpdate: Provided data did not have a truthy id'); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   await db.run( | 
					
						
							|  |  |  |     `INSERT OR REPLACE INTO ${table} (
 | 
					
						
							|  |  |  |       id, | 
					
						
							|  |  |  |       json | 
					
						
							|  |  |  |     ) values ( | 
					
						
							|  |  |  |       $id, | 
					
						
							|  |  |  |       $json | 
					
						
							|  |  |  |     )`,
 | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       $id: id, | 
					
						
							|  |  |  |       $json: objectToJSON(data), | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function bulkAdd(table: string, array: Array<any>) { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |   await db.run('BEGIN TRANSACTION;'); | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |   try { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |     await Promise.all([ | 
					
						
							|  |  |  |       ...map(array, async data => createOrUpdate(table, data)), | 
					
						
							|  |  |  |     ]); | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     await db.run('COMMIT TRANSACTION;'); | 
					
						
							|  |  |  |   } catch (error) { | 
					
						
							|  |  |  |     await db.run('ROLLBACK;'); | 
					
						
							|  |  |  |     throw error; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2021-02-04 12:46:55 -08:00
										 |  |  | bulkAdd.needsSerial = true; | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function getById(table: string, id: string | number) { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |   const row = await db.get(`SELECT * FROM ${table} WHERE id = $id;`, { | 
					
						
							|  |  |  |     $id: id, | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (!row) { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |     return undefined; | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return jsonToObject(row.json); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function removeById(table: string, id: string | number) { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |   if (!Array.isArray(id)) { | 
					
						
							|  |  |  |     await db.run(`DELETE FROM ${table} WHERE id = $id;`, { $id: id }); | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (!id.length) { | 
					
						
							|  |  |  |     throw new Error('removeById: No ids to delete!'); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // Our node interface doesn't seem to allow you to replace one single ? with an array
 | 
					
						
							|  |  |  |   await db.run( | 
					
						
							|  |  |  |     `DELETE FROM ${table} WHERE id IN ( ${id.map(() => '?').join(', ')} );`, | 
					
						
							|  |  |  |     id | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function removeAllFromTable(table: string) { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |   await db.run(`DELETE FROM ${table};`); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function getAllFromTable(table: string) { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2019-02-04 15:54:37 -08:00
										 |  |  |   const rows = await db.all(`SELECT json FROM ${table};`); | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-04 15:54:37 -08:00
										 |  |  |   return rows.map(row => jsonToObject(row.json)); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  | // Conversations
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  | async function getConversationCount() { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  |   const row = await db.get('SELECT count(*) from conversations;'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (!row) { | 
					
						
							| 
									
										
										
										
											2019-02-19 17:32:44 -08:00
										 |  |  |     throw new Error( | 
					
						
							|  |  |  |       'getConversationCount: Unable to get count of conversations' | 
					
						
							|  |  |  |     ); | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return row['count(*)']; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function saveConversation( | 
					
						
							|  |  |  |   data: ConversationType, | 
					
						
							|  |  |  |   instance = getInstance() | 
					
						
							|  |  |  | ) { | 
					
						
							| 
									
										
										
										
											2020-01-13 14:28:28 -08:00
										 |  |  |   const { | 
					
						
							|  |  |  |     active_at, | 
					
						
							| 
									
										
										
										
											2020-03-05 13:14:58 -08:00
										 |  |  |     e164, | 
					
						
							|  |  |  |     groupId, | 
					
						
							|  |  |  |     id, | 
					
						
							| 
									
										
										
										
											2020-01-13 14:28:28 -08:00
										 |  |  |     members, | 
					
						
							| 
									
										
										
										
											2020-09-08 19:25:05 -07:00
										 |  |  |     membersV2, | 
					
						
							| 
									
										
										
										
											2020-01-13 14:28:28 -08:00
										 |  |  |     name, | 
					
						
							|  |  |  |     profileFamilyName, | 
					
						
							| 
									
										
										
										
											2020-03-05 13:14:58 -08:00
										 |  |  |     profileName, | 
					
						
							| 
									
										
										
										
											2021-03-18 12:09:27 -05:00
										 |  |  |     profileLastFetchedAt, | 
					
						
							| 
									
										
										
										
											2020-03-05 13:14:58 -08:00
										 |  |  |     type, | 
					
						
							|  |  |  |     uuid, | 
					
						
							| 
									
										
										
										
											2020-01-13 14:28:28 -08:00
										 |  |  |   } = data; | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-08 19:25:05 -07:00
										 |  |  |   // prettier-ignore
 | 
					
						
							|  |  |  |   const membersList = membersV2 | 
					
						
							|  |  |  |     ? membersV2.map((item: GroupV2MemberType) => item.conversationId).join(' ') | 
					
						
							|  |  |  |     : members | 
					
						
							|  |  |  |       ? members.join(' ') | 
					
						
							|  |  |  |       : null; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-05 13:14:58 -08:00
										 |  |  |   await instance.run( | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  |     `INSERT INTO conversations (
 | 
					
						
							|  |  |  |     id, | 
					
						
							|  |  |  |     json, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-05 13:14:58 -08:00
										 |  |  |     e164, | 
					
						
							|  |  |  |     uuid, | 
					
						
							|  |  |  |     groupId, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  |     active_at, | 
					
						
							|  |  |  |     type, | 
					
						
							|  |  |  |     members, | 
					
						
							|  |  |  |     name, | 
					
						
							| 
									
										
										
										
											2020-01-13 14:28:28 -08:00
										 |  |  |     profileName, | 
					
						
							|  |  |  |     profileFamilyName, | 
					
						
							| 
									
										
										
										
											2021-03-18 12:09:27 -05:00
										 |  |  |     profileFullName, | 
					
						
							|  |  |  |     profileLastFetchedAt | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  |   ) values ( | 
					
						
							|  |  |  |     $id, | 
					
						
							|  |  |  |     $json, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-05 13:14:58 -08:00
										 |  |  |     $e164, | 
					
						
							|  |  |  |     $uuid, | 
					
						
							|  |  |  |     $groupId, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  |     $active_at, | 
					
						
							|  |  |  |     $type, | 
					
						
							|  |  |  |     $members, | 
					
						
							|  |  |  |     $name, | 
					
						
							| 
									
										
										
										
											2020-01-13 14:28:28 -08:00
										 |  |  |     $profileName, | 
					
						
							|  |  |  |     $profileFamilyName, | 
					
						
							| 
									
										
										
										
											2021-03-18 12:09:27 -05:00
										 |  |  |     $profileFullName, | 
					
						
							|  |  |  |     $profileLastFetchedAt | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  |   );`,
 | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       $id: id, | 
					
						
							| 
									
										
										
										
											2021-03-18 12:09:27 -05:00
										 |  |  |       $json: objectToJSON(omit(data, ['profileLastFetchedAt'])), | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-05 13:14:58 -08:00
										 |  |  |       $e164: e164, | 
					
						
							|  |  |  |       $uuid: uuid, | 
					
						
							|  |  |  |       $groupId: groupId, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  |       $active_at: active_at, | 
					
						
							|  |  |  |       $type: type, | 
					
						
							| 
									
										
										
										
											2020-09-08 19:25:05 -07:00
										 |  |  |       $members: membersList, | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  |       $name: name, | 
					
						
							|  |  |  |       $profileName: profileName, | 
					
						
							| 
									
										
										
										
											2020-01-13 14:28:28 -08:00
										 |  |  |       $profileFamilyName: profileFamilyName, | 
					
						
							|  |  |  |       $profileFullName: combineNames(profileName, profileFamilyName), | 
					
						
							| 
									
										
										
										
											2021-03-18 12:09:27 -05:00
										 |  |  |       $profileLastFetchedAt: profileLastFetchedAt, | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  |     } | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function saveConversations( | 
					
						
							|  |  |  |   arrayOfConversations: Array<ConversationType> | 
					
						
							|  |  |  | ) { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |   await db.run('BEGIN TRANSACTION;'); | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |   try { | 
					
						
							|  |  |  |     await Promise.all([ | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |       ...map(arrayOfConversations, async conversation => | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  |         saveConversation(conversation) | 
					
						
							|  |  |  |       ), | 
					
						
							|  |  |  |     ]); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     await db.run('COMMIT TRANSACTION;'); | 
					
						
							|  |  |  |   } catch (error) { | 
					
						
							|  |  |  |     await db.run('ROLLBACK;'); | 
					
						
							|  |  |  |     throw error; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2019-09-03 13:10:32 -07:00
										 |  |  | saveConversations.needsSerial = true; | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function updateConversation(data: ConversationType) { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2020-01-13 14:28:28 -08:00
										 |  |  |   const { | 
					
						
							|  |  |  |     id, | 
					
						
							|  |  |  |     active_at, | 
					
						
							|  |  |  |     type, | 
					
						
							|  |  |  |     members, | 
					
						
							| 
									
										
										
										
											2020-09-08 19:25:05 -07:00
										 |  |  |     membersV2, | 
					
						
							| 
									
										
										
										
											2020-01-13 14:28:28 -08:00
										 |  |  |     name, | 
					
						
							|  |  |  |     profileName, | 
					
						
							|  |  |  |     profileFamilyName, | 
					
						
							| 
									
										
										
										
											2021-03-18 12:09:27 -05:00
										 |  |  |     profileLastFetchedAt, | 
					
						
							| 
									
										
										
										
											2020-03-05 13:14:58 -08:00
										 |  |  |     e164, | 
					
						
							|  |  |  |     uuid, | 
					
						
							| 
									
										
										
										
											2020-01-13 14:28:28 -08:00
										 |  |  |   } = data; | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-08 19:25:05 -07:00
										 |  |  |   // prettier-ignore
 | 
					
						
							|  |  |  |   const membersList = membersV2 | 
					
						
							|  |  |  |     ? membersV2.map((item: GroupV2MemberType) => item.conversationId).join(' ') | 
					
						
							|  |  |  |     : members | 
					
						
							|  |  |  |       ? members.join(' ') | 
					
						
							|  |  |  |       : null; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  |   await db.run( | 
					
						
							|  |  |  |     `UPDATE conversations SET
 | 
					
						
							| 
									
										
										
										
											2019-05-23 18:27:42 -07:00
										 |  |  |       json = $json, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-05 13:14:58 -08:00
										 |  |  |       e164 = $e164, | 
					
						
							|  |  |  |       uuid = $uuid, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-23 18:27:42 -07:00
										 |  |  |       active_at = $active_at, | 
					
						
							|  |  |  |       type = $type, | 
					
						
							|  |  |  |       members = $members, | 
					
						
							|  |  |  |       name = $name, | 
					
						
							| 
									
										
										
										
											2020-01-13 14:28:28 -08:00
										 |  |  |       profileName = $profileName, | 
					
						
							|  |  |  |       profileFamilyName = $profileFamilyName, | 
					
						
							| 
									
										
										
										
											2021-03-18 12:09:27 -05:00
										 |  |  |       profileFullName = $profileFullName, | 
					
						
							|  |  |  |       profileLastFetchedAt = $profileLastFetchedAt | 
					
						
							| 
									
										
										
										
											2019-05-23 18:27:42 -07:00
										 |  |  |     WHERE id = $id;`,
 | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  |     { | 
					
						
							|  |  |  |       $id: id, | 
					
						
							| 
									
										
										
										
											2021-03-18 12:09:27 -05:00
										 |  |  |       $json: objectToJSON(omit(data, ['profileLastFetchedAt'])), | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-05 13:14:58 -08:00
										 |  |  |       $e164: e164, | 
					
						
							|  |  |  |       $uuid: uuid, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  |       $active_at: active_at, | 
					
						
							|  |  |  |       $type: type, | 
					
						
							| 
									
										
										
										
											2020-09-08 19:25:05 -07:00
										 |  |  |       $members: membersList, | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  |       $name: name, | 
					
						
							|  |  |  |       $profileName: profileName, | 
					
						
							| 
									
										
										
										
											2020-01-13 14:28:28 -08:00
										 |  |  |       $profileFamilyName: profileFamilyName, | 
					
						
							|  |  |  |       $profileFullName: combineNames(profileName, profileFamilyName), | 
					
						
							| 
									
										
										
										
											2021-03-18 12:09:27 -05:00
										 |  |  |       $profileLastFetchedAt: profileLastFetchedAt, | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  |     } | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2021-03-04 16:44:57 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function updateConversations(array: Array<ConversationType>) { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2019-09-26 12:56:31 -07:00
										 |  |  |   await db.run('BEGIN TRANSACTION;'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   try { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |     await Promise.all([...map(array, async item => updateConversation(item))]); | 
					
						
							| 
									
										
										
										
											2019-09-26 12:56:31 -07:00
										 |  |  |     await db.run('COMMIT TRANSACTION;'); | 
					
						
							|  |  |  |   } catch (error) { | 
					
						
							|  |  |  |     await db.run('ROLLBACK;'); | 
					
						
							|  |  |  |     throw error; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | updateConversations.needsSerial = true; | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function removeConversation(id: Array<string> | string) { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  |   if (!Array.isArray(id)) { | 
					
						
							|  |  |  |     await db.run('DELETE FROM conversations WHERE id = $id;', { $id: id }); | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (!id.length) { | 
					
						
							|  |  |  |     throw new Error('removeConversation: No ids to delete!'); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // Our node interface doesn't seem to allow you to replace one single ? with an array
 | 
					
						
							|  |  |  |   await db.run( | 
					
						
							|  |  |  |     `DELETE FROM conversations WHERE id IN ( ${id | 
					
						
							|  |  |  |       .map(() => '?') | 
					
						
							|  |  |  |       .join(', ')} );`,
 | 
					
						
							|  |  |  |     id | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function getConversationById(id: string) { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  |   const row = await db.get('SELECT * FROM conversations WHERE id = $id;', { | 
					
						
							|  |  |  |     $id: id, | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (!row) { | 
					
						
							|  |  |  |     return null; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return jsonToObject(row.json); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-08 20:56:23 -04:00
										 |  |  | async function eraseStorageServiceStateFromConversations() { | 
					
						
							| 
									
										
										
										
											2020-07-24 16:32:08 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   await db.run( | 
					
						
							|  |  |  |     `UPDATE conversations SET
 | 
					
						
							| 
									
										
										
										
											2020-09-29 19:29:11 -04:00
										 |  |  |       json = json_remove(json, '$.storageID', '$.needsStorageServiceSync', '$.unknownFields', '$.storageProfileKey'); | 
					
						
							| 
									
										
										
										
											2020-07-24 16:32:08 -07:00
										 |  |  |     `
 | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  | async function getAllConversations() { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2021-03-18 12:09:27 -05:00
										 |  |  |   const rows = await db.all(`
 | 
					
						
							|  |  |  |     SELECT json, profileLastFetchedAt | 
					
						
							|  |  |  |     FROM conversations | 
					
						
							|  |  |  |     ORDER BY id ASC; | 
					
						
							|  |  |  |   `);
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-18 12:09:27 -05:00
										 |  |  |   return map(rows, row => rowToConversation(row)); | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | async function getAllConversationIds() { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  |   const rows = await db.all('SELECT id FROM conversations ORDER BY id ASC;'); | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  |   return map(rows, row => row.id); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | async function getAllPrivateConversations() { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  |   const rows = await db.all( | 
					
						
							| 
									
										
										
										
											2021-03-18 12:09:27 -05:00
										 |  |  |     `SELECT json, profileLastFetchedAt
 | 
					
						
							|  |  |  |     FROM conversations | 
					
						
							|  |  |  |     WHERE type = 'private' | 
					
						
							|  |  |  |     ORDER BY id ASC;`
 | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  |   ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-18 12:09:27 -05:00
										 |  |  |   return map(rows, row => rowToConversation(row)); | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function getAllGroupsInvolvingId(id: string) { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  |   const rows = await db.all( | 
					
						
							| 
									
										
										
										
											2021-03-18 12:09:27 -05:00
										 |  |  |     `SELECT json, profileLastFetchedAt
 | 
					
						
							|  |  |  |      FROM conversations WHERE | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  |       type = 'group' AND | 
					
						
							|  |  |  |       members LIKE $id | 
					
						
							|  |  |  |      ORDER BY id ASC;`,
 | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       $id: `%${id}%`, | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-18 12:09:27 -05:00
										 |  |  |   return map(rows, row => rowToConversation(row)); | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function searchConversations( | 
					
						
							|  |  |  |   query: string, | 
					
						
							|  |  |  |   { limit }: { limit?: number } = {} | 
					
						
							|  |  |  | ): Promise<Array<ConversationType>> { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  |   const rows = await db.all( | 
					
						
							| 
									
										
										
										
											2021-03-18 12:09:27 -05:00
										 |  |  |     `SELECT json, profileLastFetchedAt
 | 
					
						
							|  |  |  |      FROM conversations WHERE | 
					
						
							| 
									
										
										
										
											2019-01-14 13:47:19 -08:00
										 |  |  |       ( | 
					
						
							| 
									
										
										
										
											2021-02-12 18:40:39 -06:00
										 |  |  |         e164 LIKE $query OR | 
					
						
							|  |  |  |         name LIKE $query OR | 
					
						
							|  |  |  |         profileFullName LIKE $query | 
					
						
							| 
									
										
										
										
											2019-01-14 13:47:19 -08:00
										 |  |  |       ) | 
					
						
							| 
									
										
										
										
											2019-09-05 08:59:21 -07:00
										 |  |  |      ORDER BY active_at DESC | 
					
						
							| 
									
										
										
										
											2019-01-14 13:49:58 -08:00
										 |  |  |      LIMIT $limit`,
 | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2021-02-12 18:40:39 -06:00
										 |  |  |       $query: `%${query}%`, | 
					
						
							| 
									
										
										
										
											2019-08-08 17:46:49 -07:00
										 |  |  |       $limit: limit || 100, | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  |     } | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-18 12:09:27 -05:00
										 |  |  |   return map(rows, row => rowToConversation(row)); | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function searchMessages( | 
					
						
							|  |  |  |   query: string, | 
					
						
							|  |  |  |   { limit }: { limit?: number } = {} | 
					
						
							|  |  |  | ): Promise<Array<SearchResultMessageType>> { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2019-01-14 13:47:19 -08:00
										 |  |  |   const rows = await db.all( | 
					
						
							|  |  |  |     `SELECT
 | 
					
						
							|  |  |  |       messages.json, | 
					
						
							| 
									
										
										
										
											2021-02-23 14:34:28 -06:00
										 |  |  |       snippet(messages_fts, -1, '<<left>>', '<<right>>', '...', 10) as snippet | 
					
						
							| 
									
										
										
										
											2019-01-14 13:47:19 -08:00
										 |  |  |     FROM messages_fts | 
					
						
							|  |  |  |     INNER JOIN messages on messages_fts.id = messages.id | 
					
						
							|  |  |  |     WHERE | 
					
						
							|  |  |  |       messages_fts match $query | 
					
						
							| 
									
										
										
										
											2021-01-13 08:32:18 -08:00
										 |  |  |     ORDER BY messages.received_at DESC, messages.sent_at DESC | 
					
						
							| 
									
										
										
										
											2019-01-14 13:47:19 -08:00
										 |  |  |     LIMIT $limit;`,
 | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       $query: query, | 
					
						
							| 
									
										
										
										
											2019-08-08 17:46:49 -07:00
										 |  |  |       $limit: limit || 500, | 
					
						
							| 
									
										
										
										
											2019-01-14 13:47:19 -08:00
										 |  |  |     } | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return map(rows, row => ({ | 
					
						
							| 
									
										
										
										
											2019-08-09 16:12:29 -07:00
										 |  |  |     json: row.json, | 
					
						
							| 
									
										
										
										
											2019-01-14 13:47:19 -08:00
										 |  |  |     snippet: row.snippet, | 
					
						
							|  |  |  |   })); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | async function searchMessagesInConversation( | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   query: string, | 
					
						
							|  |  |  |   conversationId: string, | 
					
						
							|  |  |  |   { limit }: { limit?: number } = {} | 
					
						
							|  |  |  | ): Promise<Array<SearchResultMessageType>> { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2019-01-14 13:47:19 -08:00
										 |  |  |   const rows = await db.all( | 
					
						
							|  |  |  |     `SELECT
 | 
					
						
							|  |  |  |       messages.json, | 
					
						
							| 
									
										
										
										
											2021-02-23 14:34:28 -06:00
										 |  |  |       snippet(messages_fts, -1, '<<left>>', '<<right>>', '...', 10) as snippet | 
					
						
							| 
									
										
										
										
											2019-01-14 13:47:19 -08:00
										 |  |  |     FROM messages_fts | 
					
						
							|  |  |  |     INNER JOIN messages on messages_fts.id = messages.id | 
					
						
							|  |  |  |     WHERE | 
					
						
							|  |  |  |       messages_fts match $query AND | 
					
						
							|  |  |  |       messages.conversationId = $conversationId | 
					
						
							| 
									
										
										
										
											2021-01-13 08:32:18 -08:00
										 |  |  |     ORDER BY messages.received_at DESC, messages.sent_at DESC | 
					
						
							| 
									
										
										
										
											2019-01-14 13:47:19 -08:00
										 |  |  |     LIMIT $limit;`,
 | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       $query: query, | 
					
						
							|  |  |  |       $conversationId: conversationId, | 
					
						
							|  |  |  |       $limit: limit || 100, | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return map(rows, row => ({ | 
					
						
							| 
									
										
										
										
											2019-08-09 16:12:29 -07:00
										 |  |  |     json: row.json, | 
					
						
							| 
									
										
										
										
											2019-01-14 13:47:19 -08:00
										 |  |  |     snippet: row.snippet, | 
					
						
							|  |  |  |   })); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-27 17:37:06 -04:00
										 |  |  | async function getMessageCount(conversationId?: string) { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2020-05-27 17:37:06 -04:00
										 |  |  |   const row = conversationId | 
					
						
							|  |  |  |     ? await db.get( | 
					
						
							|  |  |  |         'SELECT count(*) from messages WHERE conversationId = $conversationId;', | 
					
						
							|  |  |  |         { $conversationId: conversationId } | 
					
						
							|  |  |  |       ) | 
					
						
							|  |  |  |     : await db.get('SELECT count(*) from messages;'); | 
					
						
							| 
									
										
										
										
											2018-08-06 16:18:58 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   if (!row) { | 
					
						
							|  |  |  |     throw new Error('getMessageCount: Unable to get count of messages'); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return row['count(*)']; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function saveMessage( | 
					
						
							|  |  |  |   data: MessageType, | 
					
						
							| 
									
										
										
										
											2021-02-04 12:46:55 -08:00
										 |  |  |   { | 
					
						
							|  |  |  |     forceSave, | 
					
						
							|  |  |  |     alreadyInTransaction, | 
					
						
							|  |  |  |   }: { forceSave?: boolean; alreadyInTransaction?: boolean } = {} | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | ) { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |   const { | 
					
						
							| 
									
										
										
										
											2019-01-14 13:47:19 -08:00
										 |  |  |     body, | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |     conversationId, | 
					
						
							|  |  |  |     expires_at, | 
					
						
							|  |  |  |     hasAttachments, | 
					
						
							|  |  |  |     hasFileAttachments, | 
					
						
							|  |  |  |     hasVisualMediaAttachments, | 
					
						
							|  |  |  |     id, | 
					
						
							| 
									
										
										
										
											2019-06-26 12:33:13 -07:00
										 |  |  |     isErased, | 
					
						
							| 
									
										
										
										
											2019-08-05 13:53:15 -07:00
										 |  |  |     isViewOnce, | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |     received_at, | 
					
						
							|  |  |  |     schemaVersion, | 
					
						
							|  |  |  |     sent_at, | 
					
						
							|  |  |  |     source, | 
					
						
							| 
									
										
										
										
											2020-03-05 13:14:58 -08:00
										 |  |  |     sourceUuid, | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |     sourceDevice, | 
					
						
							| 
									
										
										
										
											2018-08-07 12:33:56 -07:00
										 |  |  |     type, | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |     unread, | 
					
						
							| 
									
										
										
										
											2018-08-07 12:33:56 -07:00
										 |  |  |     expireTimer, | 
					
						
							|  |  |  |     expirationStartTimestamp, | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |   } = data; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-07 12:33:56 -07:00
										 |  |  |   const payload = { | 
					
						
							|  |  |  |     $id: id, | 
					
						
							|  |  |  |     $json: objectToJSON(data), | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-14 13:47:19 -08:00
										 |  |  |     $body: body, | 
					
						
							| 
									
										
										
										
											2018-08-07 12:33:56 -07:00
										 |  |  |     $conversationId: conversationId, | 
					
						
							|  |  |  |     $expirationStartTimestamp: expirationStartTimestamp, | 
					
						
							|  |  |  |     $expires_at: expires_at, | 
					
						
							|  |  |  |     $expireTimer: expireTimer, | 
					
						
							|  |  |  |     $hasAttachments: hasAttachments, | 
					
						
							|  |  |  |     $hasFileAttachments: hasFileAttachments, | 
					
						
							|  |  |  |     $hasVisualMediaAttachments: hasVisualMediaAttachments, | 
					
						
							| 
									
										
										
										
											2019-06-26 12:33:13 -07:00
										 |  |  |     $isErased: isErased, | 
					
						
							| 
									
										
										
										
											2019-08-05 13:53:15 -07:00
										 |  |  |     $isViewOnce: isViewOnce, | 
					
						
							| 
									
										
										
										
											2018-08-07 12:33:56 -07:00
										 |  |  |     $received_at: received_at, | 
					
						
							|  |  |  |     $schemaVersion: schemaVersion, | 
					
						
							|  |  |  |     $sent_at: sent_at, | 
					
						
							|  |  |  |     $source: source, | 
					
						
							| 
									
										
										
										
											2020-03-05 13:14:58 -08:00
										 |  |  |     $sourceUuid: sourceUuid, | 
					
						
							| 
									
										
										
										
											2018-08-07 12:33:56 -07:00
										 |  |  |     $sourceDevice: sourceDevice, | 
					
						
							|  |  |  |     $type: type, | 
					
						
							|  |  |  |     $unread: unread, | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |   if (id && !forceSave) { | 
					
						
							| 
									
										
										
										
											2021-02-04 12:46:55 -08:00
										 |  |  |     if (!alreadyInTransaction) { | 
					
						
							|  |  |  |       await db.run('BEGIN TRANSACTION;'); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     try { | 
					
						
							|  |  |  |       await Promise.all([ | 
					
						
							|  |  |  |         db.run( | 
					
						
							|  |  |  |           `UPDATE messages SET
 | 
					
						
							|  |  |  |             id = $id, | 
					
						
							|  |  |  |             json = $json, | 
					
						
							| 
									
										
										
										
											2021-03-04 16:44:57 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 12:46:55 -08:00
										 |  |  |             body = $body, | 
					
						
							|  |  |  |             conversationId = $conversationId, | 
					
						
							|  |  |  |             expirationStartTimestamp = $expirationStartTimestamp, | 
					
						
							|  |  |  |             expires_at = $expires_at, | 
					
						
							|  |  |  |             expireTimer = $expireTimer, | 
					
						
							|  |  |  |             hasAttachments = $hasAttachments, | 
					
						
							|  |  |  |             hasFileAttachments = $hasFileAttachments, | 
					
						
							|  |  |  |             hasVisualMediaAttachments = $hasVisualMediaAttachments, | 
					
						
							|  |  |  |             isErased = $isErased, | 
					
						
							|  |  |  |             isViewOnce = $isViewOnce, | 
					
						
							|  |  |  |             received_at = $received_at, | 
					
						
							|  |  |  |             schemaVersion = $schemaVersion, | 
					
						
							|  |  |  |             sent_at = $sent_at, | 
					
						
							|  |  |  |             source = $source, | 
					
						
							|  |  |  |             sourceUuid = $sourceUuid, | 
					
						
							|  |  |  |             sourceDevice = $sourceDevice, | 
					
						
							|  |  |  |             type = $type, | 
					
						
							|  |  |  |             unread = $unread | 
					
						
							|  |  |  |           WHERE id = $id;`,
 | 
					
						
							|  |  |  |           payload | 
					
						
							|  |  |  |         ), | 
					
						
							|  |  |  |         db.run('DELETE FROM messages_fts WHERE id = $id;', { | 
					
						
							|  |  |  |           $id: id, | 
					
						
							|  |  |  |         }), | 
					
						
							|  |  |  |       ]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (body) { | 
					
						
							|  |  |  |         await db.run( | 
					
						
							|  |  |  |           `INSERT INTO messages_fts(
 | 
					
						
							|  |  |  |              id, | 
					
						
							|  |  |  |              body | 
					
						
							|  |  |  |            ) VALUES ( | 
					
						
							|  |  |  |              $id, | 
					
						
							|  |  |  |              $body | 
					
						
							|  |  |  |            ); | 
					
						
							|  |  |  |           `,
 | 
					
						
							|  |  |  |           { | 
					
						
							|  |  |  |             $id: id, | 
					
						
							|  |  |  |             $body: body, | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         ); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (!alreadyInTransaction) { | 
					
						
							|  |  |  |         await db.run('COMMIT TRANSACTION;'); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } catch (error) { | 
					
						
							|  |  |  |       if (!alreadyInTransaction) { | 
					
						
							|  |  |  |         await db.run('ROLLBACK;'); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       throw error; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     return id; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const toCreate = { | 
					
						
							|  |  |  |     ...data, | 
					
						
							|  |  |  |     id: id || generateUUID(), | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 12:46:55 -08:00
										 |  |  |   if (!alreadyInTransaction) { | 
					
						
							|  |  |  |     await db.run('BEGIN TRANSACTION;'); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 12:46:55 -08:00
										 |  |  |   try { | 
					
						
							| 
									
										
										
										
											2021-03-22 18:09:50 -07:00
										 |  |  |     await db.run('DELETE FROM messages_fts WHERE id = $id;', { | 
					
						
							|  |  |  |       $id: id, | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 12:46:55 -08:00
										 |  |  |     await Promise.all([ | 
					
						
							|  |  |  |       db.run( | 
					
						
							|  |  |  |         `INSERT INTO messages (
 | 
					
						
							|  |  |  |           id, | 
					
						
							|  |  |  |           json, | 
					
						
							| 
									
										
										
										
											2021-03-04 16:44:57 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 12:46:55 -08:00
										 |  |  |           body, | 
					
						
							|  |  |  |           conversationId, | 
					
						
							|  |  |  |           expirationStartTimestamp, | 
					
						
							|  |  |  |           expires_at, | 
					
						
							|  |  |  |           expireTimer, | 
					
						
							|  |  |  |           hasAttachments, | 
					
						
							|  |  |  |           hasFileAttachments, | 
					
						
							|  |  |  |           hasVisualMediaAttachments, | 
					
						
							|  |  |  |           isErased, | 
					
						
							|  |  |  |           isViewOnce, | 
					
						
							|  |  |  |           received_at, | 
					
						
							|  |  |  |           schemaVersion, | 
					
						
							|  |  |  |           sent_at, | 
					
						
							|  |  |  |           source, | 
					
						
							|  |  |  |           sourceUuid, | 
					
						
							|  |  |  |           sourceDevice, | 
					
						
							|  |  |  |           type, | 
					
						
							|  |  |  |           unread | 
					
						
							|  |  |  |         ) values ( | 
					
						
							|  |  |  |           $id, | 
					
						
							|  |  |  |           $json, | 
					
						
							| 
									
										
										
										
											2021-03-04 16:44:57 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 12:46:55 -08:00
										 |  |  |           $body, | 
					
						
							|  |  |  |           $conversationId, | 
					
						
							|  |  |  |           $expirationStartTimestamp, | 
					
						
							|  |  |  |           $expires_at, | 
					
						
							|  |  |  |           $expireTimer, | 
					
						
							|  |  |  |           $hasAttachments, | 
					
						
							|  |  |  |           $hasFileAttachments, | 
					
						
							|  |  |  |           $hasVisualMediaAttachments, | 
					
						
							|  |  |  |           $isErased, | 
					
						
							|  |  |  |           $isViewOnce, | 
					
						
							|  |  |  |           $received_at, | 
					
						
							|  |  |  |           $schemaVersion, | 
					
						
							|  |  |  |           $sent_at, | 
					
						
							|  |  |  |           $source, | 
					
						
							|  |  |  |           $sourceUuid, | 
					
						
							|  |  |  |           $sourceDevice, | 
					
						
							|  |  |  |           $type, | 
					
						
							|  |  |  |           $unread | 
					
						
							|  |  |  |         );`,
 | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           ...payload, | 
					
						
							|  |  |  |           $id: toCreate.id, | 
					
						
							|  |  |  |           $json: objectToJSON(toCreate), | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       ), | 
					
						
							|  |  |  |       db.run( | 
					
						
							|  |  |  |         `INSERT INTO messages_fts(
 | 
					
						
							|  |  |  |            id, | 
					
						
							|  |  |  |            body | 
					
						
							|  |  |  |          ) VALUES ( | 
					
						
							|  |  |  |            $id, | 
					
						
							|  |  |  |            $body | 
					
						
							|  |  |  |          ); | 
					
						
							|  |  |  |         `,
 | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           $id: id, | 
					
						
							|  |  |  |           $body: body, | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       ), | 
					
						
							|  |  |  |     ]); | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-04 12:46:55 -08:00
										 |  |  |     if (!alreadyInTransaction) { | 
					
						
							|  |  |  |       await db.run('COMMIT TRANSACTION;'); | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-02-04 12:46:55 -08:00
										 |  |  |   } catch (error) { | 
					
						
							|  |  |  |     if (!alreadyInTransaction) { | 
					
						
							|  |  |  |       await db.run('ROLLBACK;'); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     throw error; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   return toCreate.id; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2021-02-04 12:46:55 -08:00
										 |  |  | saveMessage.needsSerial = true; | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function saveMessages( | 
					
						
							|  |  |  |   arrayOfMessages: Array<MessageType>, | 
					
						
							|  |  |  |   { forceSave }: { forceSave?: boolean } = {} | 
					
						
							|  |  |  | ) { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |   await db.run('BEGIN TRANSACTION;'); | 
					
						
							| 
									
										
										
										
											2018-07-31 19:29:51 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |   try { | 
					
						
							|  |  |  |     await Promise.all([ | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |       ...map(arrayOfMessages, async message => | 
					
						
							| 
									
										
										
										
											2021-02-04 12:46:55 -08:00
										 |  |  |         saveMessage(message, { forceSave, alreadyInTransaction: true }) | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |       ), | 
					
						
							| 
									
										
										
										
											2018-07-31 19:29:51 -07:00
										 |  |  |     ]); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     await db.run('COMMIT TRANSACTION;'); | 
					
						
							|  |  |  |   } catch (error) { | 
					
						
							|  |  |  |     await db.run('ROLLBACK;'); | 
					
						
							|  |  |  |     throw error; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2019-09-03 13:10:32 -07:00
										 |  |  | saveMessages.needsSerial = true; | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-12 16:42:15 -08:00
										 |  |  | async function removeMessage(id: string) { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2021-02-04 12:46:55 -08:00
										 |  |  |   await db.run('BEGIN TRANSACTION;'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   try { | 
					
						
							|  |  |  |     await Promise.all([ | 
					
						
							|  |  |  |       db.run('DELETE FROM messages WHERE id = $id;', { $id: id }), | 
					
						
							|  |  |  |       db.run('DELETE FROM messages_fts WHERE id = $id;', { $id: id }), | 
					
						
							|  |  |  |     ]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     await db.run('COMMIT TRANSACTION;'); | 
					
						
							|  |  |  |   } catch (error) { | 
					
						
							|  |  |  |     await db.run('ROLLBACK;'); | 
					
						
							|  |  |  |     throw error; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2021-01-12 16:42:15 -08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2021-02-04 12:46:55 -08:00
										 |  |  | removeMessage.needsSerial = true; | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-12 16:42:15 -08:00
										 |  |  | async function removeMessages(ids: Array<string>) { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2021-02-04 12:46:55 -08:00
										 |  |  |   await db.run('BEGIN TRANSACTION;'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   try { | 
					
						
							|  |  |  |     await Promise.all([ | 
					
						
							|  |  |  |       db.run( | 
					
						
							|  |  |  |         `DELETE FROM messages WHERE id IN ( ${ids | 
					
						
							|  |  |  |           .map(() => '?') | 
					
						
							|  |  |  |           .join(', ')} );`,
 | 
					
						
							|  |  |  |         ids | 
					
						
							|  |  |  |       ), | 
					
						
							|  |  |  |       db.run( | 
					
						
							|  |  |  |         `DELETE FROM messages_fts WHERE id IN ( ${ids | 
					
						
							|  |  |  |           .map(() => '?') | 
					
						
							|  |  |  |           .join(', ')} );`,
 | 
					
						
							|  |  |  |         ids | 
					
						
							|  |  |  |       ), | 
					
						
							|  |  |  |     ]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     await db.run('COMMIT TRANSACTION;'); | 
					
						
							|  |  |  |   } catch (error) { | 
					
						
							|  |  |  |     await db.run('ROLLBACK;'); | 
					
						
							|  |  |  |     throw error; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2021-02-04 12:46:55 -08:00
										 |  |  | removeMessages.needsSerial = true; | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function getMessageById(id: string) { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |   const row = await db.get('SELECT * FROM messages WHERE id = $id;', { | 
					
						
							|  |  |  |     $id: id, | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (!row) { | 
					
						
							|  |  |  |     return null; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return jsonToObject(row.json); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function _getAllMessages() { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  |   const rows = await db.all('SELECT json FROM messages ORDER BY id ASC;'); | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  |   return map(rows, row => jsonToObject(row.json)); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | async function getAllMessageIds() { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |   const rows = await db.all('SELECT id FROM messages ORDER BY id ASC;'); | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |   return map(rows, row => row.id); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-05 13:14:58 -08:00
										 |  |  | async function getMessageBySender({ | 
					
						
							|  |  |  |   source, | 
					
						
							|  |  |  |   sourceUuid, | 
					
						
							|  |  |  |   sourceDevice, | 
					
						
							|  |  |  |   sent_at, | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | }: { | 
					
						
							|  |  |  |   source: string; | 
					
						
							|  |  |  |   sourceUuid: string; | 
					
						
							|  |  |  |   sourceDevice: string; | 
					
						
							|  |  |  |   sent_at: number; | 
					
						
							| 
									
										
										
										
											2020-03-05 13:14:58 -08:00
										 |  |  | }) { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2018-08-02 11:19:50 -07:00
										 |  |  |   const rows = await db.all( | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |     `SELECT json FROM messages WHERE
 | 
					
						
							| 
									
										
										
										
											2020-03-05 13:14:58 -08:00
										 |  |  |       (source = $source OR sourceUuid = $sourceUuid) AND | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |       sourceDevice = $sourceDevice AND | 
					
						
							|  |  |  |       sent_at = $sent_at;`,
 | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       $source: source, | 
					
						
							| 
									
										
										
										
											2020-03-05 13:14:58 -08:00
										 |  |  |       $sourceUuid: sourceUuid, | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |       $sourceDevice: sourceDevice, | 
					
						
							|  |  |  |       $sent_at: sent_at, | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return map(rows, row => jsonToObject(row.json)); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function getUnreadByConversation(conversationId: string) { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |   const rows = await db.all( | 
					
						
							|  |  |  |     `SELECT json FROM messages WHERE
 | 
					
						
							| 
									
										
										
										
											2018-08-08 18:32:10 -07:00
										 |  |  |       unread = $unread AND | 
					
						
							|  |  |  |       conversationId = $conversationId | 
					
						
							| 
									
										
										
										
											2021-01-13 08:32:18 -08:00
										 |  |  |      ORDER BY received_at DESC, sent_at DESC;`,
 | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |     { | 
					
						
							|  |  |  |       $unread: 1, | 
					
						
							| 
									
										
										
										
											2018-08-08 18:32:10 -07:00
										 |  |  |       $conversationId: conversationId, | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |     } | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return map(rows, row => jsonToObject(row.json)); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-31 15:42:01 -07:00
										 |  |  | async function getOlderMessagesByConversation( | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   conversationId: string, | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     limit = 100, | 
					
						
							|  |  |  |     receivedAt = Number.MAX_VALUE, | 
					
						
							| 
									
										
										
										
											2021-01-13 08:32:18 -08:00
										 |  |  |     sentAt = Number.MAX_VALUE, | 
					
						
							| 
									
										
										
										
											2020-07-06 10:06:44 -07:00
										 |  |  |     messageId, | 
					
						
							| 
									
										
										
										
											2020-09-24 13:57:54 -07:00
										 |  |  |   }: { | 
					
						
							|  |  |  |     limit?: number; | 
					
						
							|  |  |  |     receivedAt?: number; | 
					
						
							| 
									
										
										
										
											2021-01-13 08:32:18 -08:00
										 |  |  |     sentAt?: number; | 
					
						
							| 
									
										
										
										
											2020-09-24 13:57:54 -07:00
										 |  |  |     messageId?: string; | 
					
						
							|  |  |  |   } = {} | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | ) { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2020-07-06 10:06:44 -07:00
										 |  |  |   let rows; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (messageId) { | 
					
						
							|  |  |  |     rows = await db.all( | 
					
						
							|  |  |  |       `SELECT json FROM messages WHERE
 | 
					
						
							|  |  |  |        conversationId = $conversationId AND | 
					
						
							| 
									
										
										
										
											2021-01-13 08:32:18 -08:00
										 |  |  |        id != $messageId AND | 
					
						
							|  |  |  |        ( | 
					
						
							|  |  |  |          (received_at = $received_at AND sent_at < $sent_at) OR | 
					
						
							|  |  |  |          received_at < $received_at | 
					
						
							|  |  |  |        ) | 
					
						
							|  |  |  |      ORDER BY received_at DESC, sent_at DESC | 
					
						
							| 
									
										
										
										
											2020-07-06 10:06:44 -07:00
										 |  |  |      LIMIT $limit;`,
 | 
					
						
							|  |  |  |       { | 
					
						
							|  |  |  |         $conversationId: conversationId, | 
					
						
							|  |  |  |         $received_at: receivedAt, | 
					
						
							| 
									
										
										
										
											2021-01-13 08:32:18 -08:00
										 |  |  |         $sent_at: sentAt, | 
					
						
							| 
									
										
										
										
											2020-07-06 10:06:44 -07:00
										 |  |  |         $limit: limit, | 
					
						
							|  |  |  |         $messageId: messageId, | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } else { | 
					
						
							|  |  |  |     rows = await db.all( | 
					
						
							|  |  |  |       `SELECT json FROM messages WHERE
 | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |        conversationId = $conversationId AND | 
					
						
							| 
									
										
										
										
											2021-01-13 08:32:18 -08:00
										 |  |  |        ( | 
					
						
							|  |  |  |          (received_at = $received_at AND sent_at < $sent_at) OR | 
					
						
							|  |  |  |          received_at < $received_at | 
					
						
							|  |  |  |        ) | 
					
						
							|  |  |  |      ORDER BY received_at DESC, sent_at DESC | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |      LIMIT $limit;`,
 | 
					
						
							| 
									
										
										
										
											2020-07-06 10:06:44 -07:00
										 |  |  |       { | 
					
						
							|  |  |  |         $conversationId: conversationId, | 
					
						
							|  |  |  |         $received_at: receivedAt, | 
					
						
							| 
									
										
										
										
											2021-01-13 08:32:18 -08:00
										 |  |  |         $sent_at: sentAt, | 
					
						
							| 
									
										
										
										
											2020-07-06 10:06:44 -07:00
										 |  |  |         $limit: limit, | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-09 16:12:29 -07:00
										 |  |  |   return rows.reverse(); | 
					
						
							| 
									
										
										
										
											2019-05-31 15:42:01 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | async function getNewerMessagesByConversation( | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   conversationId: string, | 
					
						
							| 
									
										
										
										
											2021-01-13 08:32:18 -08:00
										 |  |  |   { | 
					
						
							|  |  |  |     limit = 100, | 
					
						
							|  |  |  |     receivedAt = 0, | 
					
						
							|  |  |  |     sentAt = 0, | 
					
						
							|  |  |  |   }: { limit?: number; receivedAt?: number; sentAt?: number } = {} | 
					
						
							| 
									
										
										
										
											2019-05-31 15:42:01 -07:00
										 |  |  | ) { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2019-05-31 15:42:01 -07:00
										 |  |  |   const rows = await db.all( | 
					
						
							|  |  |  |     `SELECT json FROM messages WHERE
 | 
					
						
							|  |  |  |        conversationId = $conversationId AND | 
					
						
							| 
									
										
										
										
											2021-01-13 08:32:18 -08:00
										 |  |  |        ( | 
					
						
							|  |  |  |          (received_at = $received_at AND sent_at > $sent_at) OR | 
					
						
							|  |  |  |          received_at > $received_at | 
					
						
							|  |  |  |        ) | 
					
						
							|  |  |  |      ORDER BY received_at ASC, sent_at ASC | 
					
						
							| 
									
										
										
										
											2019-05-31 15:42:01 -07:00
										 |  |  |      LIMIT $limit;`,
 | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       $conversationId: conversationId, | 
					
						
							|  |  |  |       $received_at: receivedAt, | 
					
						
							| 
									
										
										
										
											2021-01-13 08:32:18 -08:00
										 |  |  |       $sent_at: sentAt, | 
					
						
							| 
									
										
										
										
											2019-05-31 15:42:01 -07:00
										 |  |  |       $limit: limit, | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-09 16:12:29 -07:00
										 |  |  |   return rows; | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function getOldestMessageForConversation(conversationId: string) { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2019-05-31 15:42:01 -07:00
										 |  |  |   const row = await db.get( | 
					
						
							|  |  |  |     `SELECT * FROM messages WHERE
 | 
					
						
							|  |  |  |        conversationId = $conversationId | 
					
						
							| 
									
										
										
										
											2021-01-13 08:32:18 -08:00
										 |  |  |      ORDER BY received_at ASC, sent_at ASC | 
					
						
							| 
									
										
										
										
											2019-05-31 15:42:01 -07:00
										 |  |  |      LIMIT 1;`,
 | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       $conversationId: conversationId, | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (!row) { | 
					
						
							|  |  |  |     return null; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return row; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function getNewestMessageForConversation(conversationId: string) { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2019-05-31 15:42:01 -07:00
										 |  |  |   const row = await db.get( | 
					
						
							|  |  |  |     `SELECT * FROM messages WHERE
 | 
					
						
							|  |  |  |        conversationId = $conversationId | 
					
						
							| 
									
										
										
										
											2021-01-13 08:32:18 -08:00
										 |  |  |      ORDER BY received_at DESC, sent_at DESC | 
					
						
							| 
									
										
										
										
											2019-05-31 15:42:01 -07:00
										 |  |  |      LIMIT 1;`,
 | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       $conversationId: conversationId, | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (!row) { | 
					
						
							|  |  |  |     return null; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return row; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2020-08-06 17:50:54 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-20 09:31:44 -08:00
										 |  |  | async function getLastConversationActivity({ | 
					
						
							|  |  |  |   conversationId, | 
					
						
							|  |  |  |   ourConversationId, | 
					
						
							|  |  |  | }: { | 
					
						
							|  |  |  |   conversationId: string; | 
					
						
							|  |  |  |   ourConversationId: string; | 
					
						
							|  |  |  | }): Promise<MessageType | null> { | 
					
						
							| 
									
										
										
										
											2020-08-06 17:50:54 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							|  |  |  |   const row = await db.get( | 
					
						
							|  |  |  |     `SELECT * FROM messages WHERE
 | 
					
						
							|  |  |  |        conversationId = $conversationId AND | 
					
						
							| 
									
										
										
										
											2021-03-04 16:44:57 -05:00
										 |  |  |        (type IS NULL | 
					
						
							| 
									
										
										
										
											2021-01-20 09:31:44 -08:00
										 |  |  |         OR | 
					
						
							|  |  |  |         type NOT IN ( | 
					
						
							|  |  |  |           'profile-change', | 
					
						
							|  |  |  |           'verified-change', | 
					
						
							|  |  |  |           'message-history-unsynced', | 
					
						
							|  |  |  |           'keychange', | 
					
						
							|  |  |  |           'group-v1-migration' | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  |        ) AND | 
					
						
							|  |  |  |        ( | 
					
						
							|  |  |  |          json_extract(json, '$.expirationTimerUpdate.fromSync') IS NULL | 
					
						
							|  |  |  |          OR | 
					
						
							|  |  |  |          json_extract(json, '$.expirationTimerUpdate.fromSync') != 1 | 
					
						
							|  |  |  |        ) AND NOT | 
					
						
							|  |  |  |        ( | 
					
						
							|  |  |  |          type = 'group-v2-change' AND | 
					
						
							|  |  |  |          json_extract(json, '$.groupV2Change.from') != $ourConversationId AND | 
					
						
							|  |  |  |          json_extract(json, '$.groupV2Change.details.length') = 1 AND | 
					
						
							| 
									
										
										
										
											2021-02-04 11:39:07 -08:00
										 |  |  |          json_extract(json, '$.groupV2Change.details[0].type') = 'member-remove' AND | 
					
						
							| 
									
										
										
										
											2021-01-20 09:31:44 -08:00
										 |  |  |          json_extract(json, '$.groupV2Change.details[0].conversationId') != $ourConversationId | 
					
						
							|  |  |  |        ) | 
					
						
							| 
									
										
										
										
											2021-01-13 08:32:18 -08:00
										 |  |  |      ORDER BY received_at DESC, sent_at DESC | 
					
						
							| 
									
										
										
										
											2020-08-06 17:50:54 -07:00
										 |  |  |      LIMIT 1;`,
 | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       $conversationId: conversationId, | 
					
						
							| 
									
										
										
										
											2021-01-20 09:31:44 -08:00
										 |  |  |       $ourConversationId: ourConversationId, | 
					
						
							| 
									
										
										
										
											2020-08-06 17:50:54 -07:00
										 |  |  |     } | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (!row) { | 
					
						
							|  |  |  |     return null; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return jsonToObject(row.json); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2021-01-20 09:31:44 -08:00
										 |  |  | async function getLastConversationPreview({ | 
					
						
							|  |  |  |   conversationId, | 
					
						
							|  |  |  |   ourConversationId, | 
					
						
							|  |  |  | }: { | 
					
						
							|  |  |  |   conversationId: string; | 
					
						
							|  |  |  |   ourConversationId: string; | 
					
						
							|  |  |  | }): Promise<MessageType | null> { | 
					
						
							| 
									
										
										
										
											2020-08-06 17:50:54 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							|  |  |  |   const row = await db.get( | 
					
						
							|  |  |  |     `SELECT * FROM messages WHERE
 | 
					
						
							|  |  |  |        conversationId = $conversationId AND | 
					
						
							| 
									
										
										
										
											2021-01-20 09:31:44 -08:00
										 |  |  |        ( | 
					
						
							|  |  |  |         type IS NULL | 
					
						
							|  |  |  |         OR | 
					
						
							|  |  |  |         type NOT IN ( | 
					
						
							|  |  |  |           'profile-change', | 
					
						
							|  |  |  |           'verified-change', | 
					
						
							|  |  |  |           'message-history-unsynced', | 
					
						
							|  |  |  |           'group-v1-migration' | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  |        ) AND NOT | 
					
						
							|  |  |  |        ( | 
					
						
							|  |  |  |          type = 'group-v2-change' AND | 
					
						
							|  |  |  |          json_extract(json, '$.groupV2Change.from') != $ourConversationId AND | 
					
						
							|  |  |  |          json_extract(json, '$.groupV2Change.details.length') = 1 AND | 
					
						
							| 
									
										
										
										
											2021-02-04 11:39:07 -08:00
										 |  |  |          json_extract(json, '$.groupV2Change.details[0].type') = 'member-remove' AND | 
					
						
							| 
									
										
										
										
											2021-01-20 09:31:44 -08:00
										 |  |  |          json_extract(json, '$.groupV2Change.details[0].conversationId') != $ourConversationId | 
					
						
							|  |  |  |        ) | 
					
						
							| 
									
										
										
										
											2021-01-13 08:32:18 -08:00
										 |  |  |      ORDER BY received_at DESC, sent_at DESC | 
					
						
							| 
									
										
										
										
											2020-08-06 17:50:54 -07:00
										 |  |  |      LIMIT 1;`,
 | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       $conversationId: conversationId, | 
					
						
							| 
									
										
										
										
											2021-01-20 09:31:44 -08:00
										 |  |  |       $ourConversationId: ourConversationId, | 
					
						
							| 
									
										
										
										
											2020-08-06 17:50:54 -07:00
										 |  |  |     } | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (!row) { | 
					
						
							|  |  |  |     return null; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return jsonToObject(row.json); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function getOldestUnreadMessageForConversation(conversationId: string) { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2019-05-31 15:42:01 -07:00
										 |  |  |   const row = await db.get( | 
					
						
							|  |  |  |     `SELECT * FROM messages WHERE
 | 
					
						
							|  |  |  |        conversationId = $conversationId AND | 
					
						
							|  |  |  |        unread = 1 | 
					
						
							| 
									
										
										
										
											2021-01-13 08:32:18 -08:00
										 |  |  |      ORDER BY received_at ASC, sent_at ASC | 
					
						
							| 
									
										
										
										
											2019-05-31 15:42:01 -07:00
										 |  |  |      LIMIT 1;`,
 | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       $conversationId: conversationId, | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (!row) { | 
					
						
							|  |  |  |     return null; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return row; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function getTotalUnreadForConversation(conversationId: string) { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2019-05-31 15:42:01 -07:00
										 |  |  |   const row = await db.get( | 
					
						
							|  |  |  |     `SELECT count(id) from messages WHERE
 | 
					
						
							|  |  |  |        conversationId = $conversationId AND | 
					
						
							|  |  |  |        unread = 1; | 
					
						
							|  |  |  |     `,
 | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       $conversationId: conversationId, | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (!row) { | 
					
						
							|  |  |  |     throw new Error('getTotalUnreadForConversation: Unable to get count'); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return row['count(id)']; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function getMessageMetricsForConversation(conversationId: string) { | 
					
						
							| 
									
										
										
										
											2019-05-31 15:42:01 -07:00
										 |  |  |   const results = await Promise.all([ | 
					
						
							|  |  |  |     getOldestMessageForConversation(conversationId), | 
					
						
							|  |  |  |     getNewestMessageForConversation(conversationId), | 
					
						
							|  |  |  |     getOldestUnreadMessageForConversation(conversationId), | 
					
						
							|  |  |  |     getTotalUnreadForConversation(conversationId), | 
					
						
							|  |  |  |   ]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const [oldest, newest, oldestUnread, totalUnread] = results; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return { | 
					
						
							| 
									
										
										
										
											2021-01-13 08:32:18 -08:00
										 |  |  |     oldest: oldest ? pick(oldest, ['received_at', 'sent_at', 'id']) : null, | 
					
						
							|  |  |  |     newest: newest ? pick(newest, ['received_at', 'sent_at', 'id']) : null, | 
					
						
							| 
									
										
										
										
											2019-05-31 15:42:01 -07:00
										 |  |  |     oldestUnread: oldestUnread | 
					
						
							| 
									
										
										
										
											2021-01-13 08:32:18 -08:00
										 |  |  |       ? pick(oldestUnread, ['received_at', 'sent_at', 'id']) | 
					
						
							| 
									
										
										
										
											2019-05-31 15:42:01 -07:00
										 |  |  |       : null, | 
					
						
							|  |  |  |     totalUnread, | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2019-09-03 13:10:32 -07:00
										 |  |  | getMessageMetricsForConversation.needsSerial = true; | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-07 14:43:19 -06:00
										 |  |  | async function hasGroupCallHistoryMessage( | 
					
						
							|  |  |  |   conversationId: string, | 
					
						
							|  |  |  |   eraId: string | 
					
						
							|  |  |  | ): Promise<boolean> { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const row: unknown = await db.get( | 
					
						
							|  |  |  |     `
 | 
					
						
							|  |  |  |     SELECT count(*) FROM messages | 
					
						
							|  |  |  |     WHERE conversationId = $conversationId | 
					
						
							|  |  |  |     AND type = 'call-history' | 
					
						
							|  |  |  |     AND json_extract(json, '$.callHistoryDetails.callMode') = 'Group' | 
					
						
							|  |  |  |     AND json_extract(json, '$.callHistoryDetails.eraId') = $eraId | 
					
						
							|  |  |  |     LIMIT 1; | 
					
						
							|  |  |  |     `,
 | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       $conversationId: conversationId, | 
					
						
							|  |  |  |       $eraId: eraId, | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (typeof row === 'object' && row && !Array.isArray(row)) { | 
					
						
							|  |  |  |     const count = Number((row as Record<string, unknown>)['count(*)']); | 
					
						
							|  |  |  |     return Boolean(count); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   return false; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-10 11:28:49 -07:00
										 |  |  | async function migrateConversationMessages( | 
					
						
							|  |  |  |   obsoleteId: string, | 
					
						
							|  |  |  |   currentId: string | 
					
						
							|  |  |  | ) { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   await db.run( | 
					
						
							|  |  |  |     `UPDATE messages SET
 | 
					
						
							|  |  |  |       conversationId = $currentId, | 
					
						
							|  |  |  |       json = json_set(json, '$.conversationId', $currentId) | 
					
						
							|  |  |  |      WHERE conversationId = $obsoleteId;`,
 | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       $obsoleteId: obsoleteId, | 
					
						
							|  |  |  |       $currentId: currentId, | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | migrateConversationMessages.needsSerial = true; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function getMessagesBySentAt(sentAt: number) { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |   const rows = await db.all( | 
					
						
							|  |  |  |     `SELECT * FROM messages
 | 
					
						
							|  |  |  |      WHERE sent_at = $sent_at | 
					
						
							| 
									
										
										
										
											2021-01-13 08:32:18 -08:00
										 |  |  |      ORDER BY received_at DESC, sent_at DESC;`,
 | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |     { | 
					
						
							|  |  |  |       $sent_at: sentAt, | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return map(rows, row => jsonToObject(row.json)); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | async function getExpiredMessages() { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |   const now = Date.now(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const rows = await db.all( | 
					
						
							|  |  |  |     `SELECT json FROM messages WHERE
 | 
					
						
							|  |  |  |       expires_at IS NOT NULL AND | 
					
						
							|  |  |  |       expires_at <= $expires_at | 
					
						
							|  |  |  |      ORDER BY expires_at ASC;`,
 | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       $expires_at: now, | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return map(rows, row => jsonToObject(row.json)); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-07 12:33:56 -07:00
										 |  |  | async function getOutgoingWithoutExpiresAt() { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2018-08-07 12:33:56 -07:00
										 |  |  |   const rows = await db.all(`
 | 
					
						
							|  |  |  |     SELECT json FROM messages | 
					
						
							| 
									
										
										
										
											2020-01-07 01:54:58 +01:00
										 |  |  |     INDEXED BY messages_without_timer | 
					
						
							| 
									
										
										
										
											2018-08-07 12:33:56 -07:00
										 |  |  |     WHERE | 
					
						
							| 
									
										
										
										
											2018-08-08 18:32:10 -07:00
										 |  |  |       expireTimer > 0 AND | 
					
						
							|  |  |  |       expires_at IS NULL AND | 
					
						
							|  |  |  |       type IS 'outgoing' | 
					
						
							| 
									
										
										
										
											2018-08-07 12:33:56 -07:00
										 |  |  |     ORDER BY expires_at ASC; | 
					
						
							|  |  |  |   `);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return map(rows, row => jsonToObject(row.json)); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | async function getNextExpiringMessage() { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  |   // Note: we avoid 'IS NOT NULL' here because it does seem to bypass our index
 | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |   const rows = await db.all(`
 | 
					
						
							|  |  |  |     SELECT json FROM messages | 
					
						
							| 
									
										
										
										
											2018-08-08 18:32:10 -07:00
										 |  |  |     WHERE expires_at > 0 | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |     ORDER BY expires_at ASC | 
					
						
							|  |  |  |     LIMIT 1; | 
					
						
							|  |  |  |   `);
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   if (!rows || rows.length < 1) { | 
					
						
							|  |  |  |     return null; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return jsonToObject(rows[0].json); | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-26 12:33:13 -07:00
										 |  |  | async function getNextTapToViewMessageToAgeOut() { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2019-06-26 12:33:13 -07:00
										 |  |  |   const rows = await db.all(`
 | 
					
						
							|  |  |  |     SELECT json FROM messages | 
					
						
							|  |  |  |     WHERE | 
					
						
							| 
									
										
										
										
											2019-08-05 13:53:15 -07:00
										 |  |  |       isViewOnce = 1 | 
					
						
							| 
									
										
										
										
											2019-06-26 12:33:13 -07:00
										 |  |  |       AND (isErased IS NULL OR isErased != 1) | 
					
						
							| 
									
										
										
										
											2021-01-13 08:32:18 -08:00
										 |  |  |     ORDER BY received_at ASC, sent_at ASC | 
					
						
							| 
									
										
										
										
											2019-06-26 12:33:13 -07:00
										 |  |  |     LIMIT 1; | 
					
						
							|  |  |  |   `);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (!rows || rows.length < 1) { | 
					
						
							|  |  |  |     return null; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return jsonToObject(rows[0].json); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | async function getTapToViewMessagesNeedingErase() { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2019-06-26 12:33:13 -07:00
										 |  |  |   const THIRTY_DAYS_AGO = Date.now() - 30 * 24 * 60 * 60 * 1000; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const rows = await db.all( | 
					
						
							|  |  |  |     `SELECT json FROM messages
 | 
					
						
							|  |  |  |     WHERE | 
					
						
							| 
									
										
										
										
											2019-08-05 13:53:15 -07:00
										 |  |  |       isViewOnce = 1 | 
					
						
							| 
									
										
										
										
											2019-06-26 12:33:13 -07:00
										 |  |  |       AND (isErased IS NULL OR isErased != 1) | 
					
						
							| 
									
										
										
										
											2019-08-05 13:53:15 -07:00
										 |  |  |       AND received_at <= $THIRTY_DAYS_AGO | 
					
						
							| 
									
										
										
										
											2021-01-13 08:32:18 -08:00
										 |  |  |     ORDER BY received_at ASC, sent_at ASC;`,
 | 
					
						
							| 
									
										
										
										
											2019-06-26 12:33:13 -07:00
										 |  |  |     { | 
					
						
							|  |  |  |       $THIRTY_DAYS_AGO: THIRTY_DAYS_AGO, | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return map(rows, row => jsonToObject(row.json)); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function saveUnprocessed( | 
					
						
							|  |  |  |   data: UnprocessedType, | 
					
						
							|  |  |  |   { forceSave }: { forceSave?: boolean } = {} | 
					
						
							|  |  |  | ) { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2019-02-04 17:23:50 -08:00
										 |  |  |   const { id, timestamp, version, attempts, envelope } = data; | 
					
						
							|  |  |  |   if (!id) { | 
					
						
							|  |  |  |     throw new Error('saveUnprocessed: id was falsey'); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   if (forceSave) { | 
					
						
							|  |  |  |     await db.run( | 
					
						
							|  |  |  |       `INSERT INTO unprocessed (
 | 
					
						
							|  |  |  |         id, | 
					
						
							|  |  |  |         timestamp, | 
					
						
							| 
									
										
										
										
											2019-02-04 17:23:50 -08:00
										 |  |  |         version, | 
					
						
							|  |  |  |         attempts, | 
					
						
							|  |  |  |         envelope | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |       ) values ( | 
					
						
							|  |  |  |         $id, | 
					
						
							|  |  |  |         $timestamp, | 
					
						
							| 
									
										
										
										
											2019-02-04 17:23:50 -08:00
										 |  |  |         $version, | 
					
						
							|  |  |  |         $attempts, | 
					
						
							|  |  |  |         $envelope | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |       );`,
 | 
					
						
							|  |  |  |       { | 
					
						
							|  |  |  |         $id: id, | 
					
						
							|  |  |  |         $timestamp: timestamp, | 
					
						
							| 
									
										
										
										
											2019-02-04 17:23:50 -08:00
										 |  |  |         $version: version, | 
					
						
							|  |  |  |         $attempts: attempts, | 
					
						
							|  |  |  |         $envelope: envelope, | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |       } | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return id; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   await db.run( | 
					
						
							|  |  |  |     `UPDATE unprocessed SET
 | 
					
						
							| 
									
										
										
										
											2019-02-04 17:23:50 -08:00
										 |  |  |       timestamp = $timestamp, | 
					
						
							|  |  |  |       version = $version, | 
					
						
							|  |  |  |       attempts = $attempts, | 
					
						
							|  |  |  |       envelope = $envelope | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |     WHERE id = $id;`,
 | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       $id: id, | 
					
						
							|  |  |  |       $timestamp: timestamp, | 
					
						
							| 
									
										
										
										
											2019-02-04 17:23:50 -08:00
										 |  |  |       $version: version, | 
					
						
							|  |  |  |       $attempts: attempts, | 
					
						
							|  |  |  |       $envelope: envelope, | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |     } | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return id; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function saveUnprocesseds( | 
					
						
							|  |  |  |   arrayOfUnprocessed: Array<UnprocessedType>, | 
					
						
							|  |  |  |   { forceSave }: { forceSave?: boolean } = {} | 
					
						
							|  |  |  | ) { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |   await db.run('BEGIN TRANSACTION;'); | 
					
						
							| 
									
										
										
										
											2018-07-31 19:29:51 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |   try { | 
					
						
							|  |  |  |     await Promise.all([ | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |       ...map(arrayOfUnprocessed, async unprocessed => | 
					
						
							| 
									
										
										
										
											2018-07-31 19:29:51 -07:00
										 |  |  |         saveUnprocessed(unprocessed, { forceSave }) | 
					
						
							|  |  |  |       ), | 
					
						
							|  |  |  |     ]); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     await db.run('COMMIT TRANSACTION;'); | 
					
						
							|  |  |  |   } catch (error) { | 
					
						
							|  |  |  |     await db.run('ROLLBACK;'); | 
					
						
							|  |  |  |     throw error; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2019-09-03 13:10:32 -07:00
										 |  |  | saveUnprocesseds.needsSerial = true; | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function updateUnprocessedAttempts(id: string, attempts: number) { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2019-02-04 17:23:50 -08:00
										 |  |  |   await db.run('UPDATE unprocessed SET attempts = $attempts WHERE id = $id;', { | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |     $id: id, | 
					
						
							| 
									
										
										
										
											2019-02-04 17:23:50 -08:00
										 |  |  |     $attempts: attempts, | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |   }); | 
					
						
							| 
									
										
										
										
											2019-02-04 17:23:50 -08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function updateUnprocessedWithData(id: string, data: UnprocessedType) { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2021-02-04 11:43:10 -08:00
										 |  |  |   const { source, sourceUuid, sourceDevice, serverTimestamp, decrypted } = data; | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-04 17:23:50 -08:00
										 |  |  |   await db.run( | 
					
						
							|  |  |  |     `UPDATE unprocessed SET
 | 
					
						
							|  |  |  |       source = $source, | 
					
						
							| 
									
										
										
										
											2021-02-04 11:43:10 -08:00
										 |  |  |       sourceUuid = $sourceUuid, | 
					
						
							| 
									
										
										
										
											2019-02-04 17:23:50 -08:00
										 |  |  |       sourceDevice = $sourceDevice, | 
					
						
							|  |  |  |       serverTimestamp = $serverTimestamp, | 
					
						
							|  |  |  |       decrypted = $decrypted | 
					
						
							|  |  |  |     WHERE id = $id;`,
 | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       $id: id, | 
					
						
							|  |  |  |       $source: source, | 
					
						
							| 
									
										
										
										
											2021-02-04 11:43:10 -08:00
										 |  |  |       $sourceUuid: sourceUuid, | 
					
						
							| 
									
										
										
										
											2019-02-04 17:23:50 -08:00
										 |  |  |       $sourceDevice: sourceDevice, | 
					
						
							|  |  |  |       $serverTimestamp: serverTimestamp, | 
					
						
							|  |  |  |       $decrypted: decrypted, | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function updateUnprocessedsWithData( | 
					
						
							|  |  |  |   arrayOfUnprocessed: Array<UnprocessedType> | 
					
						
							|  |  |  | ) { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2019-09-26 12:56:31 -07:00
										 |  |  |   await db.run('BEGIN TRANSACTION;'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   try { | 
					
						
							|  |  |  |     await Promise.all([ | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |       ...map(arrayOfUnprocessed, async ({ id, data }) => | 
					
						
							| 
									
										
										
										
											2019-09-26 12:56:31 -07:00
										 |  |  |         updateUnprocessedWithData(id, data) | 
					
						
							|  |  |  |       ), | 
					
						
							|  |  |  |     ]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     await db.run('COMMIT TRANSACTION;'); | 
					
						
							|  |  |  |   } catch (error) { | 
					
						
							|  |  |  |     await db.run('ROLLBACK;'); | 
					
						
							|  |  |  |     throw error; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | updateUnprocessedsWithData.needsSerial = true; | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function getUnprocessedById(id: string) { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2019-02-04 17:23:50 -08:00
										 |  |  |   const row = await db.get('SELECT * FROM unprocessed WHERE id = $id;', { | 
					
						
							|  |  |  |     $id: id, | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return row; | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-28 15:51:26 -07:00
										 |  |  | async function getUnprocessedCount() { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2018-09-28 15:51:26 -07:00
										 |  |  |   const row = await db.get('SELECT count(*) from unprocessed;'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (!row) { | 
					
						
							|  |  |  |     throw new Error('getMessageCount: Unable to get count of unprocessed'); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return row['count(*)']; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | async function getAllUnprocessed() { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |   const rows = await db.all( | 
					
						
							| 
									
										
										
										
											2019-02-04 17:23:50 -08:00
										 |  |  |     'SELECT * FROM unprocessed ORDER BY timestamp ASC;' | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |   ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-04 17:23:50 -08:00
										 |  |  |   return rows; | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-26 15:42:45 -08:00
										 |  |  | async function removeUnprocessed(id: string | Array<string>) { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |   if (!Array.isArray(id)) { | 
					
						
							|  |  |  |     await db.run('DELETE FROM unprocessed WHERE id = $id;', { $id: id }); | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (!id.length) { | 
					
						
							|  |  |  |     throw new Error('removeUnprocessed: No ids to delete!'); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // Our node interface doesn't seem to allow you to replace one single ? with an array
 | 
					
						
							|  |  |  |   await db.run( | 
					
						
							|  |  |  |     `DELETE FROM unprocessed WHERE id IN ( ${id.map(() => '?').join(', ')} );`, | 
					
						
							|  |  |  |     id | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | async function removeAllUnprocessed() { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |   await db.run('DELETE FROM unprocessed;'); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  | // Attachment Downloads
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-30 12:15:07 -08:00
										 |  |  | const ATTACHMENT_DOWNLOADS_TABLE = 'attachment_downloads'; | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function getNextAttachmentDownloadJobs( | 
					
						
							|  |  |  |   limit?: number, | 
					
						
							|  |  |  |   options: { timestamp?: number } = {} | 
					
						
							|  |  |  | ) { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							|  |  |  |   const timestamp = | 
					
						
							|  |  |  |     options && options.timestamp ? options.timestamp : Date.now(); | 
					
						
							| 
									
										
										
										
											2019-01-30 12:15:07 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   const rows = await db.all( | 
					
						
							|  |  |  |     `SELECT json FROM attachment_downloads
 | 
					
						
							|  |  |  |     WHERE pending = 0 AND timestamp < $timestamp | 
					
						
							|  |  |  |     ORDER BY timestamp DESC | 
					
						
							|  |  |  |     LIMIT $limit;`,
 | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |       $limit: limit || 3, | 
					
						
							| 
									
										
										
										
											2019-01-30 12:15:07 -08:00
										 |  |  |       $timestamp: timestamp, | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return map(rows, row => jsonToObject(row.json)); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function saveAttachmentDownloadJob(job: AttachmentDownloadJobType) { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2019-01-30 12:15:07 -08:00
										 |  |  |   const { id, pending, timestamp } = job; | 
					
						
							|  |  |  |   if (!id) { | 
					
						
							|  |  |  |     throw new Error( | 
					
						
							|  |  |  |       'saveAttachmentDownloadJob: Provided job did not have a truthy id' | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   await db.run( | 
					
						
							|  |  |  |     `INSERT OR REPLACE INTO attachment_downloads (
 | 
					
						
							|  |  |  |       id, | 
					
						
							|  |  |  |       pending, | 
					
						
							|  |  |  |       timestamp, | 
					
						
							|  |  |  |       json | 
					
						
							|  |  |  |     ) values ( | 
					
						
							|  |  |  |       $id, | 
					
						
							|  |  |  |       $pending, | 
					
						
							|  |  |  |       $timestamp, | 
					
						
							|  |  |  |       $json | 
					
						
							|  |  |  |     )`,
 | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       $id: id, | 
					
						
							|  |  |  |       $pending: pending, | 
					
						
							|  |  |  |       $timestamp: timestamp, | 
					
						
							|  |  |  |       $json: objectToJSON(job), | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function setAttachmentDownloadJobPending(id: string, pending: boolean) { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2019-01-30 12:15:07 -08:00
										 |  |  |   await db.run( | 
					
						
							|  |  |  |     'UPDATE attachment_downloads SET pending = $pending WHERE id = $id;', | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       $id: id, | 
					
						
							|  |  |  |       $pending: pending, | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | async function resetAttachmentDownloadPending() { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2019-01-30 12:15:07 -08:00
										 |  |  |   await db.run( | 
					
						
							|  |  |  |     'UPDATE attachment_downloads SET pending = 0 WHERE pending != 0;' | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function removeAttachmentDownloadJob(id: string) { | 
					
						
							| 
									
										
										
										
											2019-01-30 12:15:07 -08:00
										 |  |  |   return removeById(ATTACHMENT_DOWNLOADS_TABLE, id); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | async function removeAllAttachmentDownloadJobs() { | 
					
						
							|  |  |  |   return removeAllFromTable(ATTACHMENT_DOWNLOADS_TABLE); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  | // Stickers
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function createOrUpdateStickerPack(pack: StickerPackType) { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  |   const { | 
					
						
							|  |  |  |     attemptedStatus, | 
					
						
							|  |  |  |     author, | 
					
						
							|  |  |  |     coverStickerId, | 
					
						
							|  |  |  |     createdAt, | 
					
						
							|  |  |  |     downloadAttempts, | 
					
						
							|  |  |  |     id, | 
					
						
							|  |  |  |     installedAt, | 
					
						
							|  |  |  |     key, | 
					
						
							|  |  |  |     lastUsed, | 
					
						
							|  |  |  |     status, | 
					
						
							|  |  |  |     stickerCount, | 
					
						
							|  |  |  |     title, | 
					
						
							|  |  |  |   } = pack; | 
					
						
							|  |  |  |   if (!id) { | 
					
						
							|  |  |  |     throw new Error( | 
					
						
							|  |  |  |       'createOrUpdateStickerPack: Provided data did not have a truthy id' | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-23 18:27:42 -07:00
										 |  |  |   const rows = await db.all('SELECT id FROM sticker_packs WHERE id = $id;', { | 
					
						
							|  |  |  |     $id: id, | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  |   const payload = { | 
					
						
							|  |  |  |     $attemptedStatus: attemptedStatus, | 
					
						
							|  |  |  |     $author: author, | 
					
						
							|  |  |  |     $coverStickerId: coverStickerId, | 
					
						
							|  |  |  |     $createdAt: createdAt || Date.now(), | 
					
						
							|  |  |  |     $downloadAttempts: downloadAttempts || 1, | 
					
						
							|  |  |  |     $id: id, | 
					
						
							|  |  |  |     $installedAt: installedAt, | 
					
						
							|  |  |  |     $key: key, | 
					
						
							|  |  |  |     $lastUsed: lastUsed || null, | 
					
						
							|  |  |  |     $status: status, | 
					
						
							|  |  |  |     $stickerCount: stickerCount, | 
					
						
							|  |  |  |     $title: title, | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (rows && rows.length) { | 
					
						
							|  |  |  |     await db.run( | 
					
						
							|  |  |  |       `UPDATE sticker_packs SET
 | 
					
						
							|  |  |  |         attemptedStatus = $attemptedStatus, | 
					
						
							|  |  |  |         author = $author, | 
					
						
							|  |  |  |         coverStickerId = $coverStickerId, | 
					
						
							|  |  |  |         createdAt = $createdAt, | 
					
						
							|  |  |  |         downloadAttempts = $downloadAttempts, | 
					
						
							|  |  |  |         installedAt = $installedAt, | 
					
						
							|  |  |  |         key = $key, | 
					
						
							|  |  |  |         lastUsed = $lastUsed, | 
					
						
							|  |  |  |         status = $status, | 
					
						
							|  |  |  |         stickerCount = $stickerCount, | 
					
						
							|  |  |  |         title = $title | 
					
						
							|  |  |  |       WHERE id = $id;`,
 | 
					
						
							|  |  |  |       payload | 
					
						
							|  |  |  |     ); | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-23 18:27:42 -07:00
										 |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  |   await db.run( | 
					
						
							| 
									
										
										
										
											2019-05-23 18:27:42 -07:00
										 |  |  |     `INSERT INTO sticker_packs (
 | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  |       attemptedStatus, | 
					
						
							|  |  |  |       author, | 
					
						
							|  |  |  |       coverStickerId, | 
					
						
							|  |  |  |       createdAt, | 
					
						
							|  |  |  |       downloadAttempts, | 
					
						
							|  |  |  |       id, | 
					
						
							|  |  |  |       installedAt, | 
					
						
							|  |  |  |       key, | 
					
						
							|  |  |  |       lastUsed, | 
					
						
							|  |  |  |       status, | 
					
						
							|  |  |  |       stickerCount, | 
					
						
							|  |  |  |       title | 
					
						
							|  |  |  |     ) values ( | 
					
						
							|  |  |  |       $attemptedStatus, | 
					
						
							|  |  |  |       $author, | 
					
						
							|  |  |  |       $coverStickerId, | 
					
						
							|  |  |  |       $createdAt, | 
					
						
							|  |  |  |       $downloadAttempts, | 
					
						
							|  |  |  |       $id, | 
					
						
							|  |  |  |       $installedAt, | 
					
						
							|  |  |  |       $key, | 
					
						
							|  |  |  |       $lastUsed, | 
					
						
							|  |  |  |       $status, | 
					
						
							|  |  |  |       $stickerCount, | 
					
						
							|  |  |  |       $title | 
					
						
							|  |  |  |     )`,
 | 
					
						
							| 
									
										
										
										
											2019-05-23 18:27:42 -07:00
										 |  |  |     payload | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  |   ); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function updateStickerPackStatus( | 
					
						
							|  |  |  |   id: string, | 
					
						
							|  |  |  |   status: StickerPackStatusType, | 
					
						
							|  |  |  |   options?: { timestamp: number } | 
					
						
							|  |  |  | ) { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							|  |  |  |   const timestamp = options ? options.timestamp || Date.now() : Date.now(); | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  |   const installedAt = status === 'installed' ? timestamp : null; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   await db.run( | 
					
						
							|  |  |  |     `UPDATE sticker_packs
 | 
					
						
							|  |  |  |     SET status = $status, installedAt = $installedAt | 
					
						
							|  |  |  |     WHERE id = $id; | 
					
						
							|  |  |  |     )`,
 | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       $id: id, | 
					
						
							|  |  |  |       $status: status, | 
					
						
							|  |  |  |       $installedAt: installedAt, | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2021-01-27 14:39:45 -08:00
										 |  |  | async function clearAllErrorStickerPackAttempts(): Promise<void> { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   await db.run( | 
					
						
							|  |  |  |     "UPDATE sticker_packs SET downloadAttempts = 0 WHERE status = 'error';" | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function createOrUpdateSticker(sticker: StickerType) { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  |   const { | 
					
						
							|  |  |  |     emoji, | 
					
						
							|  |  |  |     height, | 
					
						
							|  |  |  |     id, | 
					
						
							|  |  |  |     isCoverOnly, | 
					
						
							|  |  |  |     lastUsed, | 
					
						
							|  |  |  |     packId, | 
					
						
							|  |  |  |     path, | 
					
						
							|  |  |  |     width, | 
					
						
							|  |  |  |   } = sticker; | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  |   if (!isNumber(id)) { | 
					
						
							|  |  |  |     throw new Error( | 
					
						
							|  |  |  |       'createOrUpdateSticker: Provided data did not have a numeric id' | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   if (!packId) { | 
					
						
							|  |  |  |     throw new Error( | 
					
						
							|  |  |  |       'createOrUpdateSticker: Provided data did not have a truthy id' | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   await db.run( | 
					
						
							|  |  |  |     `INSERT OR REPLACE INTO stickers (
 | 
					
						
							|  |  |  |       emoji, | 
					
						
							|  |  |  |       height, | 
					
						
							|  |  |  |       id, | 
					
						
							|  |  |  |       isCoverOnly, | 
					
						
							|  |  |  |       lastUsed, | 
					
						
							|  |  |  |       packId, | 
					
						
							|  |  |  |       path, | 
					
						
							|  |  |  |       width | 
					
						
							|  |  |  |     ) values ( | 
					
						
							|  |  |  |       $emoji, | 
					
						
							|  |  |  |       $height, | 
					
						
							|  |  |  |       $id, | 
					
						
							|  |  |  |       $isCoverOnly, | 
					
						
							|  |  |  |       $lastUsed, | 
					
						
							|  |  |  |       $packId, | 
					
						
							|  |  |  |       $path, | 
					
						
							|  |  |  |       $width | 
					
						
							|  |  |  |     )`,
 | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       $emoji: emoji, | 
					
						
							|  |  |  |       $height: height, | 
					
						
							|  |  |  |       $id: id, | 
					
						
							|  |  |  |       $isCoverOnly: isCoverOnly, | 
					
						
							|  |  |  |       $lastUsed: lastUsed, | 
					
						
							|  |  |  |       $packId: packId, | 
					
						
							|  |  |  |       $path: path, | 
					
						
							|  |  |  |       $width: width, | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function updateStickerLastUsed( | 
					
						
							|  |  |  |   packId: string, | 
					
						
							|  |  |  |   stickerId: number, | 
					
						
							|  |  |  |   lastUsed: number | 
					
						
							|  |  |  | ) { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  |   await db.run( | 
					
						
							|  |  |  |     `UPDATE stickers
 | 
					
						
							|  |  |  |     SET lastUsed = $lastUsed | 
					
						
							|  |  |  |     WHERE id = $id AND packId = $packId;`,
 | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       $id: stickerId, | 
					
						
							|  |  |  |       $packId: packId, | 
					
						
							|  |  |  |       $lastUsed: lastUsed, | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  |   await db.run( | 
					
						
							|  |  |  |     `UPDATE sticker_packs
 | 
					
						
							|  |  |  |     SET lastUsed = $lastUsed | 
					
						
							|  |  |  |     WHERE id = $id;`,
 | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       $id: packId, | 
					
						
							|  |  |  |       $lastUsed: lastUsed, | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function addStickerPackReference(messageId: string, packId: string) { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  |   if (!messageId) { | 
					
						
							|  |  |  |     throw new Error( | 
					
						
							|  |  |  |       'addStickerPackReference: Provided data did not have a truthy messageId' | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   if (!packId) { | 
					
						
							|  |  |  |     throw new Error( | 
					
						
							|  |  |  |       'addStickerPackReference: Provided data did not have a truthy packId' | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   await db.run( | 
					
						
							|  |  |  |     `INSERT OR REPLACE INTO sticker_references (
 | 
					
						
							|  |  |  |       messageId, | 
					
						
							|  |  |  |       packId | 
					
						
							|  |  |  |     ) values ( | 
					
						
							|  |  |  |       $messageId, | 
					
						
							|  |  |  |       $packId | 
					
						
							|  |  |  |     )`,
 | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       $messageId: messageId, | 
					
						
							|  |  |  |       $packId: packId, | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function deleteStickerPackReference(messageId: string, packId: string) { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  |   if (!messageId) { | 
					
						
							|  |  |  |     throw new Error( | 
					
						
							|  |  |  |       'addStickerPackReference: Provided data did not have a truthy messageId' | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   if (!packId) { | 
					
						
							|  |  |  |     throw new Error( | 
					
						
							|  |  |  |       'addStickerPackReference: Provided data did not have a truthy packId' | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   try { | 
					
						
							|  |  |  |     // We use an immediate transaction here to immediately acquire an exclusive lock,
 | 
					
						
							|  |  |  |     //   which would normally only happen when we did our first write.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // We need this to ensure that our five queries are all atomic, with no other changes
 | 
					
						
							|  |  |  |     //   happening while we do it:
 | 
					
						
							|  |  |  |     // 1. Delete our target messageId/packId references
 | 
					
						
							|  |  |  |     // 2. Check the number of references still pointing at packId
 | 
					
						
							|  |  |  |     // 3. If that number is zero, get pack from sticker_packs database
 | 
					
						
							|  |  |  |     // 4. If it's not installed, then grab all of its sticker paths
 | 
					
						
							|  |  |  |     // 5. If it's not installed, then sticker pack (which cascades to all stickers and
 | 
					
						
							|  |  |  |     //      references)
 | 
					
						
							|  |  |  |     await db.run('BEGIN IMMEDIATE TRANSACTION;'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     await db.run( | 
					
						
							|  |  |  |       `DELETE FROM sticker_references
 | 
					
						
							|  |  |  |       WHERE messageId = $messageId AND packId = $packId;`,
 | 
					
						
							|  |  |  |       { | 
					
						
							|  |  |  |         $messageId: messageId, | 
					
						
							|  |  |  |         $packId: packId, | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const countRow = await db.get( | 
					
						
							|  |  |  |       `SELECT count(*) FROM sticker_references
 | 
					
						
							|  |  |  |       WHERE packId = $packId;`,
 | 
					
						
							|  |  |  |       { $packId: packId } | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |     if (!countRow) { | 
					
						
							|  |  |  |       throw new Error( | 
					
						
							|  |  |  |         'deleteStickerPackReference: Unable to get count of references' | 
					
						
							|  |  |  |       ); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     const count = countRow['count(*)']; | 
					
						
							|  |  |  |     if (count > 0) { | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |       await db.run('COMMIT TRANSACTION;'); | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |       return []; | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const packRow = await db.get( | 
					
						
							|  |  |  |       `SELECT status FROM sticker_packs
 | 
					
						
							|  |  |  |       WHERE id = $packId;`,
 | 
					
						
							|  |  |  |       { $packId: packId } | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |     if (!packRow) { | 
					
						
							|  |  |  |       console.log('deleteStickerPackReference: did not find referenced pack'); | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |       await db.run('COMMIT TRANSACTION;'); | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |       return []; | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  |     } | 
					
						
							|  |  |  |     const { status } = packRow; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (status === 'installed') { | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |       await db.run('COMMIT TRANSACTION;'); | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |       return []; | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const stickerPathRows = await db.all( | 
					
						
							|  |  |  |       `SELECT path FROM stickers
 | 
					
						
							|  |  |  |       WHERE packId = $packId;`,
 | 
					
						
							|  |  |  |       { | 
					
						
							|  |  |  |         $packId: packId, | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |     await db.run( | 
					
						
							|  |  |  |       `DELETE FROM sticker_packs
 | 
					
						
							|  |  |  |       WHERE id = $packId;`,
 | 
					
						
							|  |  |  |       { $packId: packId } | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     await db.run('COMMIT TRANSACTION;'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return (stickerPathRows || []).map(row => row.path); | 
					
						
							|  |  |  |   } catch (error) { | 
					
						
							|  |  |  |     await db.run('ROLLBACK;'); | 
					
						
							|  |  |  |     throw error; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2019-09-03 13:10:32 -07:00
										 |  |  | deleteStickerPackReference.needsSerial = true; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function deleteStickerPack(packId: string) { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  |   if (!packId) { | 
					
						
							|  |  |  |     throw new Error( | 
					
						
							|  |  |  |       'deleteStickerPack: Provided data did not have a truthy packId' | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   try { | 
					
						
							|  |  |  |     // We use an immediate transaction here to immediately acquire an exclusive lock,
 | 
					
						
							|  |  |  |     //   which would normally only happen when we did our first write.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // We need this to ensure that our two queries are atomic, with no other changes
 | 
					
						
							|  |  |  |     //   happening while we do it:
 | 
					
						
							|  |  |  |     // 1. Grab all of target pack's sticker paths
 | 
					
						
							|  |  |  |     // 2. Delete sticker pack (which cascades to all stickers and references)
 | 
					
						
							|  |  |  |     await db.run('BEGIN IMMEDIATE TRANSACTION;'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const stickerPathRows = await db.all( | 
					
						
							|  |  |  |       `SELECT path FROM stickers
 | 
					
						
							|  |  |  |       WHERE packId = $packId;`,
 | 
					
						
							|  |  |  |       { | 
					
						
							|  |  |  |         $packId: packId, | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |     await db.run( | 
					
						
							|  |  |  |       `DELETE FROM sticker_packs
 | 
					
						
							|  |  |  |       WHERE id = $packId;`,
 | 
					
						
							|  |  |  |       { $packId: packId } | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     await db.run('COMMIT TRANSACTION;'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return (stickerPathRows || []).map(row => row.path); | 
					
						
							|  |  |  |   } catch (error) { | 
					
						
							|  |  |  |     await db.run('ROLLBACK;'); | 
					
						
							|  |  |  |     throw error; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2019-09-03 13:10:32 -07:00
										 |  |  | deleteStickerPack.needsSerial = true; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  | async function getStickerCount() { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  |   const row = await db.get('SELECT count(*) from stickers;'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (!row) { | 
					
						
							|  |  |  |     throw new Error('getStickerCount: Unable to get count of stickers'); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return row['count(*)']; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | async function getAllStickerPacks() { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  |   const rows = await db.all( | 
					
						
							|  |  |  |     `SELECT * FROM sticker_packs
 | 
					
						
							|  |  |  |     ORDER BY installedAt DESC, createdAt DESC`
 | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return rows || []; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | async function getAllStickers() { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  |   const rows = await db.all( | 
					
						
							|  |  |  |     `SELECT * FROM stickers
 | 
					
						
							|  |  |  |     ORDER BY packId ASC, id ASC`
 | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return rows || []; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function getRecentStickers({ limit }: { limit?: number } = {}) { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  |   // Note: we avoid 'IS NOT NULL' here because it does seem to bypass our index
 | 
					
						
							|  |  |  |   const rows = await db.all( | 
					
						
							|  |  |  |     `SELECT stickers.* FROM stickers
 | 
					
						
							|  |  |  |     JOIN sticker_packs on stickers.packId = sticker_packs.id | 
					
						
							|  |  |  |     WHERE stickers.lastUsed > 0 AND sticker_packs.status = 'installed' | 
					
						
							|  |  |  |     ORDER BY stickers.lastUsed DESC | 
					
						
							|  |  |  |     LIMIT $limit`,
 | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       $limit: limit || 24, | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return rows || []; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-24 16:58:27 -07:00
										 |  |  | // Emojis
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function updateEmojiUsage( | 
					
						
							|  |  |  |   shortName: string, | 
					
						
							|  |  |  |   timeUsed: number = Date.now() | 
					
						
							|  |  |  | ) { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2019-05-24 16:58:27 -07:00
										 |  |  |   await db.run('BEGIN TRANSACTION;'); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |   try { | 
					
						
							|  |  |  |     const rows = await db.get( | 
					
						
							|  |  |  |       'SELECT * FROM emojis WHERE shortName = $shortName;', | 
					
						
							|  |  |  |       { | 
					
						
							|  |  |  |         $shortName: shortName, | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (rows) { | 
					
						
							|  |  |  |       await db.run( | 
					
						
							|  |  |  |         'UPDATE emojis SET lastUsage = $timeUsed WHERE shortName = $shortName;', | 
					
						
							|  |  |  |         { $shortName: shortName, $timeUsed: timeUsed } | 
					
						
							|  |  |  |       ); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       await db.run( | 
					
						
							|  |  |  |         'INSERT INTO emojis(shortName, lastUsage) VALUES ($shortName, $timeUsed);', | 
					
						
							|  |  |  |         { $shortName: shortName, $timeUsed: timeUsed } | 
					
						
							|  |  |  |       ); | 
					
						
							| 
									
										
										
										
											2019-05-24 16:58:27 -07:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     await db.run('COMMIT TRANSACTION;'); | 
					
						
							|  |  |  |   } catch (error) { | 
					
						
							|  |  |  |     await db.run('ROLLBACK;'); | 
					
						
							|  |  |  |     throw error; | 
					
						
							| 
									
										
										
										
											2019-05-24 16:58:27 -07:00
										 |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2019-09-03 13:10:32 -07:00
										 |  |  | updateEmojiUsage.needsSerial = true; | 
					
						
							| 
									
										
										
										
											2019-05-24 16:58:27 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-24 14:53:21 -07:00
										 |  |  | async function getRecentEmojis(limit = 32) { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2019-05-24 16:58:27 -07:00
										 |  |  |   const rows = await db.all( | 
					
						
							|  |  |  |     'SELECT * FROM emojis ORDER BY lastUsage DESC LIMIT $limit;', | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       $limit: limit, | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return rows || []; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  | // All data in database
 | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | async function removeAll() { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |   await db.run('BEGIN TRANSACTION;'); | 
					
						
							| 
									
										
										
										
											2018-07-31 19:29:51 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |   try { | 
					
						
							|  |  |  |     await Promise.all([ | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |       db.run('DELETE FROM conversations;'), | 
					
						
							|  |  |  |       db.run('DELETE FROM identityKeys;'), | 
					
						
							|  |  |  |       db.run('DELETE FROM items;'), | 
					
						
							| 
									
										
										
										
											2018-07-31 19:29:51 -07:00
										 |  |  |       db.run('DELETE FROM messages;'), | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |       db.run('DELETE FROM preKeys;'), | 
					
						
							|  |  |  |       db.run('DELETE FROM sessions;'), | 
					
						
							|  |  |  |       db.run('DELETE FROM signedPreKeys;'), | 
					
						
							|  |  |  |       db.run('DELETE FROM unprocessed;'), | 
					
						
							| 
									
										
										
										
											2019-01-30 12:15:07 -08:00
										 |  |  |       db.run('DELETE FROM attachment_downloads;'), | 
					
						
							|  |  |  |       db.run('DELETE FROM messages_fts;'), | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  |       db.run('DELETE FROM stickers;'), | 
					
						
							|  |  |  |       db.run('DELETE FROM sticker_packs;'), | 
					
						
							|  |  |  |       db.run('DELETE FROM sticker_references;'), | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |     ]); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     await db.run('COMMIT TRANSACTION;'); | 
					
						
							|  |  |  |   } catch (error) { | 
					
						
							|  |  |  |     await db.run('ROLLBACK;'); | 
					
						
							|  |  |  |     throw error; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2019-09-03 13:10:32 -07:00
										 |  |  | removeAll.needsSerial = true; | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | // Anything that isn't user-visible data
 | 
					
						
							|  |  |  | async function removeAllConfiguration() { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |   await db.run('BEGIN TRANSACTION;'); | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |   try { | 
					
						
							|  |  |  |     await Promise.all([ | 
					
						
							| 
									
										
										
										
											2018-10-17 18:01:21 -07:00
										 |  |  |       db.run('DELETE FROM identityKeys;'), | 
					
						
							|  |  |  |       db.run('DELETE FROM items;'), | 
					
						
							|  |  |  |       db.run('DELETE FROM preKeys;'), | 
					
						
							|  |  |  |       db.run('DELETE FROM sessions;'), | 
					
						
							|  |  |  |       db.run('DELETE FROM signedPreKeys;'), | 
					
						
							| 
									
										
										
										
											2018-07-31 19:29:51 -07:00
										 |  |  |       db.run('DELETE FROM unprocessed;'), | 
					
						
							|  |  |  |     ]); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-24 11:43:45 -07:00
										 |  |  |     await db.run('COMMIT TRANSACTION;'); | 
					
						
							|  |  |  |   } catch (error) { | 
					
						
							|  |  |  |     await db.run('ROLLBACK;'); | 
					
						
							|  |  |  |     throw error; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2019-09-03 13:10:32 -07:00
										 |  |  | removeAllConfiguration.needsSerial = true; | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function getMessagesNeedingUpgrade( | 
					
						
							|  |  |  |   limit: number, | 
					
						
							|  |  |  |   { maxVersion }: { maxVersion: number } | 
					
						
							|  |  |  | ) { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |   const rows = await db.all( | 
					
						
							|  |  |  |     `SELECT json FROM messages
 | 
					
						
							| 
									
										
										
										
											2018-08-08 18:32:10 -07:00
										 |  |  |      WHERE schemaVersion IS NULL OR schemaVersion < $maxVersion | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |      LIMIT $limit;`,
 | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       $maxVersion: maxVersion, | 
					
						
							|  |  |  |       $limit: limit, | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return map(rows, row => jsonToObject(row.json)); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | async function getMessagesWithVisualMediaAttachments( | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   conversationId: string, | 
					
						
							|  |  |  |   { limit }: { limit: number } | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  | ) { | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |   const rows = await db.all( | 
					
						
							|  |  |  |     `SELECT json FROM messages WHERE
 | 
					
						
							|  |  |  |       conversationId = $conversationId AND | 
					
						
							|  |  |  |       hasVisualMediaAttachments = 1 | 
					
						
							| 
									
										
										
										
											2021-01-13 08:32:18 -08:00
										 |  |  |      ORDER BY received_at DESC, sent_at DESC | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |      LIMIT $limit;`,
 | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       $conversationId: conversationId, | 
					
						
							|  |  |  |       $limit: limit, | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return map(rows, row => jsonToObject(row.json)); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function getMessagesWithFileAttachments( | 
					
						
							|  |  |  |   conversationId: string, | 
					
						
							|  |  |  |   { limit }: { limit: number } | 
					
						
							|  |  |  | ) { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |   const rows = await db.all( | 
					
						
							|  |  |  |     `SELECT json FROM messages WHERE
 | 
					
						
							|  |  |  |       conversationId = $conversationId AND | 
					
						
							|  |  |  |       hasFileAttachments = 1 | 
					
						
							| 
									
										
										
										
											2021-01-13 08:32:18 -08:00
										 |  |  |      ORDER BY received_at DESC, sent_at DESC | 
					
						
							| 
									
										
										
										
											2018-07-26 18:13:56 -07:00
										 |  |  |      LIMIT $limit;`,
 | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       $conversationId: conversationId, | 
					
						
							|  |  |  |       $limit: limit, | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return map(rows, row => jsonToObject(row.json)); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2018-08-06 16:18:58 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | function getExternalFilesForMessage(message: MessageType) { | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  |   const { attachments, contact, quote, preview, sticker } = message; | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const files: Array<string> = []; | 
					
						
							| 
									
										
										
										
											2018-08-06 16:18:58 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   forEach(attachments, attachment => { | 
					
						
							|  |  |  |     const { path: file, thumbnail, screenshot } = attachment; | 
					
						
							|  |  |  |     if (file) { | 
					
						
							|  |  |  |       files.push(file); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (thumbnail && thumbnail.path) { | 
					
						
							|  |  |  |       files.push(thumbnail.path); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (screenshot && screenshot.path) { | 
					
						
							|  |  |  |       files.push(screenshot.path); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (quote && quote.attachments && quote.attachments.length) { | 
					
						
							|  |  |  |     forEach(quote.attachments, attachment => { | 
					
						
							|  |  |  |       const { thumbnail } = attachment; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (thumbnail && thumbnail.path) { | 
					
						
							|  |  |  |         files.push(thumbnail.path); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (contact && contact.length) { | 
					
						
							|  |  |  |     forEach(contact, item => { | 
					
						
							|  |  |  |       const { avatar } = item; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (avatar && avatar.avatar && avatar.avatar.path) { | 
					
						
							|  |  |  |         files.push(avatar.avatar.path); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-15 19:03:56 -08:00
										 |  |  |   if (preview && preview.length) { | 
					
						
							|  |  |  |     forEach(preview, item => { | 
					
						
							|  |  |  |       const { image } = item; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (image && image.path) { | 
					
						
							|  |  |  |         files.push(image.path); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  |   if (sticker && sticker.data && sticker.data.path) { | 
					
						
							|  |  |  |     files.push(sticker.data.path); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (sticker.data.thumbnail && sticker.data.thumbnail.path) { | 
					
						
							|  |  |  |       files.push(sticker.data.thumbnail.path); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-06 16:18:58 -07:00
										 |  |  |   return files; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-18 12:09:27 -05:00
										 |  |  | function getExternalFilesForConversation( | 
					
						
							|  |  |  |   conversation: Pick<ConversationType, 'avatar' | 'profileAvatar'> | 
					
						
							|  |  |  | ) { | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  |   const { avatar, profileAvatar } = conversation; | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const files: Array<string> = []; | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   if (avatar && avatar.path) { | 
					
						
							|  |  |  |     files.push(avatar.path); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (profileAvatar && profileAvatar.path) { | 
					
						
							|  |  |  |     files.push(profileAvatar.path); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return files; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-18 12:09:27 -05:00
										 |  |  | function getExternalDraftFilesForConversation( | 
					
						
							|  |  |  |   conversation: Pick<ConversationType, 'draftAttachments'> | 
					
						
							|  |  |  | ) { | 
					
						
							| 
									
										
										
										
											2019-08-06 17:40:25 -07:00
										 |  |  |   const draftAttachments = conversation.draftAttachments || []; | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   const files: Array<string> = []; | 
					
						
							| 
									
										
										
										
											2019-08-06 17:40:25 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   forEach(draftAttachments, attachment => { | 
					
						
							|  |  |  |     const { path: file, screenshotPath } = attachment; | 
					
						
							|  |  |  |     if (file) { | 
					
						
							|  |  |  |       files.push(file); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (screenshotPath) { | 
					
						
							|  |  |  |       files.push(screenshotPath); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return files; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function removeKnownAttachments(allAttachments: Array<string>) { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							|  |  |  |   const lookup: Dictionary<boolean> = fromPairs( | 
					
						
							|  |  |  |     map(allAttachments, file => [file, true]) | 
					
						
							|  |  |  |   ); | 
					
						
							| 
									
										
										
										
											2018-08-06 16:18:58 -07:00
										 |  |  |   const chunkSize = 50; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const total = await getMessageCount(); | 
					
						
							|  |  |  |   console.log( | 
					
						
							|  |  |  |     `removeKnownAttachments: About to iterate through ${total} messages` | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   let count = 0; | 
					
						
							|  |  |  |   let complete = false; | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |   let id: string | number = ''; | 
					
						
							| 
									
										
										
										
											2018-08-06 16:18:58 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   while (!complete) { | 
					
						
							|  |  |  |     const rows = await db.all( | 
					
						
							|  |  |  |       `SELECT json FROM messages
 | 
					
						
							|  |  |  |        WHERE id > $id | 
					
						
							|  |  |  |        ORDER BY id ASC | 
					
						
							|  |  |  |        LIMIT $chunkSize;`,
 | 
					
						
							|  |  |  |       { | 
					
						
							|  |  |  |         $id: id, | 
					
						
							|  |  |  |         $chunkSize: chunkSize, | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |     const messages: Array<MessageType> = map(rows, row => | 
					
						
							|  |  |  |       jsonToObject(row.json) | 
					
						
							|  |  |  |     ); | 
					
						
							| 
									
										
										
										
											2018-08-06 16:18:58 -07:00
										 |  |  |     forEach(messages, message => { | 
					
						
							|  |  |  |       const externalFiles = getExternalFilesForMessage(message); | 
					
						
							|  |  |  |       forEach(externalFiles, file => { | 
					
						
							|  |  |  |         delete lookup[file]; | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |     const lastMessage: MessageType | undefined = last(messages); | 
					
						
							| 
									
										
										
										
											2018-08-06 16:18:58 -07:00
										 |  |  |     if (lastMessage) { | 
					
						
							|  |  |  |       ({ id } = lastMessage); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     complete = messages.length < chunkSize; | 
					
						
							|  |  |  |     count += messages.length; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   console.log(`removeKnownAttachments: Done processing ${count} messages`); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  |   complete = false; | 
					
						
							|  |  |  |   count = 0; | 
					
						
							|  |  |  |   // Though conversations.id is a string, this ensures that, when coerced, this
 | 
					
						
							|  |  |  |   //   value is still a string but it's smaller than every other string.
 | 
					
						
							|  |  |  |   id = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const conversationTotal = await getConversationCount(); | 
					
						
							|  |  |  |   console.log( | 
					
						
							|  |  |  |     `removeKnownAttachments: About to iterate through ${conversationTotal} conversations` | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   while (!complete) { | 
					
						
							|  |  |  |     const rows = await db.all( | 
					
						
							|  |  |  |       `SELECT json FROM conversations
 | 
					
						
							|  |  |  |        WHERE id > $id | 
					
						
							|  |  |  |        ORDER BY id ASC | 
					
						
							|  |  |  |        LIMIT $chunkSize;`,
 | 
					
						
							|  |  |  |       { | 
					
						
							|  |  |  |         $id: id, | 
					
						
							|  |  |  |         $chunkSize: chunkSize, | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |     const conversations: Array<ConversationType> = map(rows, row => | 
					
						
							|  |  |  |       jsonToObject(row.json) | 
					
						
							|  |  |  |     ); | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  |     forEach(conversations, conversation => { | 
					
						
							|  |  |  |       const externalFiles = getExternalFilesForConversation(conversation); | 
					
						
							|  |  |  |       forEach(externalFiles, file => { | 
					
						
							|  |  |  |         delete lookup[file]; | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |     const lastMessage: ConversationType | undefined = last(conversations); | 
					
						
							| 
									
										
										
										
											2018-09-20 18:47:19 -07:00
										 |  |  |     if (lastMessage) { | 
					
						
							|  |  |  |       ({ id } = lastMessage); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     complete = conversations.length < chunkSize; | 
					
						
							|  |  |  |     count += conversations.length; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   console.log(`removeKnownAttachments: Done processing ${count} conversations`); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-06 16:18:58 -07:00
										 |  |  |   return Object.keys(lookup); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function removeKnownStickers(allStickers: Array<string>) { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							|  |  |  |   const lookup: Dictionary<boolean> = fromPairs( | 
					
						
							|  |  |  |     map(allStickers, file => [file, true]) | 
					
						
							|  |  |  |   ); | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  |   const chunkSize = 50; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const total = await getStickerCount(); | 
					
						
							|  |  |  |   console.log( | 
					
						
							|  |  |  |     `removeKnownStickers: About to iterate through ${total} stickers` | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   let count = 0; | 
					
						
							|  |  |  |   let complete = false; | 
					
						
							|  |  |  |   let rowid = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   while (!complete) { | 
					
						
							|  |  |  |     const rows = await db.all( | 
					
						
							|  |  |  |       `SELECT rowid, path FROM stickers
 | 
					
						
							|  |  |  |        WHERE rowid > $rowid | 
					
						
							|  |  |  |        ORDER BY rowid ASC | 
					
						
							|  |  |  |        LIMIT $chunkSize;`,
 | 
					
						
							|  |  |  |       { | 
					
						
							|  |  |  |         $rowid: rowid, | 
					
						
							|  |  |  |         $chunkSize: chunkSize, | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |     const files: Array<StickerType> = map(rows, row => row.path); | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  |     forEach(files, file => { | 
					
						
							|  |  |  |       delete lookup[file]; | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |     const lastSticker: StickerType | undefined = last(rows); | 
					
						
							| 
									
										
										
										
											2019-05-16 15:32:11 -07:00
										 |  |  |     if (lastSticker) { | 
					
						
							|  |  |  |       ({ rowid } = lastSticker); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     complete = rows.length < chunkSize; | 
					
						
							|  |  |  |     count += rows.length; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   console.log(`removeKnownStickers: Done processing ${count} stickers`); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return Object.keys(lookup); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2019-08-06 17:40:25 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  | async function removeKnownDraftAttachments(allStickers: Array<string>) { | 
					
						
							|  |  |  |   const db = getInstance(); | 
					
						
							|  |  |  |   const lookup: Dictionary<boolean> = fromPairs( | 
					
						
							|  |  |  |     map(allStickers, file => [file, true]) | 
					
						
							|  |  |  |   ); | 
					
						
							| 
									
										
										
										
											2019-08-06 17:40:25 -07:00
										 |  |  |   const chunkSize = 50; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const total = await getConversationCount(); | 
					
						
							|  |  |  |   console.log( | 
					
						
							|  |  |  |     `removeKnownDraftAttachments: About to iterate through ${total} conversations` | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   let complete = false; | 
					
						
							|  |  |  |   let count = 0; | 
					
						
							|  |  |  |   // Though conversations.id is a string, this ensures that, when coerced, this
 | 
					
						
							|  |  |  |   //   value is still a string but it's smaller than every other string.
 | 
					
						
							|  |  |  |   let id = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   while (!complete) { | 
					
						
							|  |  |  |     const rows = await db.all( | 
					
						
							|  |  |  |       `SELECT json FROM conversations
 | 
					
						
							|  |  |  |        WHERE id > $id | 
					
						
							|  |  |  |        ORDER BY id ASC | 
					
						
							|  |  |  |        LIMIT $chunkSize;`,
 | 
					
						
							|  |  |  |       { | 
					
						
							|  |  |  |         $id: id, | 
					
						
							|  |  |  |         $chunkSize: chunkSize, | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |     const conversations: Array<ConversationType> = map(rows, row => | 
					
						
							|  |  |  |       jsonToObject(row.json) | 
					
						
							|  |  |  |     ); | 
					
						
							| 
									
										
										
										
											2019-08-06 17:40:25 -07:00
										 |  |  |     forEach(conversations, conversation => { | 
					
						
							|  |  |  |       const externalFiles = getExternalDraftFilesForConversation(conversation); | 
					
						
							|  |  |  |       forEach(externalFiles, file => { | 
					
						
							|  |  |  |         delete lookup[file]; | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 11:59:11 -07:00
										 |  |  |     const lastMessage: ConversationType | undefined = last(conversations); | 
					
						
							| 
									
										
										
										
											2019-08-06 17:40:25 -07:00
										 |  |  |     if (lastMessage) { | 
					
						
							|  |  |  |       ({ id } = lastMessage); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     complete = conversations.length < chunkSize; | 
					
						
							|  |  |  |     count += conversations.length; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   console.log( | 
					
						
							|  |  |  |     `removeKnownDraftAttachments: Done processing ${count} conversations` | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return Object.keys(lookup); | 
					
						
							|  |  |  | } |