From 9866347e176c659e6622deb1cbedca9ffdbb313d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Florkiewicz?= Date: Fri, 5 Jul 2024 11:47:08 +0200 Subject: [PATCH] feat: Add requesting storage persistence for more quota (#318) --- node-wasm/Cargo.toml | 1 + node-wasm/src/error.rs | 27 +++++++++++++++++++++++++-- node-wasm/src/node.rs | 7 ++++++- node-wasm/src/utils.rs | 29 +++++++++++++++++++++++++++++ node-wasm/src/worker/channel.rs | 2 +- 5 files changed, 62 insertions(+), 4 deletions(-) diff --git a/node-wasm/Cargo.toml b/node-wasm/Cargo.toml index 5104f8d2..e0ec67db 100644 --- a/node-wasm/Cargo.toml +++ b/node-wasm/Cargo.toml @@ -56,6 +56,7 @@ web-sys = { version = "0.3.69", features = [ "Navigator", "SharedWorker", "SharedWorkerGlobalScope", + "StorageManager", "Worker", "WorkerGlobalScope", "WorkerOptions", diff --git a/node-wasm/src/error.rs b/node-wasm/src/error.rs index 210b8640..62f76a20 100644 --- a/node-wasm/src/error.rs +++ b/node-wasm/src/error.rs @@ -1,11 +1,11 @@ //! Error type and utilities. -use std::fmt::Display; +use std::fmt::{self, Display}; use serde::{Deserialize, Serialize}; use wasm_bindgen::convert::IntoWasmAbi; use wasm_bindgen::describe::WasmDescribe; -use wasm_bindgen::JsValue; +use wasm_bindgen::{JsCast, JsValue}; /// Alias for a `Result` with the error type [`Error`]. pub type Result = std::result::Result; @@ -50,6 +50,29 @@ impl Error { } } +impl Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result { + let Some(error) = self.0.dyn_ref::() else { + return write!(f, "{:?}", self.0.as_string()); + }; + + write!(f, "{} ({})", error.name(), error.message())?; + + let mut cause = error.cause(); + loop { + if let Some(error) = cause.dyn_ref::() { + write!(f, "\n{} ({})", error.name(), error.message())?; + cause = error.cause(); + } else { + write!(f, "\n{:?}", cause.as_string())?; + break; + } + } + + Ok(()) + } +} + // Similar to https://github.com/rustwasm/wasm-bindgen/blob/9b37613bbab0cd71f55dcc782d59dd00c1d4d200/src/describe.rs#L218-L222 // // This is needed to impl IntoWasmAbi. diff --git a/node-wasm/src/node.rs b/node-wasm/src/node.rs index 470580f8..21b2784f 100644 --- a/node-wasm/src/node.rs +++ b/node-wasm/src/node.rs @@ -5,6 +5,7 @@ use libp2p::identity::Keypair; use libp2p::multiaddr::Protocol; use serde::{Deserialize, Serialize}; use serde_wasm_bindgen::to_value; +use tracing::error; use wasm_bindgen::prelude::*; use web_sys::BroadcastChannel; @@ -14,7 +15,7 @@ use lumina_node::node::NodeConfig; use lumina_node::store::IndexedDbStore; use crate::error::{Context, Result}; -use crate::utils::{is_chrome, js_value_from_display, Network}; +use crate::utils::{is_chrome, js_value_from_display, request_storage_persistence, Network}; use crate::worker::commands::{CheckableResponseExt, NodeCommand, SingleHeaderQuery}; use crate::worker::{AnyWorker, WorkerClient}; use crate::wrapper::libp2p::NetworkInfoSnapshot; @@ -89,6 +90,10 @@ impl NodeDriver { worker_script_url: &str, worker_type: Option, ) -> Result { + if let Err(e) = request_storage_persistence().await { + error!("Error requesting storage persistence: {e}"); + } + // For chrome we default to running in a dedicated Worker because: // 1. Chrome Android does not support SharedWorkers at all // 2. On desktop Chrome, restarting Lumina's worker causes all network connections to fail. diff --git a/node-wasm/src/utils.rs b/node-wasm/src/utils.rs index 96286193..95b00ba2 100644 --- a/node-wasm/src/utils.rs +++ b/node-wasm/src/utils.rs @@ -5,6 +5,7 @@ use lumina_node::network; use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; use serde_repr::{Deserialize_repr, Serialize_repr}; +use tracing::{info, warn}; use tracing_subscriber::filter::LevelFilter; use tracing_subscriber::fmt::format::Pretty; use tracing_subscriber::fmt::time::UtcTime; @@ -150,6 +151,34 @@ where } } +/// Request persistent storage from user for us, which has side effect of increasing the quota we +/// have. This function doesn't `await` on JavaScript promise, as that would block until user +/// either allows or blocks our request in a prompt (and we cannot do much with the result anyway). +pub(crate) async fn request_storage_persistence() -> Result<(), Error> { + let fullfiled = Closure::once(move |granted: JsValue| { + if granted.is_truthy() { + info!("Storage persistence acquired: {:?}", granted); + } else { + warn!("User rejected storage persistance request") + } + }); + let rejected = Closure::once(move |_ev: JsValue| { + warn!("Error during persistant storage request"); + }); + + // don't drop the promise, we'll log the result and hope the user clicked the right button + let _promise = get_navigator()? + .storage() + .persist()? + .then2(&fullfiled, &rejected); + + // stop rust from dropping them + fullfiled.forget(); + rejected.forget(); + + Ok(()) +} + const CHROME_USER_AGENT_DETECTION_STR: &str = "Chrome/"; // Currently, there's an issue with SharedWorkers on Chrome where restarting Lumina's worker diff --git a/node-wasm/src/worker/channel.rs b/node-wasm/src/worker/channel.rs index 1d9489b0..b7be1262 100644 --- a/node-wasm/src/worker/channel.rs +++ b/node-wasm/src/worker/channel.rs @@ -161,7 +161,7 @@ impl AnyWorker { fn setup_on_error_callback(&self) -> Closure { let onerror = Closure::new(|ev: MessageEvent| { - error!("received error from Worker: {:?}", ev.to_string()); + error!("received error from Worker: {:?}", ev); }); match self { AnyWorker::SharedWorker(worker) => {