commit e379377449593e0d240fdac4b97dd7ef54d4c1e9
parent 413cb3de125c8712ed665bac99431c6530620485
Author: triesap <triesap@radroots.dev>
Date: Mon, 19 Jan 2026 18:07:22 +0000
app: guard bootstrap writes when data exists
- add helpers to detect existing config/app data
- skip bootstrap writes when records already exist
- re-export bootstrap presence helpers
- add unit tests for presence error mapping
Diffstat:
3 files changed, 62 insertions(+), 5 deletions(-)
diff --git a/app/src/bootstrap.rs b/app/src/bootstrap.rs
@@ -1,6 +1,6 @@
#![forbid(unsafe_code)]
-use radroots_app_core::datastore::RadrootsClientDatastore;
+use radroots_app_core::datastore::{RadrootsClientDatastore, RadrootsClientDatastoreError};
use crate::{
app_datastore_obj_key_cfg_data,
@@ -24,6 +24,18 @@ pub async fn app_datastore_write_config<T: RadrootsClientDatastore>(
.map_err(AppInitError::Datastore)
}
+pub async fn app_datastore_has_config<T: RadrootsClientDatastore>(
+ datastore: &T,
+ key_maps: &AppKeyMapConfig,
+) -> AppInitResult<bool> {
+ let key = app_datastore_obj_key_cfg_data(key_maps).map_err(AppInitError::Config)?;
+ match datastore.get_obj::<AppConfigData>(key).await {
+ Ok(_) => Ok(true),
+ Err(RadrootsClientDatastoreError::NoResult) => Ok(false),
+ Err(err) => Err(AppInitError::Datastore(err)),
+ }
+}
+
pub async fn app_datastore_write_app_data<T: RadrootsClientDatastore>(
datastore: &T,
key_maps: &AppKeyMapConfig,
@@ -36,6 +48,18 @@ pub async fn app_datastore_write_app_data<T: RadrootsClientDatastore>(
.map_err(AppInitError::Datastore)
}
+pub async fn app_datastore_has_app_data<T: RadrootsClientDatastore>(
+ datastore: &T,
+ key_maps: &AppKeyMapConfig,
+) -> AppInitResult<bool> {
+ let key = app_datastore_obj_key_app_data(key_maps).map_err(AppInitError::Config)?;
+ match datastore.get_obj::<AppAppData>(key).await {
+ Ok(_) => Ok(true),
+ Err(RadrootsClientDatastoreError::NoResult) => Ok(false),
+ Err(err) => Err(AppInitError::Datastore(err)),
+ }
+}
+
pub async fn app_datastore_clear_bootstrap<T: RadrootsClientDatastore>(
datastore: &T,
key_maps: &AppKeyMapConfig,
@@ -57,6 +81,8 @@ pub async fn app_datastore_clear_bootstrap<T: RadrootsClientDatastore>(
mod tests {
use super::{
app_datastore_clear_bootstrap,
+ app_datastore_has_app_data,
+ app_datastore_has_config,
app_datastore_write_app_data,
app_datastore_write_config,
};
@@ -102,4 +128,22 @@ mod tests {
.expect_err("idb undefined");
assert_eq!(err, AppInitError::Datastore(RadrootsClientDatastoreError::IdbUndefined));
}
+
+ #[test]
+ fn has_config_maps_idb_errors() {
+ let datastore = RadrootsClientWebDatastore::new(None);
+ let key_maps = app_key_maps_default();
+ let err = futures::executor::block_on(app_datastore_has_config(&datastore, &key_maps))
+ .expect_err("idb undefined");
+ assert_eq!(err, AppInitError::Datastore(RadrootsClientDatastoreError::IdbUndefined));
+ }
+
+ #[test]
+ fn has_app_data_maps_idb_errors() {
+ let datastore = RadrootsClientWebDatastore::new(None);
+ let key_maps = app_key_maps_default();
+ let err = futures::executor::block_on(app_datastore_has_app_data(&datastore, &key_maps))
+ .expect_err("idb undefined");
+ assert_eq!(err, AppInitError::Datastore(RadrootsClientDatastoreError::IdbUndefined));
+ }
}
diff --git a/app/src/init.rs b/app/src/init.rs
@@ -16,6 +16,8 @@ use radroots_app_core::keystore::{RadrootsClientKeystoreError, RadrootsClientWeb
use crate::{
app_datastore_clear_bootstrap,
+ app_datastore_has_app_data,
+ app_datastore_has_config,
app_datastore_write_app_data,
app_datastore_write_config,
AppAppData,
@@ -178,10 +180,19 @@ pub async fn app_init_backends(config: AppConfig) -> AppInitResult<AppBackends>
.init()
.await
.map_err(AppInitError::Datastore)?;
- let config_data = AppConfigData::default();
- let _ = app_datastore_write_config(&datastore, &config.datastore.key_maps, &config_data).await?;
- let app_data = AppAppData::default();
- let _ = app_datastore_write_app_data(&datastore, &config.datastore.key_maps, &app_data).await?;
+ let has_config = app_datastore_has_config(&datastore, &config.datastore.key_maps).await?;
+ if !has_config {
+ let config_data = AppConfigData::default();
+ let _ =
+ app_datastore_write_config(&datastore, &config.datastore.key_maps, &config_data)
+ .await?;
+ }
+ let has_app_data = app_datastore_has_app_data(&datastore, &config.datastore.key_maps).await?;
+ if !has_app_data {
+ let app_data = AppAppData::default();
+ let _ = app_datastore_write_app_data(&datastore, &config.datastore.key_maps, &app_data)
+ .await?;
+ }
let nostr_keystore = RadrootsClientWebKeystoreNostr::new(Some(config.keystore.nostr_store));
Ok(AppBackends {
config,
diff --git a/app/src/lib.rs b/app/src/lib.rs
@@ -11,6 +11,8 @@ mod entry;
pub use app::App;
pub use bootstrap::{
app_datastore_clear_bootstrap,
+ app_datastore_has_app_data,
+ app_datastore_has_config,
app_datastore_write_app_data,
app_datastore_write_config,
};