diff --git a/web/database/database-module-provider.js b/web/database/database-module-provider.js index f479c23ee..daf7819e6 100644 --- a/web/database/database-module-provider.js +++ b/web/database/database-module-provider.js @@ -1,119 +1,123 @@ // @flow import { DATABASE_WORKER_PATH, SQLJS_FILE_PATH } from './utils/constants.js'; import { isSQLiteSupported } from './utils/db-utils.js'; import WorkerConnectionProxy from './utils/WorkerConnectionProxy.js'; import type { AppState } from '../redux/redux-setup.js'; import { workerRequestMessageTypes, type WorkerRequestMessage, type WorkerResponseMessage, } from '../types/worker-types.js'; declare var sqljsFilename: string; declare var preloadedState: AppState; const databaseStatuses = Object.freeze({ notSupported: 'NOT_SUPPORTED', initSuccess: 'INIT_SUCCESS', initInProgress: 'INIT_IN_PROGRESS', initError: 'INIT_ERROR', }); type DatabaseStatus = $Values; class DatabaseModule { worker: SharedWorker; workerProxy: WorkerConnectionProxy; initPromise: Promise; status: DatabaseStatus; constructor() { const currentLoggedInUserID = preloadedState.currentUserInfo?.anonymous ? undefined : preloadedState.currentUserInfo?.id; const isSupported = isSQLiteSupported(currentLoggedInUserID); if (!isSupported) { this.status = databaseStatuses.notSupported; } else { this.init(); } } - init() { + init(encryptionKey?: ?SubtleCrypto$JsonWebKey) { this.status = databaseStatuses.initInProgress; this.worker = new SharedWorker(DATABASE_WORKER_PATH); this.worker.onerror = console.error; this.workerProxy = new WorkerConnectionProxy( this.worker.port, console.error, ); const origin = window.location.origin; this.initPromise = (async () => { try { await this.workerProxy.scheduleOnWorker({ type: workerRequestMessageTypes.INIT, sqljsFilePath: `${origin}${SQLJS_FILE_PATH}`, sqljsFilename, + encryptionKey, }); this.status = databaseStatuses.initSuccess; console.info('Database initialization success'); } catch (error) { this.status = databaseStatuses.initError; console.error(`Database initialization failure`, error); } })(); } - initDBForLoggedInUser(currentLoggedInUserID: ?string) { + initDBForLoggedInUser( + currentLoggedInUserID: ?string, + encryptionKey?: ?SubtleCrypto$JsonWebKey, + ) { if (this.status === databaseStatuses.initSuccess) { return; } if ( this.status === databaseStatuses.notSupported && isSQLiteSupported(currentLoggedInUserID) ) { - this.init(); + this.init(encryptionKey); } } async clearSensitiveData(): Promise { this.status = databaseStatuses.notSupported; await this.workerProxy.scheduleOnWorker({ type: workerRequestMessageTypes.CLEAR_SENSITIVE_DATA, }); } async isDatabaseSupported(): Promise { if (this.status === databaseStatuses.initInProgress) { await this.initPromise; } return this.status === databaseStatuses.initSuccess; } async schedule( payload: WorkerRequestMessage, ): Promise { if (this.status === databaseStatuses.notSupported) { throw new Error('Database not supported'); } if (this.status === databaseStatuses.initInProgress) { await this.initPromise; } if (this.status === databaseStatuses.initError) { throw new Error('Database could not be initialized'); } return this.workerProxy.scheduleOnWorker(payload); } } const databaseModule: DatabaseModule = new DatabaseModule(); export { databaseModule }; diff --git a/web/types/worker-types.js b/web/types/worker-types.js index ec08d9402..343d8d2d4 100644 --- a/web/types/worker-types.js +++ b/web/types/worker-types.js @@ -1,145 +1,146 @@ // @flow import type { ClientDBStore, ClientDBStoreOperations, } from 'lib/types/store-ops-types.js'; // The types of messages sent from app to worker export const workerRequestMessageTypes = Object.freeze({ PING: 0, INIT: 1, GENERATE_DATABASE_ENCRYPTION_KEY: 2, PROCESS_STORE_OPERATIONS: 3, GET_CLIENT_STORE: 4, SET_CURRENT_USER_ID: 5, GET_CURRENT_USER_ID: 6, GET_PERSIST_STORAGE_ITEM: 7, SET_PERSIST_STORAGE_ITEM: 8, REMOVE_PERSIST_STORAGE_ITEM: 9, CLEAR_SENSITIVE_DATA: 10, }); export const workerWriteRequests: $ReadOnlyArray = [ workerRequestMessageTypes.PROCESS_STORE_OPERATIONS, workerRequestMessageTypes.SET_CURRENT_USER_ID, workerRequestMessageTypes.SET_PERSIST_STORAGE_ITEM, workerRequestMessageTypes.REMOVE_PERSIST_STORAGE_ITEM, ]; export type PingWorkerRequestMessage = { +type: 0, +text: string, }; export type InitWorkerRequestMessage = { +type: 1, +sqljsFilePath: string, +sqljsFilename: ?string, + +encryptionKey?: ?SubtleCrypto$JsonWebKey, }; export type GenerateDatabaseEncryptionKeyRequestMessage = { +type: 2, }; export type ProcessStoreOperationsRequestMessage = { +type: 3, +storeOperations: ClientDBStoreOperations, }; export type GetClientStoreRequestMessage = { +type: 4, }; export type SetCurrentUserIDRequestMessage = { +type: 5, +userID: string, }; export type GetCurrentUserIDRequestMessage = { +type: 6, }; export type GetPersistStorageItemRequestMessage = { +type: 7, +key: string, }; export type SetPersistStorageItemRequestMessage = { +type: 8, +key: string, +item: string, }; export type RemovePersistStorageItemRequestMessage = { +type: 9, +key: string, }; export type ClearSensitiveDataRequestMessage = { +type: 10, }; export type WorkerRequestMessage = | PingWorkerRequestMessage | InitWorkerRequestMessage | GenerateDatabaseEncryptionKeyRequestMessage | ProcessStoreOperationsRequestMessage | GetClientStoreRequestMessage | SetCurrentUserIDRequestMessage | GetCurrentUserIDRequestMessage | GetPersistStorageItemRequestMessage | SetPersistStorageItemRequestMessage | RemovePersistStorageItemRequestMessage | ClearSensitiveDataRequestMessage; export type WorkerRequestProxyMessage = { +id: number, +message: WorkerRequestMessage, }; // The types of messages sent from worker to app export const workerResponseMessageTypes = Object.freeze({ PONG: 0, CLIENT_STORE: 1, GET_CURRENT_USER_ID: 2, GET_PERSIST_STORAGE_ITEM: 3, }); export type PongWorkerResponseMessage = { +type: 0, +text: string, }; export type ClientStoreResponseMessage = { +type: 1, +store: ClientDBStore, }; export type GetCurrentUserIDResponseMessage = { +type: 2, +userID: ?string, }; export type GetPersistStorageItemResponseMessage = { +type: 3, +item: string, }; export type WorkerResponseMessage = | PongWorkerResponseMessage | ClientStoreResponseMessage | GetCurrentUserIDResponseMessage | GetPersistStorageItemResponseMessage; export type WorkerResponseProxyMessage = { +id?: number, +message?: WorkerResponseMessage, +error?: string, }; // SharedWorker types export type SharedWorkerMessageEvent = MessageEvent & { +ports: $ReadOnlyArray, ... };