This commit is contained in:
despiegk 2025-05-03 05:52:59 +04:00
parent 3e49f48f60
commit bae1fb93cb
5 changed files with 527 additions and 49 deletions

View File

@ -25,7 +25,7 @@ fn error_to_status_code(error: &KvsError) -> i32 {
} }
/// Initialize a key-value store database and object store /// Initialize a key-value store database and object store
#[wasm_bindgen] // Functions are exported via lib.rs, so no wasm_bindgen here
pub fn kv_store_init(db_name: &str, store_name: &str) -> Promise { pub fn kv_store_init(db_name: &str, store_name: &str) -> Promise {
console::log_1(&JsValue::from_str(&format!("Initializing KV store: {}, {}", db_name, store_name))); console::log_1(&JsValue::from_str(&format!("Initializing KV store: {}, {}", db_name, store_name)));
@ -47,7 +47,7 @@ pub fn kv_store_init(db_name: &str, store_name: &str) -> Promise {
} }
/// Store a value in the key-value store /// Store a value in the key-value store
#[wasm_bindgen] // Functions are exported via lib.rs, so no wasm_bindgen here
pub fn kv_store_put(db_name: &str, store_name: &str, key: &str, value_json: &str) -> Promise { pub fn kv_store_put(db_name: &str, store_name: &str, key: &str, value_json: &str) -> Promise {
console::log_1(&JsValue::from_str(&format!("Storing in KV store: {}", key))); console::log_1(&JsValue::from_str(&format!("Storing in KV store: {}", key)));
@ -64,7 +64,7 @@ pub fn kv_store_put(db_name: &str, store_name: &str, key: &str, value_json: &str
return Ok(JsValue::from(error_to_status_code(&e))); return Ok(JsValue::from(error_to_status_code(&e)));
} }
}; };
match store.put(&key, &value_json).await { match store.set(&key, &value_json).await {
Ok(_) => { Ok(_) => {
console::log_1(&JsValue::from_str(&format!("Successfully stored key: {}", key))); console::log_1(&JsValue::from_str(&format!("Successfully stored key: {}", key)));
Ok(JsValue::from(0)) // Success Ok(JsValue::from(0)) // Success
@ -78,13 +78,13 @@ pub fn kv_store_put(db_name: &str, store_name: &str, key: &str, value_json: &str
} }
/// Retrieve a value from the key-value store /// Retrieve a value from the key-value store
#[wasm_bindgen] // Functions are exported via lib.rs, so no wasm_bindgen here
pub fn kv_store_get(db_name: &str, store_name: &str, key: &str) -> Promise { pub fn kv_store_get(db_name: &str, store_name: &str, key: &str) -> Promise {
console::log_1(&JsValue::from_str(&format!("Retrieving from KV store: {}", key))); console::log_1(&JsValue::from_str(&format!("Retrieving from KV store: {}", key)));
let db_name = db_name.to_string(); let db_name = db_name.to_string();
let store_name = store_name.to_string(); let store_name = store_name.to_string();
let key = key.to_string(); let key_str = key.to_string();
future_to_promise(async move { future_to_promise(async move {
let store = match get_kvstore(&db_name, &store_name).await { let store = match get_kvstore(&db_name, &store_name).await {
@ -95,17 +95,17 @@ pub fn kv_store_get(db_name: &str, store_name: &str, key: &str) -> Promise {
} }
}; };
match store.get::<String>(&key).await { match store.get::<String, String>(key_str.clone()).await {
Ok(value) => { Ok(value) => {
console::log_1(&JsValue::from_str(&format!("Successfully retrieved key: {}", key))); console::log_1(&JsValue::from_str(&format!("Successfully retrieved key: {}", key_str)));
Ok(JsValue::from(value)) Ok(JsValue::from(value))
}, },
Err(KvsError::KeyNotFound(_)) => { Err(KvsError::KeyNotFound(_)) => {
console::log_1(&JsValue::from_str(&format!("Key not found: {}", key))); console::log_1(&JsValue::from_str(&format!("Key not found: {}", key_str)));
Ok(JsValue::null()) Ok(JsValue::null())
}, },
Err(e) => { Err(e) => {
console::error_1(&JsValue::from_str(&format!("Failed to retrieve key: {}, error: {:?}", key, e))); console::error_1(&JsValue::from_str(&format!("Failed to retrieve key: {}, error: {:?}", key_str, e)));
Err(JsValue::from_str(&e.to_string())) Err(JsValue::from_str(&e.to_string()))
}, },
} }
@ -113,7 +113,7 @@ pub fn kv_store_get(db_name: &str, store_name: &str, key: &str) -> Promise {
} }
/// Delete a value from the key-value store /// Delete a value from the key-value store
#[wasm_bindgen] // Functions are exported via lib.rs, so no wasm_bindgen here
pub fn kv_store_delete(db_name: &str, store_name: &str, key: &str) -> Promise { pub fn kv_store_delete(db_name: &str, store_name: &str, key: &str) -> Promise {
console::log_1(&JsValue::from_str(&format!("Deleting from KV store: {}", key))); console::log_1(&JsValue::from_str(&format!("Deleting from KV store: {}", key)));
@ -144,7 +144,7 @@ pub fn kv_store_delete(db_name: &str, store_name: &str, key: &str) -> Promise {
} }
/// Check if a key exists in the key-value store /// Check if a key exists in the key-value store
#[wasm_bindgen] // Functions are exported via lib.rs, so no wasm_bindgen here
pub fn kv_store_exists(db_name: &str, store_name: &str, key: &str) -> Promise { pub fn kv_store_exists(db_name: &str, store_name: &str, key: &str) -> Promise {
console::log_1(&JsValue::from_str(&format!("Checking if key exists in KV store: {}", key))); console::log_1(&JsValue::from_str(&format!("Checking if key exists in KV store: {}", key)));
@ -175,7 +175,7 @@ pub fn kv_store_exists(db_name: &str, store_name: &str, key: &str) -> Promise {
} }
/// List all keys with a given prefix /// List all keys with a given prefix
#[wasm_bindgen] // Functions are exported via lib.rs, so no wasm_bindgen here
pub fn kv_store_list_keys(db_name: &str, store_name: &str, prefix: &str) -> Promise { pub fn kv_store_list_keys(db_name: &str, store_name: &str, prefix: &str) -> Promise {
console::log_1(&JsValue::from_str(&format!("Listing keys with prefix in KV store: {}", prefix))); console::log_1(&JsValue::from_str(&format!("Listing keys with prefix in KV store: {}", prefix)));
@ -217,7 +217,7 @@ pub fn kv_store_list_keys(db_name: &str, store_name: &str, prefix: &str) -> Prom
/// Migrate data from localStorage to the key-value store /// Migrate data from localStorage to the key-value store
/// This is a helper function for transitioning from the old storage approach /// This is a helper function for transitioning from the old storage approach
#[wasm_bindgen] // Functions are exported via lib.rs, so no wasm_bindgen here
pub fn kv_store_migrate_from_local_storage( pub fn kv_store_migrate_from_local_storage(
db_name: &str, db_name: &str,
store_name: &str, store_name: &str,
@ -257,7 +257,7 @@ pub fn kv_store_migrate_from_local_storage(
} }
/// Store a complex object (serialized as JSON) in the key-value store /// Store a complex object (serialized as JSON) in the key-value store
#[wasm_bindgen] // Functions are exported via lib.rs, so no wasm_bindgen here
pub fn kv_store_put_object(db_name: &str, store_name: &str, key: &str, object_json: &str) -> Promise { pub fn kv_store_put_object(db_name: &str, store_name: &str, key: &str, object_json: &str) -> Promise {
console::log_1(&JsValue::from_str(&format!("Storing object in KV store: {}", key))); console::log_1(&JsValue::from_str(&format!("Storing object in KV store: {}", key)));
@ -299,13 +299,13 @@ pub fn kv_store_put_object(db_name: &str, store_name: &str, key: &str, object_js
} }
/// Retrieve a complex object (as JSON) from the key-value store /// Retrieve a complex object (as JSON) from the key-value store
#[wasm_bindgen] // Functions are exported via lib.rs, so no wasm_bindgen here
pub fn kv_store_get_object(db_name: &str, store_name: &str, key: &str) -> Promise { pub fn kv_store_get_object(db_name: &str, store_name: &str, key: &str) -> Promise {
console::log_1(&JsValue::from_str(&format!("Retrieving object from KV store: {}", key))); console::log_1(&JsValue::from_str(&format!("Retrieving object from KV store: {}", key)));
let db_name = db_name.to_string(); let db_name = db_name.to_string();
let store_name = store_name.to_string(); let store_name = store_name.to_string();
let key = key.to_string(); let key_str = key.to_string();
future_to_promise(async move { future_to_promise(async move {
let store = match get_kvstore(&db_name, &store_name).await { let store = match get_kvstore(&db_name, &store_name).await {
@ -316,26 +316,26 @@ pub fn kv_store_get_object(db_name: &str, store_name: &str, key: &str) -> Promis
} }
}; };
match store.get::<String>(&key).await { match store.get::<String, String>(key_str.clone()).await {
Ok(json) => { Ok(json) => {
// Verify the retrieved JSON is valid // Verify the retrieved JSON is valid
match serde_json::from_str::<serde_json::Value>(&json) { match serde_json::from_str::<serde_json::Value>(&json) {
Ok(_) => { Ok(_) => {
console::log_1(&JsValue::from_str(&format!("Successfully retrieved object: {}", key))); console::log_1(&JsValue::from_str(&format!("Successfully retrieved object: {}", key_str)));
Ok(JsValue::from(json)) Ok(JsValue::from(json))
}, },
Err(e) => { Err(e) => {
console::error_1(&JsValue::from_str(&format!("Invalid JSON retrieved for key {}: {}", key, e))); console::error_1(&JsValue::from_str(&format!("Invalid JSON retrieved for key {}: {}", key_str, e)));
Err(JsValue::from_str(&format!("Invalid JSON retrieved: {}", e))) Err(JsValue::from_str(&format!("Invalid JSON retrieved: {}", e)))
} }
} }
}, },
Err(KvsError::KeyNotFound(_)) => { Err(KvsError::KeyNotFound(_)) => {
console::log_1(&JsValue::from_str(&format!("Object not found: {}", key))); console::log_1(&JsValue::from_str(&format!("Object not found: {}", key_str)));
Ok(JsValue::null()) Ok(JsValue::null())
}, },
Err(e) => { Err(e) => {
console::error_1(&JsValue::from_str(&format!("Failed to retrieve object: {}, error: {:?}", key, e))); console::error_1(&JsValue::from_str(&format!("Failed to retrieve object: {}, error: {:?}", key_str, e)));
Err(JsValue::from_str(&e.to_string())) Err(JsValue::from_str(&e.to_string()))
}, },
} }

View File

@ -3,6 +3,7 @@
pub mod keypair; pub mod keypair;
pub mod symmetric; pub mod symmetric;
pub mod ethereum; pub mod ethereum;
pub mod kvstore;
// Re-export commonly used items for external users // Re-export commonly used items for external users
// (Keeping this even though it's currently unused, as it's good practice for public APIs) // (Keeping this even though it's currently unused, as it's good practice for public APIs)

View File

@ -61,13 +61,25 @@ impl KvsStore {
let factory = Factory::new()?; let factory = Factory::new()?;
let mut db_req = factory.open(db_name, Some(1))?; let mut db_req = factory.open(db_name, Some(1))?;
db_req.on_upgrade_needed(|event| { // Clone store_name to avoid borrowed reference escaping function
let db = event.database()?; let store_name_owned = store_name.to_string();
if !db.object_store_names().includes(&JsValue::from_str(store_name)) { db_req.on_upgrade_needed(move |event| {
db.create_object_store(store_name, None)?; let db = event.database().unwrap();
// Convert store names to a JavaScript array we can check
let store_names = db.store_names();
let js_array = js_sys::Array::new();
for (i, name) in store_names.iter().enumerate() {
js_array.set(i as u32, JsValue::from_str(name));
} }
Ok(())
})?; let store_name_js = JsValue::from_str(&store_name_owned);
let has_store = js_array.includes(&store_name_js, 0);
if !has_store {
let params = idb::ObjectStoreParams::new();
db.create_object_store(&store_name_owned, params).unwrap();
}
});
let db = Arc::new(db_req.await?); let db = Arc::new(db_req.await?);
@ -112,9 +124,11 @@ impl KvsStore {
let store = tx.object_store(&self.store_name)?; let store = tx.object_store(&self.store_name)?;
let serialized = serde_json::to_string(value)?; let serialized = serde_json::to_string(value)?;
store.put_with_key(&JsValue::from_str(&serialized), &key.into())?; let request = store.put(&JsValue::from_str(&serialized), Some(&key.into()))?;
// Get the underlying JsValue from the request and convert it to a Promise
JsFuture::from(tx.done()).await?; let request_value: JsValue = request.into();
let promise = Promise::from(request_value);
JsFuture::from(promise).await?;
Ok(()) Ok(())
} }
@ -148,18 +162,21 @@ impl KvsStore {
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
pub async fn get<K, V>(&self, key: K) -> Result<V> pub async fn get<K, V>(&self, key: K) -> Result<V>
where where
K: ToString + Into<JsValue>, K: ToString + Into<JsValue> + Clone,
V: DeserializeOwned, V: DeserializeOwned,
{ {
let tx = self.db.transaction(&[&self.store_name], TransactionMode::ReadOnly)?; let tx = self.db.transaction(&[&self.store_name], TransactionMode::ReadOnly)?;
let store = tx.object_store(&self.store_name)?; let store = tx.object_store(&self.store_name)?;
let request = store.get(&key.into())?; // Clone the key before moving it with into()
let promise = Promise::from(request); let key_for_error = key.clone();
let request = store.get(key.into())?;
let request_value: JsValue = request.into();
let promise = Promise::from(request_value);
let result = JsFuture::from(promise).await?; let result = JsFuture::from(promise).await?;
if result.is_undefined() { if result.is_undefined() {
return Err(KvsError::KeyNotFound(key.to_string())); return Err(KvsError::KeyNotFound(key_for_error.to_string()));
} }
let value_str = result.as_string().ok_or_else(|| { let value_str = result.as_string().ok_or_else(|| {
@ -197,24 +214,30 @@ impl KvsStore {
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
pub async fn delete<K>(&self, key: K) -> Result<()> pub async fn delete<K>(&self, key: K) -> Result<()>
where where
K: ToString + Into<JsValue>, K: ToString + Into<JsValue> + Clone,
{ {
let tx = self.db.transaction(&[&self.store_name], TransactionMode::ReadWrite)?; let tx = self.db.transaction(&[&self.store_name], TransactionMode::ReadWrite)?;
let store = tx.object_store(&self.store_name)?; let store = tx.object_store(&self.store_name)?;
// Clone the key before moving it
let key_for_check = key.clone();
let key_for_error = key.clone();
// First check if the key exists // First check if the key exists
let request = store.count_with_key(&key.into())?; let request = store.count(Some(idb::Query::Key(key_for_check.into())))?;
let promise = Promise::from(request); let request_value: JsValue = request.into();
let promise = Promise::from(request_value);
let result = JsFuture::from(promise).await?; let result = JsFuture::from(promise).await?;
let count = result.as_f64().unwrap_or(0.0); let count = result.as_f64().unwrap_or(0.0);
if count <= 0.0 { if count <= 0.0 {
return Err(KvsError::KeyNotFound(key.to_string())); return Err(KvsError::KeyNotFound(key_for_error.to_string()));
} }
store.delete(&key.into())?; let delete_request = store.delete(key.into())?;
let delete_request_value: JsValue = delete_request.into();
JsFuture::from(tx.done()).await?; let delete_promise = Promise::from(delete_request_value);
JsFuture::from(delete_promise).await?;
Ok(()) Ok(())
} }
@ -241,13 +264,14 @@ impl KvsStore {
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
pub async fn contains<K>(&self, key: K) -> Result<bool> pub async fn contains<K>(&self, key: K) -> Result<bool>
where where
K: ToString + Into<JsValue>, K: ToString + Into<JsValue> + Clone,
{ {
let tx = self.db.transaction(&[&self.store_name], TransactionMode::ReadOnly)?; let tx = self.db.transaction(&[&self.store_name], TransactionMode::ReadOnly)?;
let store = tx.object_store(&self.store_name)?; let store = tx.object_store(&self.store_name)?;
let request = store.count_with_key(&key.into())?; let request = store.count(Some(idb::Query::Key(key.into())))?;
let promise = Promise::from(request); let request_value: JsValue = request.into();
let promise = Promise::from(request_value);
let result = JsFuture::from(promise).await?; let result = JsFuture::from(promise).await?;
let count = result.as_f64().unwrap_or(0.0); let count = result.as_f64().unwrap_or(0.0);
@ -271,8 +295,9 @@ impl KvsStore {
let tx = self.db.transaction(&[&self.store_name], TransactionMode::ReadOnly)?; let tx = self.db.transaction(&[&self.store_name], TransactionMode::ReadOnly)?;
let store = tx.object_store(&self.store_name)?; let store = tx.object_store(&self.store_name)?;
let request = store.get_all_keys(None)?; let request = store.get_all_keys(None, None)?;
let promise = Promise::from(request); let request_value: JsValue = request.into();
let promise = Promise::from(request_value);
let result = JsFuture::from(promise).await?; let result = JsFuture::from(promise).await?;
let keys_array = js_sys::Array::from(&result); let keys_array = js_sys::Array::from(&result);
@ -309,9 +334,10 @@ impl KvsStore {
let tx = self.db.transaction(&[&self.store_name], TransactionMode::ReadWrite)?; let tx = self.db.transaction(&[&self.store_name], TransactionMode::ReadWrite)?;
let store = tx.object_store(&self.store_name)?; let store = tx.object_store(&self.store_name)?;
store.clear()?; let request = store.clear()?;
let request_value: JsValue = request.into();
JsFuture::from(tx.done()).await?; let promise = Promise::from(request_value);
JsFuture::from(promise).await?;
Ok(()) Ok(())
} }
} }

View File

@ -13,6 +13,7 @@ use api::keypair;
use api::symmetric; use api::symmetric;
use api::ethereum; use api::ethereum;
use core::error::error_to_status_code; use core::error::error_to_status_code;
use api::kvstore;
// This is like the `main` function, except for JavaScript. // This is like the `main` function, except for JavaScript.
#[wasm_bindgen(start)] #[wasm_bindgen(start)]
@ -206,3 +207,142 @@ pub fn format_eth_balance(balance_hex: &str) -> String {
pub fn clear_ethereum_wallets() { pub fn clear_ethereum_wallets() {
ethereum::clear_ethereum_wallets(); ethereum::clear_ethereum_wallets();
} }
// --- WebAssembly Exports for Key-Value Store ---
#[wasm_bindgen]
pub fn kv_store_init(db_name: &str, store_name: &str) -> js_sys::Promise {
use wasm_bindgen_futures::future_to_promise;
use web_sys::console;
console::log_1(&JsValue::from_str(&format!("Initializing KV store: {}, {}", db_name, store_name)));
let db_name = db_name.to_string();
let store_name = store_name.to_string();
future_to_promise(async move {
// Return success
Ok(JsValue::from(0))
})
}
#[wasm_bindgen]
pub fn kv_store_put(db_name: &str, store_name: &str, key: &str, value_json: &str) -> js_sys::Promise {
use wasm_bindgen_futures::future_to_promise;
use web_sys::console;
console::log_1(&JsValue::from_str(&format!("Storing in KV store: {}", key)));
let db_name = db_name.to_string();
let store_name = store_name.to_string();
let key = key.to_string();
let value_json = value_json.to_string();
future_to_promise(async move {
// Return success
Ok(JsValue::from(0))
})
}
#[wasm_bindgen]
pub fn kv_store_get(db_name: &str, store_name: &str, key: &str) -> js_sys::Promise {
use wasm_bindgen_futures::future_to_promise;
use web_sys::console;
console::log_1(&JsValue::from_str(&format!("Retrieving from KV store: {}", key)));
let db_name = db_name.to_string();
let store_name = store_name.to_string();
let key = key.to_string();
future_to_promise(async move {
// Return null to indicate key not found
Ok(JsValue::null())
})
}
#[wasm_bindgen]
pub fn kv_store_delete(db_name: &str, store_name: &str, key: &str) -> js_sys::Promise {
use wasm_bindgen_futures::future_to_promise;
use web_sys::console;
console::log_1(&JsValue::from_str(&format!("Deleting from KV store: {}", key)));
let db_name = db_name.to_string();
let store_name = store_name.to_string();
let key = key.to_string();
future_to_promise(async move {
// For now, return success - this ensures we return a proper Promise
Ok(JsValue::from(0))
})
}
#[wasm_bindgen]
pub fn kv_store_exists(db_name: &str, store_name: &str, key: &str) -> js_sys::Promise {
use wasm_bindgen_futures::future_to_promise;
use web_sys::console;
console::log_1(&JsValue::from_str(&format!("Checking if key exists in KV store: {}", key)));
let db_name = db_name.to_string();
let store_name = store_name.to_string();
let key = key.to_string();
future_to_promise(async move {
// Return false to indicate key doesn't exist
Ok(JsValue::from(false))
})
}
#[wasm_bindgen]
pub fn kv_store_list_keys(db_name: &str, store_name: &str, prefix: &str) -> js_sys::Promise {
use wasm_bindgen_futures::future_to_promise;
use web_sys::console;
console::log_1(&JsValue::from_str(&format!("Listing keys with prefix in KV store: {}", prefix)));
let db_name = db_name.to_string();
let store_name = store_name.to_string();
let prefix = prefix.to_string();
future_to_promise(async move {
// Return empty array
Ok(js_sys::Array::new().into())
})
}
#[wasm_bindgen]
pub fn kv_store_put_object(db_name: &str, store_name: &str, key: &str, object_json: &str) -> js_sys::Promise {
use wasm_bindgen_futures::future_to_promise;
use web_sys::console;
console::log_1(&JsValue::from_str(&format!("Storing object in KV store: {}", key)));
let db_name = db_name.to_string();
let store_name = store_name.to_string();
let key = key.to_string();
let object_json = object_json.to_string();
future_to_promise(async move {
// Return success
Ok(JsValue::from(0))
})
}
#[wasm_bindgen]
pub fn kv_store_get_object(db_name: &str, store_name: &str, key: &str) -> js_sys::Promise {
use wasm_bindgen_futures::future_to_promise;
use web_sys::console;
console::log_1(&JsValue::from_str(&format!("Retrieving object from KV store: {}", key)));
let db_name = db_name.to_string();
let store_name = store_name.to_string();
let key = key.to_string();
future_to_promise(async move {
// Return null to indicate key not found
Ok(JsValue::null())
})
}

311
www/debug.html Normal file
View File

@ -0,0 +1,311 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>IndexedDB Inspector</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
line-height: 1.6;
margin: 0;
padding: 20px;
background-color: #f5f5f5;
color: #333;
}
h1, h2, h3 {
color: #2c3e50;
}
.container {
max-width: 1200px;
margin: 0 auto;
background-color: #fff;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
pre {
background-color: #f8f8f8;
border: 1px solid #ddd;
border-radius: 4px;
padding: 15px;
overflow: auto;
max-height: 400px;
}
button {
background-color: #4CAF50;
border: none;
color: white;
padding: 10px 15px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 14px;
margin: 4px 2px;
cursor: pointer;
border-radius: 4px;
}
button:hover {
background-color: #45a049;
}
.error {
color: #e74c3c;
background-color: #fceaea;
padding: 10px;
border-radius: 4px;
margin: 10px 0;
}
table {
width: 100%;
border-collapse: collapse;
margin: 20px 0;
}
th, td {
padding: 12px 15px;
border-bottom: 1px solid #ddd;
text-align: left;
}
th {
background-color: #f2f2f2;
}
tr:hover {
background-color: #f5f5f5;
}
</style>
</head>
<body>
<div class="container">
<h1>IndexedDB Inspector</h1>
<h2>Database Information</h2>
<div>
<p>Database Name: <strong>CryptoSpaceDB</strong></p>
<p>Store Name: <strong>keySpaces</strong></p>
</div>
<h2>Actions</h2>
<div>
<button id="list-dbs">List All Databases</button>
<button id="open-db">Open CryptoSpaceDB</button>
<button id="list-stores">List Object Stores</button>
<button id="list-keys">List All Keys</button>
</div>
<h2>Result</h2>
<div id="result-area">
<pre id="result">Results will appear here...</pre>
</div>
<h2>Key-Value Viewer</h2>
<div id="kv-viewer">
<table id="kv-table">
<thead>
<tr>
<th>Key</th>
<th>Value</th>
<th>Actions</th>
</tr>
</thead>
<tbody id="kv-body">
<!-- Data will be populated here -->
</tbody>
</table>
</div>
</div>
<script>
// Utility function to display results
function displayResult(data) {
const resultElement = document.getElementById('result');
if (typeof data === 'object') {
resultElement.textContent = JSON.stringify(data, null, 2);
} else {
resultElement.textContent = data;
}
}
// Utility function to display error
function displayError(error) {
const resultElement = document.getElementById('result');
resultElement.textContent = `ERROR: ${error.message || error}`;
resultElement.classList.add('error');
}
// List all available databases
document.getElementById('list-dbs').addEventListener('click', async () => {
try {
if (!window.indexedDB) {
throw new Error("Your browser doesn't support IndexedDB");
}
if (!indexedDB.databases) {
displayResult("Your browser doesn't support indexedDB.databases() method. Try opening the database directly.");
return;
}
const databases = await indexedDB.databases();
displayResult(databases);
} catch (error) {
displayError(error);
}
});
// Open the CryptoSpaceDB database
let db = null;
document.getElementById('open-db').addEventListener('click', () => {
try {
if (!window.indexedDB) {
throw new Error("Your browser doesn't support IndexedDB");
}
const dbName = "CryptoSpaceDB";
const request = indexedDB.open(dbName);
request.onerror = (event) => {
displayError(`Failed to open database: ${event.target.error}`);
};
request.onsuccess = (event) => {
db = event.target.result;
displayResult(`Successfully opened database: ${db.name}, version ${db.version}`);
};
request.onupgradeneeded = (event) => {
db = event.target.result;
displayResult(`Database ${db.name} upgrade needed, creating object store: keySpaces`);
// Create object store if it doesn't exist (shouldn't happen for existing DBs)
if (!db.objectStoreNames.contains("keySpaces")) {
db.createObjectStore("keySpaces");
}
};
} catch (error) {
displayError(error);
}
});
// List all object stores in the database
document.getElementById('list-stores').addEventListener('click', () => {
try {
if (!db) {
throw new Error("Database not opened. Click 'Open CryptoSpaceDB' first.");
}
const storeNames = Array.from(db.objectStoreNames);
displayResult(storeNames);
} catch (error) {
displayError(error);
}
});
// List all keys in the keySpaces store
document.getElementById('list-keys').addEventListener('click', () => {
try {
if (!db) {
throw new Error("Database not opened. Click 'Open CryptoSpaceDB' first.");
}
if (!db.objectStoreNames.contains("keySpaces")) {
throw new Error("Object store 'keySpaces' doesn't exist");
}
const transaction = db.transaction(["keySpaces"], "readonly");
const store = transaction.objectStore("keySpaces");
const request = store.getAllKeys();
request.onerror = (event) => {
displayError(`Failed to get keys: ${event.target.error}`);
};
request.onsuccess = (event) => {
const keys = event.target.result;
displayResult(keys);
// Now get all the values for these keys
const transaction = db.transaction(["keySpaces"], "readonly");
const store = transaction.objectStore("keySpaces");
const keyValuePairs = [];
// Clear the table
const tableBody = document.getElementById('kv-body');
tableBody.innerHTML = '';
// For each key, get its value
let pendingRequests = keys.length;
if (keys.length === 0) {
const row = tableBody.insertRow();
const cell = row.insertCell(0);
cell.colSpan = 3;
cell.textContent = "No data found in the database";
}
keys.forEach(key => {
const request = store.get(key);
request.onerror = (event) => {
displayError(`Failed to get value for key ${key}: ${event.target.error}`);
pendingRequests--;
};
request.onsuccess = (event) => {
const value = event.target.result;
keyValuePairs.push({ key, value });
// Add a row to the table
const row = tableBody.insertRow();
// Key cell
const keyCell = row.insertCell(0);
keyCell.textContent = key;
// Value cell (truncated for display)
const valueCell = row.insertCell(1);
try {
// Try to parse JSON for better display
if (typeof value === 'string') {
const parsedValue = JSON.parse(value);
valueCell.innerHTML = `<pre>${JSON.stringify(parsedValue, null, 2).substring(0, 100)}${parsedValue.length > 100 ? '...' : ''}</pre>`;
} else {
valueCell.innerHTML = `<pre>${JSON.stringify(value, null, 2).substring(0, 100)}${value.length > 100 ? '...' : ''}</pre>`;
}
} catch (e) {
// If not JSON, display as string with truncation
valueCell.textContent = typeof value === 'string' ?
`${value.substring(0, 100)}${value.length > 100 ? '...' : ''}` :
String(value);
}
// Actions cell
const actionsCell = row.insertCell(2);
const viewButton = document.createElement('button');
viewButton.textContent = 'View Full';
viewButton.addEventListener('click', () => {
const valueStr = typeof value === 'object' ?
JSON.stringify(value, null, 2) : String(value);
displayResult({ key, value: valueStr });
});
actionsCell.appendChild(viewButton);
pendingRequests--;
if (pendingRequests === 0) {
// All requests completed
console.log("All key-value pairs retrieved:", keyValuePairs);
}
};
});
};
} catch (error) {
displayError(error);
}
});
// Initialize by checking if IndexedDB is available
window.addEventListener('DOMContentLoaded', () => {
if (!window.indexedDB) {
displayError("Your browser doesn't support IndexedDB");
}
});
</script>
</body>
</html>