app

Local-first trade for farms and co-ops
git clone https://radroots.dev/git/app.git
Log | Files | Refs | README | LICENSE

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:
Mapp/src/bootstrap.rs | 46+++++++++++++++++++++++++++++++++++++++++++++-
Mapp/src/init.rs | 19+++++++++++++++----
Mapp/src/lib.rs | 2++
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, };