diff --git a/web/database/worker/db-worker.js b/web/database/worker/db-worker.js --- a/web/database/worker/db-worker.js +++ b/web/database/worker/db-worker.js @@ -35,7 +35,11 @@ SQLITE_CONTENT, SQLITE_ENCRYPTION_KEY, } from '../utils/constants.js'; -import { generateDatabaseCryptoKey } from '../utils/worker-crypto-utils.js'; +import { + decryptDatabaseFile, + encryptDatabaseFile, + generateDatabaseCryptoKey, +} from '../utils/worker-crypto-utils.js'; const localforageConfig: PartialConfig = { driver: localforage.INDEXEDDB, @@ -47,9 +51,26 @@ localforage.config(localforageConfig); let sqliteDb: ?SqliteDatabase = null; +let encryptionKey: ?CryptoKey = null; async function initDatabase(sqljsFilePath: string, sqljsFilename: ?string) { - const content = await localforage.getItem(SQLITE_CONTENT); + encryptionKey = await localforage.getItem(SQLITE_ENCRYPTION_KEY); + if (!encryptionKey) { + const cryptoKey = await generateDatabaseCryptoKey(); + await localforage.setItem(SQLITE_ENCRYPTION_KEY, cryptoKey); + } + + const encryptedContent = await localforage.getItem(SQLITE_CONTENT); + + let dbContent = null; + try { + if (encryptionKey && encryptedContent) { + dbContent = await decryptDatabaseFile(encryptedContent, encryptionKey); + } + } catch (e) { + console.error('Error while decrypting content, clearing database content'); + await localforage.removeItem(SQLITE_CONTENT); + } const locateFile = defaultFilename => { if (sqljsFilename) { @@ -61,11 +82,15 @@ locateFile, }); - if (content) { - sqliteDb = new SQL.Database(new Uint8Array(content)); + if (dbContent) { + sqliteDb = new SQL.Database(dbContent); + console.info( + 'Database exists and is properly encrypted, using persisted data', + ); } else { sqliteDb = new SQL.Database(); setupSQLiteDB(sqliteDb); + console.info('Creating fresh database'); } const dbVersion = getSQLiteDBVersion(sqliteDb); @@ -104,6 +129,23 @@ }; } +async function persist() { + if (!sqliteDb) { + throw new Error('Database not initialized'); + } + + if (!encryptionKey) { + encryptionKey = await localforage.getItem(SQLITE_ENCRYPTION_KEY); + } + + const dbData = sqliteDb.export(); + if (!encryptionKey) { + throw new Error('Encryption key is missing'); + } + const encryptedData = await encryptDatabaseFile(dbData, encryptionKey); + await localforage.setItem(SQLITE_CONTENT, encryptedData); +} + async function processAppRequest( message: WorkerRequestMessage, ): Promise { @@ -157,19 +199,23 @@ if (draftStoreOperations) { processDraftStoreOperations(draftStoreOperations); } + persist(); return; } else if (message.type === workerRequestMessageTypes.SET_CURRENT_USER_ID) { setMetadata(sqliteDb, CURRENT_USER_ID_KEY, message.userID); + persist(); return; } else if ( message.type === workerRequestMessageTypes.SET_PERSIST_STORAGE_ITEM ) { setPersistStorageItem(sqliteDb, message.key, message.item); + persist(); return; } else if ( message.type === workerRequestMessageTypes.REMOVE_PERSIST_STORAGE_ITEM ) { removePersistStorageItem(sqliteDb, message.key); + persist(); return; }