diff --git a/Cargo.lock b/Cargo.lock index d17d032aabe..acdbec32e0c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "accessory" -version = "1.3.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "850bb534b9dc04744fbbb71d30ad6d25a7e4cf6dc33e223c81ef3a92ebab4e0b" +checksum = "bb3791c4beae5b827e93558ac83a88e63a841aad61759a05d9b577ef16030470" dependencies = [ "macroific", "proc-macro2", @@ -878,10 +878,12 @@ dependencies = [ [[package]] name = "delegate-display" -version = "2.1.1" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98a85201f233142ac819bbf6226e36d0b5e129a47bd325084674261c82d4cd66" +checksum = "9926686c832494164c33a36bf65118f4bd6e704000b58c94681bf62e9ad67a74" dependencies = [ + "impartial-ord", + "itoa", "macroific", "proc-macro2", "quote", @@ -919,6 +921,38 @@ dependencies = [ "serde", ] +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive_more" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", + "unicode-xid", +] + [[package]] name = "dialoguer" version = "0.11.0" @@ -928,7 +962,7 @@ dependencies = [ "console", "fuzzy-matcher", "shell-words", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -1129,9 +1163,9 @@ dependencies = [ [[package]] name = "fancy_constructor" -version = "1.2.2" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f71f317e4af73b2f8f608fac190c52eac4b1879d2145df1db2fe48881ca69435" +checksum = "fac0fd7f4636276b4bd7b3148d0ba2c1c3fbede2b5214e47e7fedb70b02cde44" dependencies = [ "macroific", "proc-macro2", @@ -1194,7 +1228,7 @@ dependencies = [ "anyhow", "itertools 0.12.1", "regex", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -1778,20 +1812,63 @@ dependencies = [ ] [[package]] -name = "indexed_db_futures" -version = "0.5.0" +name = "impartial-ord" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43315957678a70eb21fb0d2384fe86dde0d6c859a01e24ce127eb65a0143d28c" +checksum = "0ab604ee7085efba6efc65e4ebca0e9533e3aff6cb501d7d77b211e3a781c6d5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", +] + +[[package]] +name = "indexed-db" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78f4ecbb6cd50773303683617a93fc2782267d2c94546e9545ec4190eb69aa1a" +dependencies = [ + "futures-channel", + "futures-util", + "pin-project-lite", + "scoped-tls", + "thiserror 2.0.11", + "web-sys", +] + +[[package]] +name = "indexed_db_futures" +version = "0.6.0" dependencies = [ "accessory", "cfg-if 1.0.0", "delegate-display", + "derive_more", "fancy_constructor", + "futures-core", + "indexed_db_futures_macros_internal", "js-sys", - "uuid", + "sealed", + "serde", + "serde-wasm-bindgen", + "smallvec", + "thiserror 1.0.69", + "tokio", "wasm-bindgen", "wasm-bindgen-futures", + "wasm_evt_listener", "web-sys", + "web-time", +] + +[[package]] +name = "indexed_db_futures_macros_internal" +version = "1.0.0" +dependencies = [ + "macroific", + "proc-macro2", + "quote", + "syn 2.0.96", ] [[package]] @@ -1889,7 +1966,7 @@ dependencies = [ "combine", "jni-sys", "log", - "thiserror", + "thiserror 1.0.69", "walkdir", "windows-sys 0.45.0", ] @@ -2079,7 +2156,7 @@ dependencies = [ "log", "reqwest", "reqwest-eventsource", - "thiserror", + "thiserror 1.0.69", "tokio", "wasm-bindgen-test", ] @@ -2112,7 +2189,7 @@ dependencies = [ "serde_test", "sha2", "sodiumoxide", - "thiserror", + "thiserror 1.0.69", "uuid", "x25519-dalek", "zeroize", @@ -2209,6 +2286,7 @@ dependencies = [ name = "libparsec_platform_storage" version = "3.2.5-a.0+dev" dependencies = [ + "indexed-db", "indexed_db_futures", "js-sys", "lazy_static", @@ -2246,7 +2324,7 @@ dependencies = [ "serde", "serde_json", "serde_with", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -2356,7 +2434,7 @@ dependencies = [ "serde_bytes", "serde_test", "serde_with", - "thiserror", + "thiserror 1.0.69", "unicode-normalization", "url", "uuid", @@ -2444,9 +2522,9 @@ checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" [[package]] name = "macroific" -version = "1.3.1" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f05c00ac596022625d01047c421a0d97d7f09a18e429187b341c201cb631b9dd" +checksum = "89f276537b4b8f981bf1c13d79470980f71134b7bdcc5e6e911e910e556b0285" dependencies = [ "macroific_attr_parse", "macroific_core", @@ -2455,32 +2533,33 @@ dependencies = [ [[package]] name = "macroific_attr_parse" -version = "1.3.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd94d5da95b30ae6e10621ad02340909346ad91661f3f8c0f2b62345e46a2f67" +checksum = "ad4023761b45fcd36abed8fb7ae6a80456b0a38102d55e89a57d9a594a236be9" dependencies = [ - "cfg-if 1.0.0", "proc-macro2", "quote", + "sealed", "syn 2.0.96", ] [[package]] name = "macroific_core" -version = "1.0.2" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13198c120864097a565ccb3ff947672d969932b7975ebd4085732c9f09435e55" +checksum = "d0a7594d3c14916fa55bef7e9d18c5daa9ed410dd37504251e4b75bbdeec33e3" dependencies = [ "proc-macro2", "quote", + "sealed", "syn 2.0.96", ] [[package]] name = "macroific_macro" -version = "1.1.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0c9853143cbed7f1e41dc39fee95f9b361bec65c8dc2a01bf609be01b61f5ae" +checksum = "4da6f2ed796261b0a74e2b52b42c693bb6dee1effba3a482c49592659f824b3b" dependencies = [ "macroific_attr_parse", "macroific_core", @@ -2990,7 +3069,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56f8023d0fb78c8e03784ea1c7f3fa36e68a723138990b8d5a47d916b651e7a8" dependencies = [ "memchr", - "thiserror", + "thiserror 1.0.69", "ucd-trie", ] @@ -3395,7 +3474,7 @@ checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4" dependencies = [ "getrandom", "libredox", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -3435,7 +3514,7 @@ checksum = "cf3b6d580d46b9d6d6c291f90c5d762e8b93a268a96e429556419fcd7e349f94" dependencies = [ "bitflags 1.3.2", "log", - "thiserror", + "thiserror 1.0.69", "utfx", "winapi", ] @@ -3502,7 +3581,7 @@ dependencies = [ "nom", "pin-project-lite", "reqwest", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -3515,7 +3594,7 @@ dependencies = [ "nix 0.27.1", "regex", "tempfile", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -3738,6 +3817,17 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "sealed" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22f968c5ea23d555e670b449c1c5e7b2fc399fdaec1d304a17cd48e288abc107" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", +] + [[package]] name = "security-framework" version = "2.9.2" @@ -3899,7 +3989,7 @@ dependencies = [ "rand", "serde", "serde_json", - "thiserror", + "thiserror 1.0.69", "time", "url", "uuid", @@ -4055,6 +4145,9 @@ name = "smallvec" version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" +dependencies = [ + "serde", +] [[package]] name = "socket2" @@ -4172,7 +4265,7 @@ dependencies = [ "sha2", "smallvec", "sqlformat", - "thiserror", + "thiserror 1.0.69", "tokio", "tokio-stream", "tracing", @@ -4382,7 +4475,16 @@ version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ - "thiserror-impl", + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc" +dependencies = [ + "thiserror-impl 2.0.11", ] [[package]] @@ -4396,6 +4498,17 @@ dependencies = [ "syn 2.0.96", ] +[[package]] +name = "thiserror-impl" +version = "2.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.95", +] + [[package]] name = "thread_local" version = "1.1.8" @@ -4673,6 +4786,12 @@ version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + [[package]] name = "unicode_categories" version = "0.1.1" @@ -4754,7 +4873,6 @@ dependencies = [ "getrandom", "rand", "serde", - "wasm-bindgen", ] [[package]] @@ -4916,6 +5034,24 @@ dependencies = [ "web-sys", ] +[[package]] +name = "wasm_evt_listener" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc92d6378b411ed94839112a36d9dbc77143451d85b05dfb0cce93a78dab1963" +dependencies = [ + "accessory", + "derivative", + "derive_more", + "fancy_constructor", + "futures-core", + "js-sys", + "smallvec", + "tokio", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "web-sys" version = "0.3.74" @@ -4926,6 +5062,17 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "serde", + "wasm-bindgen", +] + [[package]] name = "widestring" version = "1.1.0" diff --git a/Cargo.toml b/Cargo.toml index 630bea3b99e..f5d00660d41 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -142,7 +142,8 @@ glob = { version = "0.3.2", default-features = false } gloo-timers = { version = "0.3.0", default-features = false } hex = { version = "0.4.3", default-features = false } hex-literal = { version = "0.4.1", default-features = false } -indexed_db_futures = { version = "0.5.0", default-features = false } +indexed-db = { version = "0.4.2", default-features = false } +indexed_db_futures = { version = "0.6.0", default-features = false, path = "../rust-indexed-db" } itertools = { version = "0.12.1", default-features = false } jni = { version = "0.21.1", default-features = false } js-sys = { version = "0.3.70", default-features = false } diff --git a/bindings/web/src/lib.rs b/bindings/web/src/lib.rs index a8f42563d69..7156557d171 100644 --- a/bindings/web/src/lib.rs +++ b/bindings/web/src/lib.rs @@ -15,5 +15,11 @@ pub fn initLogger() { { console_error_panic_hook::set_once(); } - console_log::init_with_level(log::Level::Info).expect("cannot initialize console logger"); + let log_level = if cfg!(debug_assertions) { + log::Level::Trace + } else { + log::Level::Info + }; + console_log::init_with_level(log_level).expect("cannot initialize console logger"); + log::info!("Libparsec logger initialized with level {log_level}"); } diff --git a/libparsec/crates/client/src/certif/add.rs b/libparsec/crates/client/src/certif/add.rs index ecea70bc80a..80414db30a5 100644 --- a/libparsec/crates/client/src/certif/add.rs +++ b/libparsec/crates/client/src/certif/add.rs @@ -180,6 +180,7 @@ pub(super) async fn add_certificates_batch( return Ok(MaybeRedactedSwitch::NoSwitch); } + log::debug!("Add certificates batch: as work to do"); let send_event_on_invalid_certificate = |err: CertifAddCertificatesBatchError| { if let CertifAddCertificatesBatchError::InvalidCertificate(what) = err { let event = EventInvalidCertificate(what); @@ -191,8 +192,10 @@ pub(super) async fn add_certificates_batch( }; let initial_stored_last_timestamps = store.get_last_timestamps().await?; + log::debug!("Add certificates batch: last_timestamp={initial_stored_last_timestamps:?}"); let storage_initially_empty = initial_stored_last_timestamps.is_empty(); let initial_self_profile = store.get_current_self_profile().await?; + log::debug!("Add certificates batch: self_profile={initial_self_profile}"); // If a certificate is invalid we exit without any further validation: the // write operation is going to be rolled back. diff --git a/libparsec/crates/client/src/certif/store.rs b/libparsec/crates/client/src/certif/store.rs index 1edc3fdd3b0..37218867bc6 100644 --- a/libparsec/crates/client/src/certif/store.rs +++ b/libparsec/crates/client/src/certif/store.rs @@ -256,6 +256,9 @@ impl CertificatesStore { // be rollback on drop. match write_guard.storage.commit().await { Err(commit_err) => { + log::error!( + "Failed to commit transaction for certificate storage: {commit_err}" + ); reset_cache(); Err(commit_err.into()) } @@ -265,6 +268,7 @@ impl CertificatesStore { } } } else { + log::debug!("Operation failed for certificate storage, not commiting transaction"); reset_cache(); // Ok(Err(...)) Ok(outcome) @@ -291,7 +295,7 @@ impl CertificatesStore { Some(storage) => storage, }; - let mut write_guard = CertificatesStoreReadGuard { + let mut read_guard = CertificatesStoreReadGuard { store: self, storage, }; @@ -305,9 +309,9 @@ impl CertificatesStore { // of a Future returned by a closure depends on the closure parameter if // they are references (see `for_write` code). // TODO: Remove this once async closure are available - let static_write_guard_mut_ref = unsafe { pretend_static(&mut write_guard) }; + let static_read_guard_mut_ref = unsafe { pretend_static(&mut read_guard) }; - let fut = cb(static_write_guard_mut_ref); + let fut = cb(static_read_guard_mut_ref); let outcome = fut.await; Ok(outcome) @@ -469,6 +473,7 @@ macro_rules! impl_read_methods { .lock() .expect("Mutex is poisoned !"); if let ScalarCache::Present(last_timestamps) = &guard.per_topic_last_timestamps { + log::trace!("Cached last_timestamps"); return Ok(last_timestamps.to_owned()); } } diff --git a/libparsec/crates/platform_device_loader/src/lib.rs b/libparsec/crates/platform_device_loader/src/lib.rs index 759bee9d8d6..5a66c6e7be6 100644 --- a/libparsec/crates/platform_device_loader/src/lib.rs +++ b/libparsec/crates/platform_device_loader/src/lib.rs @@ -128,7 +128,9 @@ pub async fn load_device( ) -> Result, LoadDeviceError> { #[cfg(feature = "test-with-testbed")] if let Some(result) = testbed::maybe_load_device(config_dir, access) { - return result; + return result.inspect_err(|e| log::error!("Failed to load device from testbed: {e}")); + } else { + log::trace!("Device not found in testbed"); } platform::load_device(access) diff --git a/libparsec/crates/platform_device_loader/src/testbed.rs b/libparsec/crates/platform_device_loader/src/testbed.rs index 16a24c49059..754d1620978 100644 --- a/libparsec/crates/platform_device_loader/src/testbed.rs +++ b/libparsec/crates/platform_device_loader/src/testbed.rs @@ -219,6 +219,10 @@ pub(crate) fn maybe_load_device( config_dir: &Path, access: &DeviceAccessStrategy, ) -> Option, LoadDeviceError>> { + log::trace!( + "Trying to load device from testbed (cfg={cfg}, access={access:?})", + cfg = config_dir.display() + ); test_get_testbed_component_store::(config_dir, STORE_ENTRY_KEY, store_factory) .and_then(|store| { // 1) Try to load from the cache diff --git a/libparsec/crates/platform_storage/Cargo.toml b/libparsec/crates/platform_storage/Cargo.toml index f30e5d02b01..e67dbd7f453 100644 --- a/libparsec/crates/platform_storage/Cargo.toml +++ b/libparsec/crates/platform_storage/Cargo.toml @@ -32,7 +32,8 @@ libsqlite3-sys = { workspace = true, features = ["bundled"] } sqlx = { workspace = true, features = ["sqlite", "runtime-tokio", "macros"] } [target.'cfg(target_arch = "wasm32")'.dependencies] -indexed_db_futures = { workspace = true, features = ["indices", "cursors"] } +indexed-db = { workspace = true } +indexed_db_futures = { workspace = true, features = ["indices", "cursors", "serde", "streams"] } js-sys = { workspace = true } web-sys = { workspace = true, features = ["IdbKeyRange"] } serde = { workspace = true } diff --git a/libparsec/crates/platform_storage/src/web/certificates.rs b/libparsec/crates/platform_storage/src/web/certificates.rs index 4bee6f93f59..3ff7c0900dc 100644 --- a/libparsec/crates/platform_storage/src/web/certificates.rs +++ b/libparsec/crates/platform_storage/src/web/certificates.rs @@ -1,13 +1,10 @@ // Parsec Cloud (https://parsec.cloud) Copyright (c) BUSL-1.1 2016-present Scille SAS -#![allow(unused_variables)] - use std::collections::HashMap; use std::path::Path; use std::sync::Arc; -use indexed_db_futures::prelude::IdbTransaction; -use indexed_db_futures::IdbDatabase; +use indexed_db_futures::{database::Database, transaction::Transaction}; use libparsec_types::prelude::*; use crate::certificates::{ @@ -19,12 +16,25 @@ use crate::web::DB_VERSION; #[derive(Debug)] pub(crate) struct PlatformCertificatesStorageForUpdateGuard<'a> { - transaction: IdbTransaction<'a>, + transaction: Option>, } impl<'a> PlatformCertificatesStorageForUpdateGuard<'a> { - pub async fn commit(self) -> anyhow::Result<()> { - super::db::commit(self.transaction).await + fn get_transaction(&self) -> &Transaction<'a> { + self.transaction + .as_ref() + .expect("Transaction already committed or dropped") + } + + pub async fn commit(mut self) -> anyhow::Result<()> { + log::debug!(concat!( + stringify!(PlatformCertificatesStorageForUpdateGuard), + " Committing transaction" + )); + let Some(transaction) = self.transaction.take() else { + panic!("Transaction already committed or dropped") + }; + super::db::commit(transaction).await } pub async fn get_certificate_encrypted<'b>( @@ -32,7 +42,8 @@ impl<'a> PlatformCertificatesStorageForUpdateGuard<'a> { query: GetCertificateQuery<'b>, up_to: UpTo, ) -> Result<(DateTime, Vec), GetCertificateError> { - let certifs = Certificate::get_values(&self.transaction, CertificateFilter(query)).await?; + let certifs = + Certificate::get_values(self.get_transaction(), CertificateFilter(query)).await?; let maybe_certif_timestamp = certifs .get(0) @@ -64,7 +75,7 @@ impl<'a> PlatformCertificatesStorageForUpdateGuard<'a> { }; // Determine if the result is an actual success or a ExistButTooRecent error - if let Some((certif_timestamp, certif)) = maybe_certif_timestamp { + if let Some((certif_timestamp, _certif)) = maybe_certif_timestamp { let certificate_timestamp = DateTime::from_timestamp_micros(certif_timestamp) .map_err(|err| GetCertificateError::Internal(err.into()))?; @@ -86,7 +97,8 @@ impl<'a> PlatformCertificatesStorageForUpdateGuard<'a> { offset: Option, limit: Option, ) -> anyhow::Result)>> { - let certifs = Certificate::get_values(&self.transaction, CertificateFilter(query)).await?; + let certifs = + Certificate::get_values(&self.get_transaction(), CertificateFilter(query)).await?; let mut certifs = if let UpTo::Timestamp(up_to) = up_to { certifs @@ -116,7 +128,7 @@ impl<'a> PlatformCertificatesStorageForUpdateGuard<'a> { } pub async fn forget_all_certificates(&mut self) -> anyhow::Result<()> { - Certificate::clear(&self.transaction).await + Certificate::clear(self.get_transaction()).await } pub async fn add_certificate( @@ -147,7 +159,7 @@ impl<'a> PlatformCertificatesStorageForUpdateGuard<'a> { filter1, filter2, }, - &self.transaction, + self.get_transaction(), ) .await } @@ -157,10 +169,12 @@ impl<'a> PlatformCertificatesStorageForUpdateGuard<'a> { let mut sequester_last_timestamp = None; let mut per_realm_last_timestamps = HashMap::new(); let mut shamir_recovery_last_timestamp = None; + let transaction = self.get_transaction(); for ty in ::TYPES { + log::trace!("get_last_timestamps: fetch common certifs of type {ty}"); let certifs = Certificate::get_values( - &self.transaction, + transaction, CertificateFilter(GetCertificateQuery::NoFilter { certificate_type: ty, }), @@ -178,8 +192,9 @@ impl<'a> PlatformCertificatesStorageForUpdateGuard<'a> { } for ty in ::TYPES { + log::trace!("get_last_timestamps: fetch sequester certifs of type {ty}"); let certifs = Certificate::get_values( - &self.transaction, + transaction, CertificateFilter(GetCertificateQuery::NoFilter { certificate_type: ty, }), @@ -197,8 +212,9 @@ impl<'a> PlatformCertificatesStorageForUpdateGuard<'a> { } for ty in ::TYPES { + log::trace!("get_last_timestamps: fetch realm certifs of type {ty}"); let certifs = Certificate::get_values( - &self.transaction, + transaction, CertificateFilter(GetCertificateQuery::NoFilter { certificate_type: ty, }), @@ -224,8 +240,9 @@ impl<'a> PlatformCertificatesStorageForUpdateGuard<'a> { } for ty in ::TYPES { + log::trace!("get_last_timestamps: fetch shamir certifs of type {ty}"); let certifs = Certificate::get_values( - &self.transaction, + transaction, CertificateFilter(GetCertificateQuery::NoFilter { certificate_type: ty, }), @@ -258,9 +275,20 @@ impl<'a> PlatformCertificatesStorageForUpdateGuard<'a> { } } +impl Drop for PlatformCertificatesStorageForUpdateGuard<'_> { + fn drop(&mut self) { + if self.transaction.is_some() { + log::debug!(concat!( + stringify!(PlatformCertificatesStorageForUpdateGuard), + " Dropping transaction without commit" + )); + } + } +} + #[derive(Debug)] pub(crate) struct PlatformCertificatesStorage { - conn: Arc, + conn: Arc, } // Safety: PlatformCertificatesStorage is read only @@ -268,6 +296,7 @@ unsafe impl Send for PlatformCertificatesStorage {} impl PlatformCertificatesStorage { pub async fn no_populate_start( + #[cfg_attr(not(feature = "test-with-testbed"), allow(unused_variables))] data_base_dir: &Path, device: &LocalDevice, ) -> anyhow::Result { @@ -276,15 +305,14 @@ impl PlatformCertificatesStorage { #[cfg(feature = "test-with-testbed")] let name = format!( "{}-{}-certificates", - data_base_dir.to_str().unwrap(), + data_base_dir.display(), device.device_id.hex() ); #[cfg(not(feature = "test-with-testbed"))] let name = format!("{}-certificates", device.device_id.hex()); - let db_req = - IdbDatabase::open_u32(&name, DB_VERSION).map_err(|e| anyhow::anyhow!("{e:?}"))?; + let db_req = Database::open(&name).with_version(DB_VERSION); // 2) Initialize the database (if needed) @@ -297,7 +325,8 @@ impl PlatformCertificatesStorage { } pub async fn stop(self) -> anyhow::Result<()> { - self.conn.close(); + // TODO: Should we wrap the connection in an Option to be able to take the value on close? + self.conn.as_ref().clone().close(); Ok(()) } @@ -305,7 +334,7 @@ impl PlatformCertificatesStorage { &mut self, ) -> anyhow::Result { Ok(PlatformCertificatesStorageForUpdateGuard { - transaction: Certificate::write(&self.conn)?, + transaction: Some(Certificate::write(&self.conn)?), }) } diff --git a/libparsec/crates/platform_storage/src/web/db.rs b/libparsec/crates/platform_storage/src/web/db.rs index cd1b32d2a13..56aa8244385 100644 --- a/libparsec/crates/platform_storage/src/web/db.rs +++ b/libparsec/crates/platform_storage/src/web/db.rs @@ -1,57 +1,66 @@ // Parsec Cloud (https://parsec.cloud) Copyright (c) BUSL-1.1 2016-present Scille SAS use indexed_db_futures::{ - prelude::IdbTransaction, - web_sys::{wasm_bindgen::JsValue, IdbTransactionMode}, - IdbDatabase, IdbQuerySource, + database::Database, + prelude::BuildSerde, + query_source::QuerySource, + transaction::{Transaction, TransactionMode}, + Build, BuildPrimitive, DeserialiseFromJs, KeyRange, SerialiseToJs, }; +use js_sys::wasm_bindgen::JsValue; +use libparsec_platform_async::stream::TryStreamExt; use libparsec_types::anyhow; use serde::de::DeserializeOwned; -pub(super) fn read<'a>(conn: &'a IdbDatabase, store: &str) -> anyhow::Result> { - conn.transaction_on_one_with_mode(store, IdbTransactionMode::Readonly) +pub(super) fn read<'a>(conn: &'a Database, store: &str) -> anyhow::Result> { + conn.transaction(store) + .with_mode(TransactionMode::Readonly) + .build() .map_err(|e| anyhow::anyhow!("{e:?}")) } -pub(super) fn write<'a>(conn: &'a IdbDatabase, store: &str) -> anyhow::Result> { - conn.transaction_on_one_with_mode(store, IdbTransactionMode::Readwrite) +pub(super) fn write<'a>(conn: &'a Database, store: &str) -> anyhow::Result> { + conn.transaction(store) + .with_mode(TransactionMode::Readwrite) + .build() .map_err(|e| anyhow::anyhow!("{e:?}")) } -pub(super) async fn commit(tx: IdbTransaction<'_>) -> anyhow::Result<()> { - tx.await.into_result().map_err(|e| anyhow::anyhow!("{e:?}")) +pub(super) async fn commit(tx: Transaction<'_>) -> anyhow::Result<()> { + tx.commit().await.map_err(|e| anyhow::anyhow!("{e:?}")) } -pub(super) async fn get_value( - tx: &IdbTransaction<'_>, +pub(super) async fn get_value( + tx: &Transaction<'_>, store: &str, - key: JsValue, + key: I, ) -> anyhow::Result> where V: DeserializeOwned, + I: Into>, + KeyRange: SerialiseToJs, { let store = tx .object_store(store) .map_err(|e| anyhow::anyhow!("{e:?}"))?; store - .get(&key) + .get(key) + .serde() .map_err(|e| anyhow::anyhow!("{e:?}"))? .await - .map_err(|e| anyhow::anyhow!("{e:?}"))? - .map(|x| serde_wasm_bindgen::from_value(x)) - .transpose() .map_err(|e| anyhow::anyhow!("{e:?}")) } -pub(super) async fn get_values( - tx: &IdbTransaction<'_>, +pub(super) async fn get_values( + tx: &Transaction<'_>, store: &str, index: &str, - key: JsValue, + query: Q, ) -> anyhow::Result> where V: DeserializeOwned, + Q: Into, { let store = tx .object_store(store) @@ -59,20 +68,20 @@ where let indexed_store = store.index(index).map_err(|e| anyhow::anyhow!("{e:?}"))?; - let values = indexed_store - .get_all_with_key(&key) + let get_all = indexed_store.get_all::().with_raw_query(query.into()); + let values = get_all + .serde() .map_err(|e| anyhow::anyhow!("{e:?}"))? .await .map_err(|e| anyhow::anyhow!("{e:?}"))?; values .into_iter() - .map(|x| serde_wasm_bindgen::from_value(x)) .collect::, _>>() .map_err(|e| anyhow::anyhow!("{e:?}")) } -pub(super) async fn get_all(tx: &IdbTransaction<'_>, store: &str) -> anyhow::Result> +pub(super) async fn get_all(tx: &Transaction<'_>, store: &str) -> anyhow::Result> where V: DeserializeOwned, { @@ -81,20 +90,20 @@ where .map_err(|e| anyhow::anyhow!("{e:?}"))?; let values = store - .get_all() + .get_all::() + .serde() .map_err(|e| anyhow::anyhow!("{e:?}"))? .await .map_err(|e| anyhow::anyhow!("{e:?}"))?; values .into_iter() - .map(|x| serde_wasm_bindgen::from_value(x)) .collect::, _>>() .map_err(|e| anyhow::anyhow!("{e:?}")) } pub(super) async fn list( - tx: &IdbTransaction<'_>, + tx: &Transaction<'_>, store: &str, offset: u32, limit: u32, @@ -102,28 +111,21 @@ pub(super) async fn list( where V: DeserializeOwned, { - use js_sys::Number; - use web_sys::IdbKeyRange; + use indexed_db_futures::KeyRange; // Index start at 1 let start = offset + 1; let end = start + limit; - let range = IdbKeyRange::bound_with_lower_open_and_upper_open( - &Number::from(start), - &Number::from(end), - false, - true, - ) - .map_err(|e| anyhow::anyhow!("{e:?}"))?; + let range = KeyRange::Bound(start, false, end, true); let store = tx .object_store(store) .map_err(|e| anyhow::anyhow!("{e:?}"))?; let Some(cursor) = store - .open_cursor_with_range_owned(range) - .map_err(|e| anyhow::anyhow!("{e:?}"))? + .open_cursor() + .with_query::>(range) .await .map_err(|e| anyhow::anyhow!("{e:?}"))? else { @@ -131,27 +133,22 @@ where }; cursor - .into_vec(0) + .stream_ser::() + .try_collect() .await .map_err(|e| anyhow::anyhow!("{e:?}")) - .and_then(|v: Vec<_>| { - v.into_iter() - .map(|key_val| - // TODO: Sad that KeyVal does not provide it's internal value without a reference. - // That reference force us to clone the value, which is not optimal. - // Could be improved if https://github.com/Alorel/rust-indexed-db/issues/39 is fixed - serde_wasm_bindgen::from_value(key_val.value().clone()) - .map_err(|e| anyhow::anyhow!("{e:?}"))) - .collect::>>() - }) } -pub(super) async fn count( - tx: &IdbTransaction<'_>, +pub(super) async fn count( + tx: &Transaction<'_>, store: &str, index: &str, - key: JsValue, -) -> anyhow::Result { + key: I, +) -> anyhow::Result +where + I: Into>, + KeyRange: SerialiseToJs, +{ let store = tx .object_store(store) .map_err(|e| anyhow::anyhow!("{e:?}"))?; @@ -159,13 +156,15 @@ pub(super) async fn count( let indexed_store = store.index(index).map_err(|e| anyhow::anyhow!("{e:?}"))?; indexed_store - .count_with_key(&key) + .count() + .with_query(key) + .serde() .map_err(|e| anyhow::anyhow!("{e:?}"))? .await .map_err(|e| anyhow::anyhow!("{e:?}")) } -pub(super) async fn clear(tx: &IdbTransaction<'_>, store: &str) -> anyhow::Result<()> { +pub(super) async fn clear(tx: &Transaction<'_>, store: &str) -> anyhow::Result<()> { let store = tx .object_store(store) .map_err(|e| anyhow::anyhow!("{e:?}"))?; @@ -175,45 +174,56 @@ pub(super) async fn clear(tx: &IdbTransaction<'_>, store: &str) -> anyhow::Resul Ok(()) } -pub(super) async fn insert_with_key( - tx: &IdbTransaction<'_>, +pub(super) async fn insert_with_key( + tx: &Transaction<'_>, store: &str, - key: JsValue, - value: JsValue, -) -> anyhow::Result<()> { + key: K, + value: V, +) -> anyhow::Result<()> +where + K: SerialiseToJs + DeserialiseFromJs, + V: SerialiseToJs, +{ let store = tx .object_store(store) .map_err(|e| anyhow::anyhow!("{e:?}"))?; store - .put_key_val(&key, &value) - .map_err(|e| anyhow::anyhow!("{e:?} ({value:?}) is invalid"))? + .put(value) + .with_key(key) + .serde() + .map_err(|e| anyhow::anyhow!("{e:?}"))? .await + .and(Ok(())) .map_err(|e| anyhow::anyhow!("{e:?}")) } -pub(super) async fn insert( - tx: &IdbTransaction<'_>, - store: &str, - value: JsValue, -) -> anyhow::Result<()> { +pub(super) async fn insert(tx: &Transaction<'_>, store: &str, value: V) -> anyhow::Result<()> +where + V: SerialiseToJs, +{ let store = tx .object_store(store) .map_err(|e| anyhow::anyhow!("{e:?}"))?; store - .put_val(&value) - .map_err(|e| anyhow::anyhow!("{e:?} ({value:?}) is invalid"))? + .put(value) + .serde() + .map_err(|e| anyhow::anyhow!("{e:?}"))? .await .map_err(|e| anyhow::anyhow!("{e:?}")) } -pub(super) async fn remove( - tx: &IdbTransaction<'_>, +pub(super) async fn remove( + tx: &Transaction<'_>, store: &str, index: &str, - key: JsValue, -) -> anyhow::Result<()> { + key: I, +) -> anyhow::Result<()> +where + I: Into> + std::fmt::Debug, + KeyRange: indexed_db_futures::primitive::TryToJs, +{ let store = tx .object_store(store) .map_err(|e| anyhow::anyhow!("{e:?}"))?; @@ -221,17 +231,20 @@ pub(super) async fn remove( let indexed_store = store.index(index).map_err(|e| anyhow::anyhow!("{e:?}"))?; if let Some(primary_key) = indexed_store - .get_key(&key) - .map_err(|e| anyhow::anyhow!("{e:?} ({key:?}) is invalid"))? + .get_key(key) + .with_key_type::() + .primitive() + .map_err(|e| anyhow::anyhow!("{e:?}"))? .await .map_err(|e| anyhow::anyhow!("{e:?}"))? { store .delete(&primary_key) + .primitive() .map_err(|e| anyhow::anyhow!("{e:?}"))? .await .map_err(|e| anyhow::anyhow!("{e:?}")) } else { - Err(anyhow::anyhow!("Entry with {key:?} not found")) + Err(anyhow::anyhow!("Entry not found")) } } diff --git a/libparsec/crates/platform_storage/src/web/model.rs b/libparsec/crates/platform_storage/src/web/model.rs index 40983197150..b32638c56c6 100644 --- a/libparsec/crates/platform_storage/src/web/model.rs +++ b/libparsec/crates/platform_storage/src/web/model.rs @@ -1,7 +1,13 @@ // Parsec Cloud (https://parsec.cloud) Copyright (c) BUSL-1.1 2016-present Scille SAS -use indexed_db_futures::{prelude::*, web_sys::DomException}; -use js_sys::{wasm_bindgen::JsValue, Array, Uint8Array}; +use indexed_db_futures::{ + database::{Database, VersionChangeEvent}, + factory::OpenDbRequestBuilder, + transaction::Transaction, + Build, KeyPath, KeyPathSeq, +}; +use js_sys::Array; +use js_sys::{wasm_bindgen::JsValue, Uint8Array}; use serde::{Deserialize, Serialize}; use libparsec_types::prelude::*; @@ -27,13 +33,12 @@ impl CertificateFilter<'_> { } } - fn to_js_array(&self) -> JsValue { + fn to_query(&self) -> Array { match &self.0 { GetCertificateQuery::NoFilter { certificate_type } => { let array = Array::new_with_length(1); array.set(0, JsValue::from(*certificate_type)); - - array.into() + array } GetCertificateQuery::Filter1 { certificate_type, @@ -41,18 +46,16 @@ impl CertificateFilter<'_> { } => { let array = Array::new_with_length(2); array.set(0, JsValue::from(*certificate_type)); - match filter1 { FilterKind::Null => (), FilterKind::Bytes(filter) => { - array.set(1, JsValue::from(Uint8Array::from(filter.as_ref()))); + array.set(1, Uint8Array::from(filter.as_ref()).into()) } FilterKind::U64(filter) => { - array.set(1, JsValue::from(Uint8Array::from(filter.as_ref()))); + array.set(1, Uint8Array::from(filter.as_ref()).into()) } } - - array.into() + array } GetCertificateQuery::Filter2 { certificate_type, @@ -60,18 +63,16 @@ impl CertificateFilter<'_> { } => { let array = Array::new_with_length(2); array.set(0, JsValue::from(*certificate_type)); - match filter2 { FilterKind::Null => (), FilterKind::Bytes(filter) => { - array.set(1, JsValue::from(Uint8Array::from(filter.as_ref()))); + array.set(1, Uint8Array::from(filter.as_ref()).into()) } FilterKind::U64(filter) => { - array.set(1, JsValue::from(Uint8Array::from(filter.as_ref()))); + array.set(1, Uint8Array::from(filter.as_ref()).into()) } } - - array.into() + array } GetCertificateQuery::BothFilters { certificate_type, @@ -79,33 +80,29 @@ impl CertificateFilter<'_> { filter2, } => { let array = Array::new_with_length(3); - array.set(0, JsValue::from(*certificate_type)); - let mut index = 1; - + array.set(0, JsValue::from(*certificate_type)); match filter1 { FilterKind::Null => (), FilterKind::Bytes(filter) => { - array.set(index, JsValue::from(Uint8Array::from(filter.as_ref()))); + array.set(index, Uint8Array::from(filter.as_ref()).into()); index += 1; } FilterKind::U64(filter) => { - array.set(index, JsValue::from(Uint8Array::from(filter.as_ref()))); + array.set(index, Uint8Array::from(filter.as_ref()).into()); index += 1; } } - match filter2 { FilterKind::Null => (), FilterKind::Bytes(filter) => { - array.set(index, JsValue::from(Uint8Array::from(filter.as_ref()))); + array.set(index, Uint8Array::from(filter.as_ref()).into()) } FilterKind::U64(filter) => { - array.set(index, JsValue::from(Uint8Array::from(filter.as_ref()))); + array.set(index, Uint8Array::from(filter.as_ref()).into()) } } - - array.into() + array } GetCertificateQuery::Filter1EqFilter2WhereFilter1 { .. } | GetCertificateQuery::Filter1EqFilter1WhereFilter2 { .. } @@ -131,47 +128,66 @@ impl Certificate { const INDEX_FILTER2: &'static str = "filter2"; const INDEX_FILTERS: &'static str = "filters"; - fn create(evt: &IdbVersionChangeEvent) -> Result<(), DomException> { - if !evt.db().object_store_names().any(|n| n == Self::STORE) { - let store = evt.db().create_object_store_with_params( - Self::STORE, - IdbObjectStoreParameters::default().auto_increment(true), - )?; - store.create_index( - Self::INDEX_CERTIFICATE_TYPE, - &IdbKeyPath::str_sequence(&[Self::INDEX_CERTIFICATE_TYPE]), - )?; - store.create_index( - Self::INDEX_FILTER1, - &IdbKeyPath::str_sequence(&[Self::INDEX_CERTIFICATE_TYPE, Self::INDEX_FILTER1]), - )?; - store.create_index( - Self::INDEX_FILTER2, - &IdbKeyPath::str_sequence(&[Self::INDEX_CERTIFICATE_TYPE, Self::INDEX_FILTER2]), - )?; - store.create_index( - Self::INDEX_FILTERS, - &IdbKeyPath::str_sequence(&[ + fn create( + _evt: &VersionChangeEvent, + db: &Database, + ) -> Result<(), indexed_db_futures::error::Error> { + if !db.object_store_names().any(|n| n == Self::STORE) { + let store = db + .create_object_store(Self::STORE) + .with_auto_increment(true) + .build()?; + store + .create_index( Self::INDEX_CERTIFICATE_TYPE, + KeyPath::Sequence(KeyPathSeq::from_slice(&[Self::INDEX_CERTIFICATE_TYPE])), + ) + .build()?; + store + .create_index( Self::INDEX_FILTER1, + KeyPath::Sequence(KeyPathSeq::from_slice(&[ + Self::INDEX_CERTIFICATE_TYPE, + Self::INDEX_FILTER1, + ])), + ) + .build()?; + store + .create_index( Self::INDEX_FILTER2, - ]), - )?; + KeyPath::Sequence(KeyPathSeq::from_slice(&[ + Self::INDEX_CERTIFICATE_TYPE, + Self::INDEX_FILTER2, + ])), + ) + .build()?; + store + .create_index( + Self::INDEX_FILTERS, + KeyPath::Sequence(KeyPathSeq::from_slice(&[ + Self::INDEX_CERTIFICATE_TYPE, + Self::INDEX_FILTER1, + Self::INDEX_FILTER2, + ])), + ) + .build()?; } Ok(()) } - pub(super) fn write(conn: &IdbDatabase) -> anyhow::Result { + pub(super) fn write(conn: &Database) -> anyhow::Result { + log::debug!("Create write transaction for certificates"); super::db::write(conn, Self::STORE) } - pub(super) async fn clear(tx: &IdbTransaction<'_>) -> anyhow::Result<()> { + pub(super) async fn clear(tx: &Transaction<'_>) -> anyhow::Result<()> { + log::debug!("Clear the certificate store"); super::db::clear(tx, Self::STORE).await } pub(super) async fn get_values( - conn: &IdbTransaction<'_>, + conn: &Transaction<'_>, filter: CertificateFilter<'_>, ) -> anyhow::Result> { match &filter.0 { @@ -179,7 +195,7 @@ impl Certificate { | GetCertificateQuery::Filter1 { .. } | GetCertificateQuery::Filter2 { .. } | GetCertificateQuery::BothFilters { .. } => { - super::db::get_values(conn, Self::STORE, filter.index(), filter.to_js_array()).await + super::db::get_values(conn, Self::STORE, filter.index(), filter.to_query()).await } GetCertificateQuery::Filter1EqFilter2WhereFilter1 { @@ -195,7 +211,7 @@ impl Certificate { conn, Self::STORE, subquery_filter.index(), - subquery_filter.to_js_array(), + subquery_filter.to_query(), ) .await?; let sub_query_filter2_result = match sub_query_certifs.get(0) { @@ -214,7 +230,7 @@ impl Certificate { conn, Self::STORE, main_filter.index(), - main_filter.to_js_array(), + main_filter.to_query(), ) .await } @@ -231,7 +247,7 @@ impl Certificate { conn, Self::STORE, subquery_filter.index(), - subquery_filter.to_js_array(), + subquery_filter.to_query(), ) .await?; let sub_query_filter1_result = match sub_query_certifs.get(0) { @@ -250,7 +266,7 @@ impl Certificate { conn, Self::STORE, main_filter.index(), - main_filter.to_js_array(), + main_filter.to_query(), ) .await } @@ -267,7 +283,7 @@ impl Certificate { conn, Self::STORE, subquery_filter.index(), - subquery_filter.to_js_array(), + subquery_filter.to_query(), ) .await?; let sub_query_filter1_result = match sub_query_certifs.get(0) { @@ -286,7 +302,7 @@ impl Certificate { conn, Self::STORE, main_filter.index(), - main_filter.to_js_array(), + main_filter.to_query(), ) .await } @@ -303,7 +319,7 @@ impl Certificate { conn, Self::STORE, subquery_filter.index(), - subquery_filter.to_js_array(), + subquery_filter.to_query(), ) .await?; let sub_query_filter2_result = match sub_query_certifs.get(0) { @@ -322,16 +338,15 @@ impl Certificate { conn, Self::STORE, main_filter.index(), - main_filter.to_js_array(), + main_filter.to_query(), ) .await } } } - pub(super) async fn insert(&self, tx: &IdbTransaction<'_>) -> anyhow::Result<()> { - let value = serde_wasm_bindgen::to_value(self).map_err(|e| anyhow::anyhow!("{e:?}"))?; - super::db::insert(tx, Self::STORE, value).await + pub(super) async fn insert(&self, tx: &Transaction<'_>) -> anyhow::Result<()> { + super::db::insert(tx, Self::STORE, self).await } } @@ -351,68 +366,70 @@ impl Chunk { const INDEX_CHUNK_ID: &'static str = "chunk_id"; const INDEX_IS_BLOCK: &'static str = "is_block"; - fn create(evt: &IdbVersionChangeEvent) -> Result<(), DomException> { - if !evt.db().object_store_names().any(|n| n == Self::STORE) { - let store = evt.db().create_object_store_with_params( - Self::STORE, - IdbObjectStoreParameters::default().auto_increment(true), - )?; - let index_params = IdbIndexParameters::new(); - index_params.set_unique(true); - store.create_index_with_params( - Self::INDEX_CHUNK_ID, - &IdbKeyPath::str(Self::INDEX_CHUNK_ID), - &index_params, - )?; - store.create_index(Self::INDEX_IS_BLOCK, &IdbKeyPath::str(Self::INDEX_IS_BLOCK))?; + fn create( + _evt: &VersionChangeEvent, + db: &Database, + ) -> Result<(), indexed_db_futures::error::Error> { + if !db.object_store_names().any(|n| n == Self::STORE) { + let store = db + .create_object_store(Self::STORE) + .with_auto_increment(true) + .build()?; + store + .create_index(Self::INDEX_CHUNK_ID, KeyPath::One(Self::INDEX_CHUNK_ID)) + .with_unique(true) + .build()?; + store + .create_index(Self::INDEX_IS_BLOCK, KeyPath::One(Self::INDEX_IS_BLOCK)) + .build()?; } Ok(()) } - pub(super) fn read(conn: &IdbDatabase) -> anyhow::Result { + pub(super) fn read(conn: &Database) -> anyhow::Result { super::db::read(conn, Self::STORE) } - pub(super) fn write(conn: &IdbDatabase) -> anyhow::Result { + pub(super) fn write(conn: &Database) -> anyhow::Result { super::db::write(conn, Self::STORE) } - pub(super) async fn count_blocks(tx: &IdbTransaction<'_>) -> anyhow::Result { - super::db::count(tx, Self::STORE, Self::INDEX_IS_BLOCK, 1.into()) + pub(super) async fn count_blocks(tx: &Transaction<'_>) -> anyhow::Result { + super::db::count(tx, Self::STORE, Self::INDEX_IS_BLOCK, 1_u32) .await .map(|x| x as IndexInt) } pub(super) async fn get( - tx: &IdbTransaction<'_>, + tx: &Transaction<'_>, chunk_id: &Bytes, ) -> anyhow::Result> { - let chunk_id = - serde_wasm_bindgen::to_value(&chunk_id).map_err(|e| anyhow::anyhow!("{e:?}"))?; - super::db::get_values(tx, Self::STORE, Self::INDEX_CHUNK_ID, chunk_id) - .await - .map(|x| x.into_iter().nth(0)) + super::db::get_values( + tx, + Self::STORE, + Self::INDEX_CHUNK_ID, + Uint8Array::from(chunk_id.as_ref()), + ) + .await + .map(|x| x.into_iter().nth(0)) } #[cfg(any(test, feature = "expose-test-methods"))] - pub(super) async fn get_chunks(tx: &IdbTransaction<'_>) -> anyhow::Result> { - super::db::get_values(tx, Self::STORE, Self::INDEX_IS_BLOCK, 0.into()).await + pub(super) async fn get_chunks(tx: &Transaction<'_>) -> anyhow::Result> { + super::db::get_values(tx, Self::STORE, Self::INDEX_IS_BLOCK, 0_u32).await } - pub(super) async fn get_blocks(tx: &IdbTransaction<'_>) -> anyhow::Result> { - super::db::get_values(tx, Self::STORE, Self::INDEX_IS_BLOCK, 1.into()).await + pub(super) async fn get_blocks(tx: &Transaction<'_>) -> anyhow::Result> { + super::db::get_values(tx, Self::STORE, Self::INDEX_IS_BLOCK, 1_u32).await } - pub(super) async fn insert(&self, tx: &IdbTransaction<'_>) -> anyhow::Result<()> { - let value = serde_wasm_bindgen::to_value(self).map_err(|e| anyhow::anyhow!("{e:?}"))?; - super::db::insert(tx, Self::STORE, value).await + pub(super) async fn insert(&self, tx: &Transaction<'_>) -> anyhow::Result<()> { + super::db::insert(tx, Self::STORE, self).await } - pub(super) async fn remove(tx: &IdbTransaction<'_>, chunk_id: &Bytes) -> anyhow::Result<()> { - let chunk_id = - serde_wasm_bindgen::to_value(chunk_id).map_err(|e| anyhow::anyhow!("{e:?}"))?; - super::db::remove(tx, Self::STORE, Self::INDEX_CHUNK_ID, chunk_id).await + pub(super) async fn remove(tx: &Transaction<'_>, chunk_id: &Bytes) -> anyhow::Result<()> { + super::db::remove(tx, Self::STORE, Self::INDEX_CHUNK_ID, chunk_id.as_ref()).await } } @@ -425,35 +442,33 @@ pub(super) struct PreventSyncPattern { impl PreventSyncPattern { const STORE: &'static str = "prevent_sync_pattern"; - fn create(evt: &IdbVersionChangeEvent) -> Result<(), DomException> { - if !evt.db().object_store_names().any(|n| n == Self::STORE) { - evt.db().create_object_store_with_params( - Self::STORE, - &IdbObjectStoreParameters::default(), - )?; + fn create( + _evt: &VersionChangeEvent, + db: &Database, + ) -> Result<(), indexed_db_futures::error::Error> { + if !db.object_store_names().any(|n| n == Self::STORE) { + db.create_object_store(Self::STORE).build()?; } Ok(()) } - pub(super) fn read(conn: &IdbDatabase) -> anyhow::Result { + pub(super) fn read(conn: &Database) -> anyhow::Result { super::db::read(conn, Self::STORE) } - pub(super) fn write(conn: &IdbDatabase) -> anyhow::Result { + pub(super) fn write(conn: &Database) -> anyhow::Result { super::db::write(conn, Self::STORE) } /// Retrieve the prevent sync pattern with `id = 0` from the database. - pub(super) async fn get(tx: &IdbTransaction<'_>) -> anyhow::Result> { - super::db::get_value(&tx, Self::STORE, 0.into()).await + pub(super) async fn get(tx: &Transaction<'_>) -> anyhow::Result> { + super::db::get_value(&tx, Self::STORE, 0_u32).await } /// Insert the current sync pattern into the database. - pub(super) async fn insert(&self, tx: &IdbTransaction<'_>) -> anyhow::Result<()> { - let value = serde_wasm_bindgen::to_value(self).map_err(|e| anyhow::anyhow!("{e:?}"))?; - - super::db::insert_with_key(&tx, Self::STORE, 0.into(), value).await + pub(super) async fn insert(&self, tx: &Transaction<'_>) -> anyhow::Result<()> { + super::db::insert_with_key(&tx, Self::STORE, 0_u32, self).await } } @@ -465,32 +480,33 @@ pub(super) struct RealmCheckpoint { impl RealmCheckpoint { const STORE: &'static str = "realm_checkpoint"; - fn create(evt: &IdbVersionChangeEvent) -> Result<(), DomException> { - if !evt.db().object_store_names().any(|n| n == Self::STORE) { - evt.db().create_object_store_with_params( - Self::STORE, - IdbObjectStoreParameters::default().auto_increment(true), - )?; + fn create( + _evt: &VersionChangeEvent, + db: &Database, + ) -> Result<(), indexed_db_futures::error::Error> { + if !db.object_store_names().any(|n| n == Self::STORE) { + db.create_object_store(Self::STORE) + .with_auto_increment(true) + .build()?; } Ok(()) } - pub(super) fn read(conn: &IdbDatabase) -> anyhow::Result { + pub(super) fn read(conn: &Database) -> anyhow::Result { super::db::read(conn, Self::STORE) } - pub(super) fn write(conn: &IdbDatabase) -> anyhow::Result { + pub(super) fn write(conn: &Database) -> anyhow::Result { super::db::write(conn, Self::STORE) } - pub(super) async fn get(tx: &IdbTransaction<'_>) -> anyhow::Result> { - super::db::get_value(&tx, Self::STORE, 0.into()).await + pub(super) async fn get(tx: &Transaction<'_>) -> anyhow::Result> { + super::db::get_value(&tx, Self::STORE, 0_u32).await } - pub(super) async fn insert(&self, tx: &IdbTransaction<'_>) -> anyhow::Result<()> { - let value = serde_wasm_bindgen::to_value(self).map_err(|e| anyhow::anyhow!("{e:?}"))?; - super::db::insert_with_key(&tx, Self::STORE, 0.into(), value).await + pub(super) async fn insert(&self, tx: &Transaction<'_>) -> anyhow::Result<()> { + super::db::insert_with_key(&tx, Self::STORE, 0_u32, self).await } } @@ -502,12 +518,14 @@ pub(super) struct Remanence { impl Remanence { const STORE: &'static str = "remanence"; - fn create(evt: &IdbVersionChangeEvent) -> Result<(), DomException> { - if !evt.db().object_store_names().any(|n| n == Self::STORE) { - evt.db().create_object_store_with_params( - Self::STORE, - IdbObjectStoreParameters::default().auto_increment(true), - )?; + fn create( + _evt: &VersionChangeEvent, + db: &Database, + ) -> Result<(), indexed_db_futures::error::Error> { + if !db.object_store_names().any(|n| n == Self::STORE) { + db.create_object_store(Self::STORE) + .with_auto_increment(true) + .build()?; } Ok(()) @@ -528,59 +546,58 @@ impl Vlob { const INDEX_VLOB_ID: &'static str = "vlob_id"; const INDEX_NEED_SYNC: &'static str = "need_sync"; - fn create(evt: &IdbVersionChangeEvent) -> Result<(), DomException> { - if !evt.db().object_store_names().any(|n| n == Self::STORE) { - let store = evt.db().create_object_store_with_params( - Self::STORE, - IdbObjectStoreParameters::default().auto_increment(true), - )?; - let index_params = IdbIndexParameters::new(); - index_params.set_unique(true); - store.create_index_with_params( - Self::INDEX_VLOB_ID, - &IdbKeyPath::str(Self::INDEX_VLOB_ID), - &index_params, - )?; - store.create_index( - Self::INDEX_NEED_SYNC, - &IdbKeyPath::str(Self::INDEX_NEED_SYNC), - )?; + fn create( + _evt: &VersionChangeEvent, + db: &Database, + ) -> Result<(), indexed_db_futures::error::Error> { + if !db.object_store_names().any(|n| n == Self::STORE) { + let store = db + .create_object_store(Self::STORE) + .with_auto_increment(true) + .build()?; + store + .create_index(Self::INDEX_VLOB_ID, KeyPath::One(Self::INDEX_VLOB_ID)) + .with_unique(true) + .build()?; + store + .create_index(Self::INDEX_NEED_SYNC, KeyPath::One(Self::INDEX_NEED_SYNC)) + .build()?; } Ok(()) } - pub(super) fn read(conn: &IdbDatabase) -> anyhow::Result { + pub(super) fn read(conn: &Database) -> anyhow::Result { super::db::read(conn, Self::STORE) } - pub(super) fn write(conn: &IdbDatabase) -> anyhow::Result { + pub(super) fn write(conn: &Database) -> anyhow::Result { super::db::write(conn, Self::STORE) } - pub(super) async fn get( - tx: &IdbTransaction<'_>, - vlob_id: &Bytes, - ) -> anyhow::Result> { - let vlob_id = - serde_wasm_bindgen::to_value(&vlob_id).map_err(|e| anyhow::anyhow!("{e:?}"))?; - super::db::get_values(&tx, Self::STORE, Self::INDEX_VLOB_ID, vlob_id) - .await - .map(|x| x.into_iter().nth(0)) + pub(super) async fn get(tx: &Transaction<'_>, vlob_id: &Bytes) -> anyhow::Result> { + super::db::get_values( + &tx, + Self::STORE, + Self::INDEX_VLOB_ID, + Uint8Array::from(vlob_id.as_ref()), + ) + .await + .map(|x| x.into_iter().nth(0)) } - pub(super) async fn get_need_sync(conn: &IdbDatabase) -> anyhow::Result> { + pub(super) async fn get_need_sync(conn: &Database) -> anyhow::Result> { let tx = Self::read(conn)?; - super::db::get_values(&tx, Self::STORE, Self::INDEX_NEED_SYNC, true.into()).await + super::db::get_values(&tx, Self::STORE, Self::INDEX_NEED_SYNC, true).await } - pub(super) async fn get_all(conn: &IdbDatabase) -> anyhow::Result> { + pub(super) async fn get_all(conn: &Database) -> anyhow::Result> { let tx = Self::read(conn)?; super::db::get_all(&tx, Self::STORE).await } pub(super) async fn list( - conn: &IdbDatabase, + conn: &Database, offset: u32, limit: u32, ) -> anyhow::Result> { @@ -588,31 +605,28 @@ impl Vlob { super::db::list(&tx, Self::STORE, offset, limit).await } - pub(super) async fn remove(tx: &IdbTransaction<'_>, vlob_id: &Bytes) -> anyhow::Result<()> { - let vlob_id = - serde_wasm_bindgen::to_value(vlob_id).map_err(|e| anyhow::anyhow!("{e:?}"))?; - super::db::remove(&tx, Self::STORE, Self::INDEX_VLOB_ID, vlob_id).await + pub(super) async fn remove(tx: &Transaction<'_>, vlob_id: &Bytes) -> anyhow::Result<()> { + super::db::remove(&tx, Self::STORE, Self::INDEX_VLOB_ID, vlob_id.as_ref()).await } - pub(super) async fn insert(&self, tx: &IdbTransaction<'_>) -> anyhow::Result<()> { - let value = serde_wasm_bindgen::to_value(self).map_err(|e| anyhow::anyhow!("{e:?}"))?; - super::db::insert(&tx, Self::STORE, value).await + pub(super) async fn insert(&self, tx: &Transaction<'_>) -> anyhow::Result<()> { + super::db::insert(&tx, Self::STORE, self).await } } pub(super) async fn initialize_model_if_needed( - mut db_req: OpenDbRequest, -) -> anyhow::Result { - db_req.set_on_upgrade_needed(Some(|evt: &IdbVersionChangeEvent| { - Vlob::create(evt)?; - RealmCheckpoint::create(evt)?; - PreventSyncPattern::create(evt)?; - Chunk::create(evt)?; - Remanence::create(evt)?; - Certificate::create(evt)?; + db_req: OpenDbRequestBuilder<&'_ String, u32>, +) -> anyhow::Result { + let db_req = db_req.with_on_upgrade_needed(|evt: VersionChangeEvent, db: Database| { + Vlob::create(&evt, &db)?; + RealmCheckpoint::create(&evt, &db)?; + PreventSyncPattern::create(&evt, &db)?; + Chunk::create(&evt, &db)?; + Remanence::create(&evt, &db)?; + Certificate::create(&evt, &db)?; Ok(()) - })); + }); let conn = db_req.await.map_err(|e| anyhow::anyhow!("{e:?}"))?; diff --git a/libparsec/crates/platform_storage/src/web/user.rs b/libparsec/crates/platform_storage/src/web/user.rs index 886657d21b9..19e5ef21fc8 100644 --- a/libparsec/crates/platform_storage/src/web/user.rs +++ b/libparsec/crates/platform_storage/src/web/user.rs @@ -2,7 +2,7 @@ use std::{path::Path, sync::Arc}; -use indexed_db_futures::prelude::*; +use indexed_db_futures::database::Database; use libparsec_types::prelude::*; use crate::web::{ @@ -18,7 +18,7 @@ pub struct NeedSyncEntries { #[derive(Debug)] pub struct PlatformUserStorage { - conn: Arc, + conn: Arc, realm_id: VlobID, } @@ -42,8 +42,7 @@ impl PlatformUserStorage { #[cfg(not(feature = "test-with-testbed"))] let name = format!("{}-user", device.device_id.hex()); - let db_req = - IdbDatabase::open_u32(&name, DB_VERSION).map_err(|e| anyhow::anyhow!("{e:?}"))?; + let db_req = Database::open(&name).with_version(DB_VERSION); // 2) Initialize the database (if needed) @@ -58,7 +57,9 @@ impl PlatformUserStorage { } pub async fn stop(&self) -> anyhow::Result<()> { - self.conn.close(); + // TODO: Should we wrap the connection in an Option to be able to take it once closing the + // storage? + self.conn.as_ref().clone().close(); Ok(()) } diff --git a/libparsec/crates/platform_storage/src/web/workspace.rs b/libparsec/crates/platform_storage/src/web/workspace.rs index 74435c06ca9..1880f63127d 100644 --- a/libparsec/crates/platform_storage/src/web/workspace.rs +++ b/libparsec/crates/platform_storage/src/web/workspace.rs @@ -6,7 +6,7 @@ use std::{path::Path, sync::Arc}; -use indexed_db_futures::{prelude::IdbTransaction, IdbDatabase}; +use indexed_db_futures::{database::Database, transaction::Transaction}; use libparsec_types::prelude::*; #[cfg(any(test, feature = "expose-test-methods"))] @@ -24,7 +24,7 @@ use crate::{ #[derive(Debug)] pub(crate) struct PlatformWorkspaceStorage { - conn: Arc, + conn: Arc, cache_max_blocks: u64, } @@ -33,7 +33,8 @@ unsafe impl Send for PlatformWorkspaceStorage {} impl PlatformWorkspaceStorage { pub async fn no_populate_start( - _data_base_dir: &Path, + #[cfg_attr(not(feature = "test-with-testbed"), allow(unused_variables))] + data_base_dir: &Path, device: &LocalDevice, realm_id: VlobID, cache_max_blocks: u64, @@ -43,7 +44,7 @@ impl PlatformWorkspaceStorage { #[cfg(feature = "test-with-testbed")] let name = format!( "{}-{}-{}-workspace", - _data_base_dir.to_str().unwrap(), + data_base_dir.display(), device.device_id.hex(), realm_id.hex() ); @@ -51,8 +52,7 @@ impl PlatformWorkspaceStorage { #[cfg(not(feature = "test-with-testbed"))] let name = format!("{}-{}-workspace", device.device_id.hex(), realm_id.hex()); - let db_req = - IdbDatabase::open_u32(&name, DB_VERSION).map_err(|e| anyhow::anyhow!("{e:?}"))?; + let db_req = Database::open(&name).with_version(DB_VERSION); // 2) Initialize the database (if needed) @@ -68,7 +68,9 @@ impl PlatformWorkspaceStorage { } pub async fn stop(self) -> anyhow::Result<()> { - self.conn.close(); + // TODO: Should we wrap the connection in an Option to be able to take it once closing the + // storage? + self.conn.as_ref().clone().close(); Ok(()) } @@ -517,7 +519,7 @@ pub async fn workspace_storage_non_speculative_init( } pub async fn db_populate_manifest<'a>( - tx: &IdbTransaction<'a>, + tx: &Transaction<'a>, manifest: &UpdateManifestData, ) -> anyhow::Result { match Vlob::get(tx, &manifest.entry_id.as_bytes().to_vec().into()).await? { @@ -538,7 +540,7 @@ pub async fn db_populate_manifest<'a>( } async fn db_update_manifest<'a>( - tx: &IdbTransaction<'a>, + tx: &Transaction<'a>, manifest: &UpdateManifestData, ) -> anyhow::Result<()> { match Vlob::get(tx, &manifest.entry_id.as_bytes().to_vec().into()).await? { @@ -569,7 +571,7 @@ async fn db_update_manifest<'a>( } async fn db_get_chunk( - tx: &IdbTransaction<'_>, + tx: &Transaction<'_>, chunk_id: ChunkID, ) -> anyhow::Result> { match super::model::Chunk::get(tx, &chunk_id.as_bytes().to_vec().into()).await? { @@ -579,7 +581,7 @@ async fn db_get_chunk( } async fn db_insert_block( - tx: &IdbTransaction<'_>, + tx: &Transaction<'_>, block_id: BlockID, encrypted: &[u8], accessed_on: DateTime, @@ -596,6 +598,6 @@ async fn db_insert_block( .await } -async fn db_remove_chunk(tx: &IdbTransaction<'_>, chunk_id: ChunkID) -> anyhow::Result<()> { +async fn db_remove_chunk(tx: &Transaction<'_>, chunk_id: ChunkID) -> anyhow::Result<()> { super::model::Chunk::remove(tx, &chunk_id.as_bytes().to_vec().into()).await } diff --git a/server/src/lib.rs b/server/src/lib.rs index 02980416c2f..f1846b5f878 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -1,4 +1,5 @@ // Parsec Cloud (https://parsec.cloud) Copyright (c) BUSL-1.1 2016-present Scille SAS +#![cfg(not(target_family = "wasm"))] use pyo3::{ exceptions::PyException,