diff --git a/native/backup/use-client-backup.js b/native/backup/use-client-backup.js --- a/native/backup/use-client-backup.js +++ b/native/backup/use-client-backup.js @@ -51,7 +51,7 @@ await setMockCommServicesAuthMetadata(); const backupSecret = await getBackupSecret(); - await commCoreModule.createNewBackup(backupSecret); + await commCoreModule.createNewBackup(backupSecret, ''); console.info('Backup uploaded.'); }, [currentUserID, loggedIn, setMockCommServicesAuthMetadata]); diff --git a/native/cpp/CommonCpp/NativeModules/CommCoreModule.h b/native/cpp/CommonCpp/NativeModules/CommCoreModule.h --- a/native/cpp/CommonCpp/NativeModules/CommCoreModule.h +++ b/native/cpp/CommonCpp/NativeModules/CommCoreModule.h @@ -183,8 +183,10 @@ virtual jsi::Value clearCommServicesAccessToken(jsi::Runtime &rt) override; virtual void startBackupHandler(jsi::Runtime &rt) override; virtual void stopBackupHandler(jsi::Runtime &rt) override; - virtual jsi::Value - createNewBackup(jsi::Runtime &rt, jsi::String backupSecret) override; + virtual jsi::Value createNewBackup( + jsi::Runtime &rt, + jsi::String backupSecret, + jsi::String backupMessage) override; virtual jsi::Value restoreBackup(jsi::Runtime &rt, jsi::String backupSecret) override; virtual jsi::Value restoreBackupData( diff --git a/native/cpp/CommonCpp/NativeModules/CommCoreModule.cpp b/native/cpp/CommonCpp/NativeModules/CommCoreModule.cpp --- a/native/cpp/CommonCpp/NativeModules/CommCoreModule.cpp +++ b/native/cpp/CommonCpp/NativeModules/CommCoreModule.cpp @@ -1781,9 +1781,12 @@ } } -jsi::Value -CommCoreModule::createNewBackup(jsi::Runtime &rt, jsi::String backupSecret) { +jsi::Value CommCoreModule::createNewBackup( + jsi::Runtime &rt, + jsi::String backupSecret, + jsi::String backupMessage) { std::string backupSecretStr = backupSecret.utf8(rt); + std::string backupMessageStr = backupMessage.utf8(rt); return createPromiseAsJSIValue( rt, [=](jsi::Runtime &innerRt, std::shared_ptr promise) { this->cryptoThread->scheduleTask([=, &innerRt]() { @@ -1818,6 +1821,7 @@ rust::string(backupSecretStr), rust::string(pickleKey), rust::string(pickledAccount), + rust::string(backupMessageStr), currentID); } else { this->jsInvoker_->invokeAsync( diff --git a/native/cpp/CommonCpp/_generated/commJSI-generated.cpp b/native/cpp/CommonCpp/_generated/commJSI-generated.cpp --- a/native/cpp/CommonCpp/_generated/commJSI-generated.cpp +++ b/native/cpp/CommonCpp/_generated/commJSI-generated.cpp @@ -182,7 +182,7 @@ return jsi::Value::undefined(); } static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_createNewBackup(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { - return static_cast(&turboModule)->createNewBackup(rt, args[0].asString(rt)); + return static_cast(&turboModule)->createNewBackup(rt, args[0].asString(rt), args[1].asString(rt)); } static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_restoreBackup(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { return static_cast(&turboModule)->restoreBackup(rt, args[0].asString(rt)); @@ -256,7 +256,7 @@ methodMap_["clearCommServicesAccessToken"] = MethodMetadata {0, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_clearCommServicesAccessToken}; methodMap_["startBackupHandler"] = MethodMetadata {0, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_startBackupHandler}; methodMap_["stopBackupHandler"] = MethodMetadata {0, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_stopBackupHandler}; - methodMap_["createNewBackup"] = MethodMetadata {1, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_createNewBackup}; + methodMap_["createNewBackup"] = MethodMetadata {2, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_createNewBackup}; methodMap_["restoreBackup"] = MethodMetadata {1, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_restoreBackup}; methodMap_["restoreBackupData"] = MethodMetadata {3, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_restoreBackupData}; methodMap_["retrieveBackupKeys"] = MethodMetadata {1, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_retrieveBackupKeys}; diff --git a/native/cpp/CommonCpp/_generated/commJSI.h b/native/cpp/CommonCpp/_generated/commJSI.h --- a/native/cpp/CommonCpp/_generated/commJSI.h +++ b/native/cpp/CommonCpp/_generated/commJSI.h @@ -74,7 +74,7 @@ virtual jsi::Value clearCommServicesAccessToken(jsi::Runtime &rt) = 0; virtual void startBackupHandler(jsi::Runtime &rt) = 0; virtual void stopBackupHandler(jsi::Runtime &rt) = 0; - virtual jsi::Value createNewBackup(jsi::Runtime &rt, jsi::String backupSecret) = 0; + virtual jsi::Value createNewBackup(jsi::Runtime &rt, jsi::String backupSecret, jsi::String backupMessage) = 0; virtual jsi::Value restoreBackup(jsi::Runtime &rt, jsi::String backupSecret) = 0; virtual jsi::Value restoreBackupData(jsi::Runtime &rt, jsi::String backupID, jsi::String backupDataKey, jsi::String backupLogDataKey) = 0; virtual jsi::Value retrieveBackupKeys(jsi::Runtime &rt, jsi::String backupSecret) = 0; @@ -533,13 +533,13 @@ return bridging::callFromJs( rt, &T::stopBackupHandler, jsInvoker_, instance_); } - jsi::Value createNewBackup(jsi::Runtime &rt, jsi::String backupSecret) override { + jsi::Value createNewBackup(jsi::Runtime &rt, jsi::String backupSecret, jsi::String backupMessage) override { static_assert( - bridging::getParameterCount(&T::createNewBackup) == 2, - "Expected createNewBackup(...) to have 2 parameters"); + bridging::getParameterCount(&T::createNewBackup) == 3, + "Expected createNewBackup(...) to have 3 parameters"); return bridging::callFromJs( - rt, &T::createNewBackup, jsInvoker_, instance_, std::move(backupSecret)); + rt, &T::createNewBackup, jsInvoker_, instance_, std::move(backupSecret), std::move(backupMessage)); } jsi::Value restoreBackup(jsi::Runtime &rt, jsi::String backupSecret) override { static_assert( diff --git a/native/native_rust_library/src/backup.rs b/native/native_rust_library/src/backup.rs --- a/native/native_rust_library/src/backup.rs +++ b/native/native_rust_library/src/backup.rs @@ -6,9 +6,9 @@ use crate::constants::{aes, secure_store}; use crate::ffi::{ create_main_compaction, get_backup_directory_path, - get_backup_user_keys_file_path, restore_from_backup_log, - restore_from_main_compaction, secure_store_get, string_callback, - void_callback, + get_backup_user_keys_file_path, get_siwe_backup_message_path, + restore_from_backup_log, restore_from_main_compaction, secure_store_get, + string_callback, void_callback, }; use crate::utils::future_manager; use crate::utils::jsi_callbacks::handle_string_result_as_callback; @@ -32,6 +32,7 @@ backup_secret: String, pickle_key: String, pickled_account: String, + backup_message: String, promise_id: u32, ) { compaction_upload_promises::insert(backup_id.clone(), promise_id); @@ -51,6 +52,19 @@ return; } + let result = if !backup_message.is_empty() { + create_backup_message_compaction(backup_id.clone(), backup_message) + .await + .map_err(|err| err.to_string()) + } else { + Ok(()) + }; + + if let Err(err) = result { + compaction_upload_promises::resolve(&backup_id, Err(err)); + return; + } + let (future_id, future) = future_manager::new_future::<()>().await; create_main_compaction(&backup_id, future_id); if let Err(err) = future.await { @@ -200,6 +214,16 @@ Ok(()) } +pub async fn create_backup_message_compaction( + backup_id: String, + backup_message: String, +) -> Result<(), Box> { + let backup_message_file = get_siwe_backup_message_path(&backup_id)?; + tokio::fs::write(backup_message_file, backup_message).await?; + + Ok(()) +} + async fn download_backup( backup_secret: String, ) -> Result> { diff --git a/native/native_rust_library/src/backup/upload_handler.rs b/native/native_rust_library/src/backup/upload_handler.rs --- a/native/native_rust_library/src/backup/upload_handler.rs +++ b/native/native_rust_library/src/backup/upload_handler.rs @@ -4,7 +4,7 @@ use crate::constants::BACKUP_SERVICE_CONNECTION_RETRY_DELAY; use crate::ffi::{ get_backup_directory_path, get_backup_file_path, get_backup_log_file_path, - get_backup_user_keys_file_path, + get_backup_user_keys_file_path, get_siwe_backup_message_path, }; use crate::BACKUP_SOCKET_ADDR; use crate::RUNTIME; @@ -103,6 +103,7 @@ | BackupHandlerError::LockError => break, BackupHandlerError::IoError(_) | BackupHandlerError::CxxException(_) => continue, + BackupHandlerError::FromUtf8Error(_) => break, } } @@ -207,11 +208,22 @@ Err(err) => return Err(err.into()), }; + let backup_message_path = get_siwe_backup_message_path(&backup_id)?; + let message_backup = match tokio::fs::read(&backup_message_path).await { + Ok(data) => match String::from_utf8(data) { + Ok(valid_string) => Some(valid_string), + Err(err) => return Err(err.into()), + }, + Err(err) if err.kind() == ErrorKind::NotFound => None, + Err(err) => return Err(err.into()), + }; + let backup_data = BackupData { backup_id: backup_id.clone(), user_data, user_keys, attachments, + message_backup, }; backup_client @@ -235,6 +247,12 @@ Ok(()) => Result::<_, Box>::Ok(()), Err(err) if err.kind() == ErrorKind::NotFound => Ok(()), Err(err) => Err(err.into()), + }?; + let backup_message_path = get_siwe_backup_message_path(&backup_id)?; + match tokio::fs::remove_file(&backup_message_path).await { + Ok(()) => Result::<_, Box>::Ok(()), + Err(err) if err.kind() == ErrorKind::NotFound => Ok(()), + Err(err) => Err(err.into()), } }; @@ -309,6 +327,7 @@ IoError(std::io::Error), CxxException(cxx::Exception), LockError, + FromUtf8Error(std::string::FromUtf8Error), } impl From> for BackupHandlerError { diff --git a/native/native_rust_library/src/lib.rs b/native/native_rust_library/src/lib.rs --- a/native/native_rust_library/src/lib.rs +++ b/native/native_rust_library/src/lib.rs @@ -346,6 +346,7 @@ backup_secret: String, pickle_key: String, pickled_account: String, + backup_message: String, promise_id: u32, ); diff --git a/native/schema/CommCoreModuleSchema.js b/native/schema/CommCoreModuleSchema.js --- a/native/schema/CommCoreModuleSchema.js +++ b/native/schema/CommCoreModuleSchema.js @@ -149,7 +149,10 @@ +clearCommServicesAccessToken: () => Promise; +startBackupHandler: () => void; +stopBackupHandler: () => void; - +createNewBackup: (backupSecret: string) => Promise; + +createNewBackup: ( + backupSecret: string, + backupMessage: string, + ) => Promise; +restoreBackup: (backupSecret: string) => Promise; +restoreBackupData: ( backupID: string, diff --git a/services/commtest/tests/backup_integration_test.rs b/services/commtest/tests/backup_integration_test.rs --- a/services/commtest/tests/backup_integration_test.rs +++ b/services/commtest/tests/backup_integration_test.rs @@ -160,6 +160,7 @@ Some(b'A'), ), attachments: vec![], + message_backup: None, }, generate_log_data("b1", b'a'), ), @@ -175,6 +176,7 @@ Some(b'B'), ), attachments: vec![], + message_backup: None, }, generate_log_data("b2", b'b'), ), diff --git a/services/commtest/tests/backup_performance_test.rs b/services/commtest/tests/backup_performance_test.rs --- a/services/commtest/tests/backup_performance_test.rs +++ b/services/commtest/tests/backup_performance_test.rs @@ -35,6 +35,7 @@ Some(i as u8), ), attachments: vec![], + message_backup: None, }); } diff --git a/shared/backup_client/src/lib.rs b/shared/backup_client/src/lib.rs --- a/shared/backup_client/src/lib.rs +++ b/shared/backup_client/src/lib.rs @@ -53,6 +53,7 @@ user_keys, user_data, attachments, + message_backup, } = backup_data; let client = reqwest::Client::new(); @@ -70,6 +71,11 @@ .part("user_data", Part::stream(Body::from(user_data))) .text("attachments", attachments.join("\n")); + let form = match message_backup { + Some(msg_backup) => form.text("msg_backup", msg_backup), + None => form, + }; + let response = client .post(self.url.join("backups")?) .bearer_auth(user_identity.as_authorization_token()?) @@ -309,6 +315,7 @@ pub user_keys: Vec, pub user_data: Vec, pub attachments: Vec, + pub message_backup: Option, } #[derive(Debug, Clone, Serialize, Deserialize)]