diff --git a/web/database/database-module-provider.js b/web/database/database-module-provider.js --- a/web/database/database-module-provider.js +++ b/web/database/database-module-provider.js @@ -14,7 +14,6 @@ generateDatabaseCryptoKey, } from './utils/worker-crypto-utils.js'; import WorkerConnectionProxy from './utils/WorkerConnectionProxy.js'; -import type { AppState } from '../redux/redux-setup.js'; import { workerRequestMessageTypes, type WorkerRequestMessage, @@ -22,10 +21,8 @@ } from '../types/worker-types.js'; declare var commQueryExecutorFilename: string; -declare var preloadedState: AppState; - const databaseStatuses = Object.freeze({ - notSupported: 'NOT_SUPPORTED', + notRunning: 'NOT_RUNNING', initSuccess: 'INIT_SUCCESS', initInProgress: 'INIT_IN_PROGRESS', initError: 'INIT_ERROR', @@ -33,21 +30,28 @@ type DatabaseStatus = $Values; +type InitOptions = { +clearDatabase: boolean }; + class DatabaseModule { worker: ?SharedWorker; workerProxy: ?WorkerConnectionProxy; initPromise: ?Promise; - status: DatabaseStatus = databaseStatuses.notSupported; + status: DatabaseStatus = databaseStatuses.notRunning; - async init(currentLoggedInUserID: ?string): Promise { - if (!currentLoggedInUserID) { + async init({ clearDatabase }: InitOptions): Promise { + if (!isSQLiteSupported()) { + console.warn('SQLite is not supported'); + this.status = databaseStatuses.initError; return; } - if (!isSQLiteSupported(currentLoggedInUserID)) { - console.warn('Sqlite is not supported'); - this.status = databaseStatuses.notSupported; - return; + if (clearDatabase && this.status === databaseStatuses.initSuccess) { + console.info('Clearing sensitive data'); + invariant(this.workerProxy, 'Worker proxy should exist'); + await this.workerProxy.scheduleOnWorker({ + type: workerRequestMessageTypes.CLEAR_SENSITIVE_DATA, + }); + this.status = databaseStatuses.notRunning; } if (this.status === databaseStatuses.initInProgress) { @@ -98,14 +102,6 @@ await this.initPromise; } - async clearSensitiveData(): Promise { - this.status = databaseStatuses.notSupported; - invariant(this.workerProxy, 'Worker proxy should exist'); - await this.workerProxy.scheduleOnWorker({ - type: workerRequestMessageTypes.CLEAR_SENSITIVE_DATA, - }); - } - async isDatabaseSupported(): Promise { if (this.status === databaseStatuses.initInProgress) { await this.initPromise; @@ -116,8 +112,8 @@ async schedule( payload: WorkerRequestMessage, ): Promise { - if (this.status === databaseStatuses.notSupported) { - throw new Error('Database not supported'); + if (this.status === databaseStatuses.notRunning) { + throw new Error('Database not running'); } if (this.status === databaseStatuses.initInProgress) { @@ -149,12 +145,11 @@ async function getDatabaseModule(): Promise { if (!databaseModule) { databaseModule = new DatabaseModule(); - const currentLoggedInUserID = preloadedState.currentUserInfo?.anonymous - ? undefined - : preloadedState.currentUserInfo?.id; - await databaseModule.init(currentLoggedInUserID); + await databaseModule.init({ clearDatabase: false }); } return databaseModule; } +// Start initializing the database immediately +getDatabaseModule(); export { getDatabaseModule }; diff --git a/web/database/sqlite-data-handler.js b/web/database/sqlite-data-handler.js --- a/web/database/sqlite-data-handler.js +++ b/web/database/sqlite-data-handler.js @@ -27,13 +27,14 @@ }); const currentDBUserID = currentUserData?.userID; - if (currentDBUserID && currentDBUserID !== currentLoggedInUserID) { - await databaseModule.clearSensitiveData(); + if (currentDBUserID === currentLoggedInUserID) { + return; + } + + if (currentDBUserID) { + await databaseModule.init({ clearDatabase: true }); } - if ( - currentLoggedInUserID && - (currentDBUserID || currentDBUserID !== currentLoggedInUserID) - ) { + if (currentLoggedInUserID) { await databaseModule.schedule({ type: workerRequestMessageTypes.SET_CURRENT_USER_ID, userID: currentLoggedInUserID, @@ -49,10 +50,6 @@ (async () => { const databaseModule = await getDatabaseModule(); - if (currentLoggedInUserID) { - await databaseModule.init(currentLoggedInUserID); - } - if (!rehydrateConcluded) { return; } diff --git a/web/database/utils/db-utils.js b/web/database/utils/db-utils.js --- a/web/database/utils/db-utils.js +++ b/web/database/utils/db-utils.js @@ -2,9 +2,6 @@ import { detect as detectBrowser } from 'detect-browser'; -import { isStaff } from 'lib/shared/staff-utils.js'; -import { isDev } from 'lib/utils/dev-utils.js'; - import { DB_SUPPORTED_BROWSERS, DB_SUPPORTED_OS } from './constants.js'; import type { EmscriptenModule } from '../types/module.js'; import { type SQLiteQueryExecutor } from '../types/sqlite-query-executor.js'; @@ -39,14 +36,7 @@ }); } -function isSQLiteSupported(currentLoggedInUserID: ?string): boolean { - if (!currentLoggedInUserID) { - return false; - } - if (!isDev && (!currentLoggedInUserID || !isStaff(currentLoggedInUserID))) { - return false; - } - +function isSQLiteSupported(): boolean { return ( DB_SUPPORTED_OS.includes(browser.os) && DB_SUPPORTED_BROWSERS.includes(browser.name) diff --git a/web/redux/persist.js b/web/redux/persist.js --- a/web/redux/persist.js +++ b/web/redux/persist.js @@ -37,11 +37,6 @@ declare var preloadedState: AppState; -const initiallyLoggedInUserID = preloadedState.currentUserInfo?.anonymous - ? undefined - : preloadedState.currentUserInfo?.id; -const isDatabaseSupported = isSQLiteSupported(initiallyLoggedInUserID); - const migrations = { [1]: async state => { const { @@ -60,6 +55,7 @@ }, [2]: async state => { const databaseModule = await getDatabaseModule(); + const isDatabaseSupported = await databaseModule.isDatabaseSupported(); if (!isDatabaseSupported) { return state; } @@ -91,6 +87,7 @@ } const databaseModule = await getDatabaseModule(); + const isDatabaseSupported = await databaseModule.isDatabaseSupported(); if (!isDatabaseSupported) { return newState; @@ -158,6 +155,33 @@ console.log('redux-persist: migrating state to SQLite storage'); } + // We need to simulate the keyserverStoreTransform for data stored in the + // old local storage (because redux persist will only run it for the + // sqlite storage which is empty in this case). + // We don't just use keyserverStoreTransform.out(oldStorage) because + // the transform might change in the future, but we need to treat + // this code like migration code (it shouldn't change). + if (oldStorage?._persist?.version === 4) { + const { connection, updatesCurrentAsOf, sessionID } = + preloadedState.keyserverStore.keyserverInfos[ashoatKeyserverID]; + + return { + ...oldStorage, + keyserverStore: { + ...oldStorage.keyserverStore, + keyserverInfos: { + ...oldStorage.keyserverStore.keyserverInfos, + [ashoatKeyserverID]: { + ...oldStorage.keyserverStore.keyserverInfos[ashoatKeyserverID], + connection, + updatesCurrentAsOf, + sessionID, + }, + }, + }, + }; + } + return oldStorage; }; @@ -208,7 +232,7 @@ const persistConfig: PersistConfig = { key: rootKey, storage: commReduxStorageEngine, - whitelist: isDatabaseSupported + whitelist: isSQLiteSupported() ? persistWhitelist : [...persistWhitelist, 'draftStore'], migrate: (createAsyncMigrate(