diff --git a/keyserver/src/responders/website-responders.js b/keyserver/src/responders/website-responders.js --- a/keyserver/src/responders/website-responders.js +++ b/keyserver/src/responders/website-responders.js @@ -44,6 +44,7 @@ +olmFilename: string, +commQueryExecutorFilename: string, +opaqueURL: string, + +backupClientFilename: string, }; let assetInfo: ?AssetInfo = null; async function getAssetInfo() { @@ -59,6 +60,7 @@ olmFilename: '', commQueryExecutorFilename: '', opaqueURL: 'http://localhost:8080/opaque-ke.wasm', + backupClientFilename: '', }; return assetInfo; } @@ -83,6 +85,7 @@ olmFilename: manifest['olm.wasm'], commQueryExecutorFilename: webworkersManifest['comm_query_executor.wasm'], opaqueURL: `compiled/${manifest['comm_opaque2_wasm_bg.wasm']}`, + backupClientFilename: webworkersManifest['backup_client_bg.wasm'], }; return assetInfo; } catch { @@ -134,6 +137,7 @@ olmFilename, opaqueURL, commQueryExecutorFilename, + backupClientFilename, } = await assetInfoPromise; // prettier-ignore @@ -185,6 +189,7 @@ var baseURL = "${baseURL}"; var olmFilename = "${olmFilename}"; var commQueryExecutorFilename = "${commQueryExecutorFilename}"; + var backupClientFilename = "${backupClientFilename}"; var opaqueURL = "${opaqueURL}"; diff --git a/package.json b/package.json --- a/package.json +++ b/package.json @@ -11,7 +11,8 @@ "keyserver/addons/rust-node-addon", "native/expo-modules/comm-expo-package", "services/electron-update-server", - "web/opaque-ke-wasm" + "web/opaque-ke-wasm", + "shared/backup_client" ], "scripts": { "clean": "yarn workspace lib clean && yarn workspace web clean && yarn workspace native clean && yarn workspace keyserver clean && yarn workspace landing clean && yarn workspace desktop clean && yarn workspace rust-node-addon clean && yarn workspace electron-update-server clean && rm -rf node_modules/", 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 @@ -24,6 +24,8 @@ declare var baseURL: string; declare var commQueryExecutorFilename: string; +declare var backupClientFilename: string; + const databaseStatuses = Object.freeze({ notRunning: 'NOT_RUNNING', initSuccess: 'INIT_SUCCESS', @@ -93,6 +95,7 @@ databaseModuleFilePath: `${origin}${baseURL}${WORKERS_MODULES_DIR_PATH}`, encryptionKey, commQueryExecutorFilename, + backupClientFilename, }); this.status = { type: databaseStatuses.initSuccess }; console.info('Database initialization success'); diff --git a/web/database/utils/constants.js b/web/database/utils/constants.js --- a/web/database/utils/constants.js +++ b/web/database/utils/constants.js @@ -11,6 +11,7 @@ export const WORKERS_MODULES_DIR_PATH = '/compiled/webworkers'; export const DEFAULT_COMM_QUERY_EXECUTOR_FILENAME = 'comm_query_executor.wasm'; +export const DEFAULT_BACKUP_CLIENT_FILENAME = 'backup-client.wasm'; export const DEFAULT_OLM_FILENAME = 'olm.wasm'; 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 @@ -1,5 +1,6 @@ // @flow +import initBackupClientModule from 'backup-client'; import localforage from 'localforage'; import { @@ -31,6 +32,7 @@ localforageConfig, SQLITE_CONTENT, SQLITE_ENCRYPTION_KEY, + DEFAULT_BACKUP_CLIENT_FILENAME, } from '../utils/constants.js'; import { clearSensitiveData, @@ -102,6 +104,19 @@ ); } +async function initBackupClient( + databaseModuleFilePath: string, + backupClientFilename: ?string, +) { + let modulePath; + if (backupClientFilename) { + modulePath = `${databaseModuleFilePath}/${backupClientFilename}`; + } else { + modulePath = `http://localhost:8080/${DEFAULT_BACKUP_CLIENT_FILENAME}`; + } + await initBackupClientModule(modulePath); +} + async function persist() { persistInProgress = true; const module = dbModule; @@ -148,11 +163,19 @@ // database operations if (message.type === workerRequestMessageTypes.INIT) { - await initDatabase( + const databasePromise = initDatabase( message.databaseModuleFilePath, message.commQueryExecutorFilename, message.encryptionKey, ); + let backupClientPromise = new Promise(resolve => resolve()); + if (!(message.backupClientFilename === undefined)) { + backupClientPromise = initBackupClient( + message.databaseModuleFilePath, + message.backupClientFilename, + ); + } + await Promise.all([databasePromise, backupClientPromise]); return undefined; } else if (message.type === workerRequestMessageTypes.CLEAR_SENSITIVE_DATA) { encryptionKey = null; diff --git a/web/flow-typed/backup-client.js b/web/flow-typed/backup-client.js new file mode 100644 --- /dev/null +++ b/web/flow-typed/backup-client.js @@ -0,0 +1,38 @@ +// @flow + +declare module 'backup-client' { + declare export default (string | URL) => mixed; + + declare export var RequestedData: { + +BackupID: 0, + +UserKeys: 1, + +UserData: 2, + }; + + declare type UserIdentity = { + +userID: string, + +accessToken: string, + +deviceID: string, + }; + + declare type BackupDescriptor = { + +type: "BackupID", +backupID: string, +userIdentity: UserIdentity, + } | {+type: "Latest", +username: string}; + + declare export class BackupClient { + constructor(url: string): void; + + downloadBackupData( + backupDescriptor: BackupDescriptor, + requestedData: $Values, + ): Promise; + + downloadLogs( + userIdentity: UserIdentity, + backupID: string, + f: (Uint8Array) => mixed, + ): Promise; + + free(): void; + } +} diff --git a/web/package.json b/web/package.json --- a/web/package.json +++ b/web/package.json @@ -42,6 +42,7 @@ "@babel/runtime": "^7.23.7", "@commapp/olm": "0.1.0", "@commapp/opaque-ke-wasm": "npm:@commapp/opaque-ke-wasm@^0.0.3", + "backup-client": "0.1.0", "@emoji-mart/data": "^1.1.2", "@emoji-mart/react": "^1.1.1", "@fortawesome/fontawesome-svg-core": "1.2.25", diff --git a/web/types/worker-types.js b/web/types/worker-types.js --- a/web/types/worker-types.js +++ b/web/types/worker-types.js @@ -37,6 +37,7 @@ +databaseModuleFilePath: string, +commQueryExecutorFilename: ?string, +encryptionKey?: ?SubtleCrypto$JsonWebKey, + +backupClientFilename?: ?string, }; export type GenerateDatabaseEncryptionKeyRequestMessage = { diff --git a/web/webpack.config.cjs b/web/webpack.config.cjs --- a/web/webpack.config.cjs +++ b/web/webpack.config.cjs @@ -152,6 +152,14 @@ }, ], }), + new CopyPlugin({ + patterns: [ + { + from: 'node_modules/backup-client/web/_generated/backup_client_bg.wasm', + to: path.join(__dirname, 'dist', 'backup-client.wasm'), + }, + ], + }), ]; const prodWebWorkersPlugins = [ @@ -181,6 +189,19 @@ }, ], }), + new CopyPlugin({ + patterns: [ + { + from: 'node_modules/backup-client/web/_generated/backup_client_bg.wasm', + to: path.join( + __dirname, + 'dist', + 'webworkers', + 'backup-client.[contenthash:12].wasm', + ), + }, + ], + }), new WebpackManifestPlugin({ publicPath: '', }),