v0.2.6-pre.005
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-app-wallet-desk/src/app_state.rs
|
||||
// version: 4
|
||||
// version: 5
|
||||
|
||||
//! Shared backend state owned by the Wallet Desk Tauri application.
|
||||
|
||||
@@ -10,6 +10,7 @@ pub(crate) struct AppState {
|
||||
splash_settings: crate::SplashSettings,
|
||||
splash_sequence_started: std::sync::atomic::AtomicBool,
|
||||
wallet_config_startup: crate::WalletConfigStartup,
|
||||
wallet_session: std::sync::Mutex<crate::WalletSession>,
|
||||
}
|
||||
|
||||
impl AppState {
|
||||
@@ -58,6 +59,7 @@ impl AppState {
|
||||
splash_settings,
|
||||
splash_sequence_started: std::sync::atomic::AtomicBool::new(false),
|
||||
wallet_config_startup,
|
||||
wallet_session: std::sync::Mutex::new(crate::WalletSession::no_selection()),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -100,7 +102,7 @@ impl AppState {
|
||||
effective_wallets_directory_created_on_startup: self.wallet_config_startup.effective_directory_created_on_startup(),
|
||||
fallback_logging_active: runtime.fallback_active,
|
||||
root_wallets_directory_created_on_startup: self.wallet_config_startup.root_directory_created_on_startup(),
|
||||
shell_phase: "pre.004-wallet-inventory".to_owned(),
|
||||
shell_phase: "pre.005-wallet-session-create".to_owned(),
|
||||
startup_diagnostic: runtime.startup_diagnostic.clone(),
|
||||
wallets_directory: resolved.wallets_directory().to_string_lossy().into_owned(),
|
||||
wallets_subdirectory,
|
||||
@@ -113,6 +115,123 @@ impl AppState {
|
||||
return self.wallet_config_startup.resolved().effective_wallets_directory();
|
||||
}
|
||||
|
||||
/// Selects one locked Wallet and installs it as the durable backend session.
|
||||
pub(crate) async fn select_wallet(&self, request: crate::WalletSelectionRequestDto) -> ksp_core_lib::Result<crate::LockedWalletDto> {
|
||||
let deselected = self.deselect_wallet();
|
||||
if let std::result::Result::Err(error) = deselected {
|
||||
return std::result::Result::Err(error);
|
||||
}
|
||||
let root = self.wallet_inventory_root().to_path_buf();
|
||||
let resolved = crate::resolve_locked_wallet(root.as_path(), request.wallet_id).await;
|
||||
let (path, locked_info, dto) = match resolved {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let session = self.wallet_session.lock();
|
||||
let mut session = match session {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => return std::result::Result::Err(session_lock_error()),
|
||||
};
|
||||
*session = crate::WalletSession::Locked { wallet_id: dto.wallet_id.clone(), path, locked_info };
|
||||
return std::result::Result::Ok(dto);
|
||||
}
|
||||
|
||||
/// Drops any selected or authorized Wallet state and returns to `NoSelection`.
|
||||
pub(crate) fn deselect_wallet(&self) -> ksp_core_lib::Result<crate::WalletSessionDto> {
|
||||
let previous = {
|
||||
let session = self.wallet_session.lock();
|
||||
let mut session = match session {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => return std::result::Result::Err(session_lock_error()),
|
||||
};
|
||||
std::mem::replace(&mut *session, crate::WalletSession::no_selection())
|
||||
};
|
||||
drop(previous);
|
||||
ksp_logging_lib::debug!(target: crate::TRACING_TARGET, domain = crate::TRACING_DOMAIN_WALLET_SESSION, "Wallet session deselected and protected handle state purged");
|
||||
return std::result::Result::Ok(crate::WalletSession::no_selection().safe_projection());
|
||||
}
|
||||
|
||||
/// Creates a new Wallet under the effective root and retains the returned OWNER handle only in Rust.
|
||||
pub(crate) async fn create_wallet(&self, request: crate::WalletCreateRequestDto) -> ksp_core_lib::Result<crate::WalletAuthorizedDto> {
|
||||
let deselected = self.deselect_wallet();
|
||||
if let std::result::Result::Err(error) = deselected {
|
||||
return std::result::Result::Err(error);
|
||||
}
|
||||
let crate::WalletCreateRequestDto { alias, filename, initial_note, owner_password, view_password } = request;
|
||||
let root = self.wallet_inventory_root().to_path_buf();
|
||||
let destination = crate::new_wallet_destination(root.as_path(), filename.as_str());
|
||||
let destination = match destination {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let view_enabled = view_password.is_some();
|
||||
let owner_password = ksp_wallet_lib::OwnerPassword::new(owner_password);
|
||||
let view_password = view_password.map(ksp_wallet_lib::ViewPassword::new);
|
||||
let alias = normalize_optional_text(alias);
|
||||
let note = normalize_optional_text(initial_note);
|
||||
let note_texts = note.into_iter().collect::<std::vec::Vec<_>>();
|
||||
let metadata = ksp_wallet_lib::WalletCreateMetadataV1::new(alias, note_texts);
|
||||
ksp_logging_lib::debug!(target: crate::TRACING_TARGET, domain = crate::TRACING_DOMAIN_WALLET_SESSION, wallet_id = filename.as_str(), view_enabled, "Wallet creation requested under effective Config root");
|
||||
let created = ksp_wallet_lib::create_wallet_file_v1(destination.as_path(), owner_password, view_password, metadata).await;
|
||||
let owner = match created {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => {
|
||||
ksp_logging_lib::warn!(target: crate::TRACING_TARGET, domain = crate::TRACING_DOMAIN_WALLET_SESSION, wallet_id = filename.as_str(), error_domain = error.code().domain(), error_code = error.code().code(), "Wallet creation failed");
|
||||
return std::result::Result::Err(error);
|
||||
},
|
||||
};
|
||||
let dto = crate::owner_projection(filename.as_str(), view_enabled, &owner);
|
||||
let session = self.wallet_session.lock();
|
||||
let mut session = match session {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => return std::result::Result::Err(session_lock_error()),
|
||||
};
|
||||
*session = crate::WalletSession::Owner { wallet_id: filename.clone(), path: destination, wallet: owner };
|
||||
ksp_logging_lib::info!(target: crate::TRACING_TARGET, domain = crate::TRACING_DOMAIN_WALLET_SESSION, wallet_id = filename.as_str(), view_enabled, "Wallet created and OWNER session opened");
|
||||
return std::result::Result::Ok(dto);
|
||||
}
|
||||
|
||||
/// Drops an authorized handle, re-inspects the selected file and returns the session to `Locked`.
|
||||
pub(crate) async fn lock_wallet(&self) -> ksp_core_lib::Result<crate::LockedWalletDto> {
|
||||
let previous = {
|
||||
let session = self.wallet_session.lock();
|
||||
let mut session = match session {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => return std::result::Result::Err(session_lock_error()),
|
||||
};
|
||||
std::mem::replace(&mut *session, crate::WalletSession::no_selection())
|
||||
};
|
||||
let (wallet_id, path) = match previous {
|
||||
crate::WalletSession::NoSelection => {
|
||||
return std::result::Result::Err(ksp_core_lib::Error::new(crate::ERROR_CODE_WALLET_SESSION_INVALID, "No Wallet is selected to lock"));
|
||||
},
|
||||
crate::WalletSession::Locked { wallet_id, path, .. } => (wallet_id, path),
|
||||
crate::WalletSession::Owner { wallet_id, path, wallet } => {
|
||||
drop(wallet);
|
||||
(wallet_id, path)
|
||||
},
|
||||
};
|
||||
let locked = ksp_wallet_lib::inspect_locked_wallet_file_v1(path.as_path()).await;
|
||||
let locked = match locked {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let dto = crate::LockedWalletDto {
|
||||
filename: wallet_id.clone(),
|
||||
format_version: locked.format_version(),
|
||||
wallet_id: wallet_id.clone(),
|
||||
view_enabled: locked.view_enabled(),
|
||||
};
|
||||
let session = self.wallet_session.lock();
|
||||
let mut session = match session {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => return std::result::Result::Err(session_lock_error()),
|
||||
};
|
||||
*session = crate::WalletSession::Locked { wallet_id: wallet_id.clone(), path, locked_info: locked };
|
||||
ksp_logging_lib::debug!(target: crate::TRACING_TARGET, domain = crate::TRACING_DOMAIN_WALLET_SESSION, wallet_id = wallet_id.as_str(), "Wallet session locked and authorized handle state purged");
|
||||
return std::result::Result::Ok(dto);
|
||||
}
|
||||
|
||||
/// Returns the resolved common splash timings captured during bootstrap.
|
||||
#[must_use]
|
||||
pub(crate) const fn splash_settings(&self) -> crate::SplashSettings {
|
||||
@@ -128,6 +247,19 @@ impl AppState {
|
||||
}
|
||||
}
|
||||
|
||||
fn normalize_optional_text(value: std::option::Option<String>) -> std::option::Option<String> {
|
||||
return value.and_then(|text| {
|
||||
if text.trim().is_empty() {
|
||||
return std::option::Option::None;
|
||||
}
|
||||
return std::option::Option::Some(text);
|
||||
});
|
||||
}
|
||||
|
||||
fn session_lock_error() -> ksp_core_lib::Error {
|
||||
return ksp_core_lib::Error::new(crate::ERROR_CODE_APP_STATE_LOCK_FAILED, "Wallet Desk Wallet session state lock is poisoned");
|
||||
}
|
||||
|
||||
struct LoggingRuntimeState {
|
||||
guard: ksp_logging_lib::LoggingGuard,
|
||||
active_profile_id: std::option::Option<String>,
|
||||
|
||||
Reference in New Issue
Block a user