use tauri::State; use crate::credentials::Credential; use crate::AppState; /// Guard helper: lock the credentials mutex and return a ref to the inner /// `CredentialService`, or a "Vault is locked" error if the vault has not /// been unlocked for this session. /// /// This is a macro rather than a function because returning a `MutexGuard` /// from a helper function would require lifetime annotations that complicate /// the tauri command signatures unnecessarily. macro_rules! require_unlocked { ($state:expr) => {{ let guard = $state .credentials .lock() .map_err(|_| "Credentials mutex was poisoned".to_string())?; if guard.is_none() { return Err("Vault is locked — call unlock before accessing credentials".into()); } // SAFETY: we just checked `is_none` above, so `unwrap` cannot panic. guard }}; } /// Return all credentials ordered by name. /// /// Secret values (passwords, private keys) are never included — only metadata. #[tauri::command] pub fn list_credentials(state: State<'_, AppState>) -> Result, String> { let guard = require_unlocked!(state); guard.as_ref().unwrap().list() } /// Store a new username/password credential. /// /// The `password` value is encrypted with AES-256-GCM before being persisted. /// Returns the created credential record (without the plaintext password). /// `domain` is `None` for non-domain credentials; `Some("")` is treated as NULL. #[tauri::command] pub fn create_password( name: String, username: String, password: String, domain: Option, state: State<'_, AppState>, ) -> Result { let guard = require_unlocked!(state); guard .as_ref() .unwrap() .create_password(name, username, password, domain) } /// Store a new SSH private key credential. /// /// Both `private_key_pem` and `passphrase` are encrypted before storage. /// Pass `None` for `passphrase` when the key has no passphrase. /// Returns the created credential record without any secret material. #[tauri::command] pub fn create_ssh_key( name: String, username: String, private_key_pem: String, passphrase: Option, state: State<'_, AppState>, ) -> Result { let guard = require_unlocked!(state); guard .as_ref() .unwrap() .create_ssh_key(name, username, private_key_pem, passphrase) } /// Delete a credential by id. /// /// For SSH key credentials, the associated `ssh_keys` row is also deleted. /// Returns `Err` if the vault is locked or the id does not exist. #[tauri::command] pub fn delete_credential(id: i64, state: State<'_, AppState>) -> Result<(), String> { let guard = require_unlocked!(state); guard.as_ref().unwrap().delete(id) } /// Decrypt and return the password for a credential. #[tauri::command] pub fn decrypt_password(credential_id: i64, state: State<'_, AppState>) -> Result { let guard = require_unlocked!(state); guard.as_ref().unwrap().decrypt_password(credential_id) } /// Decrypt and return the SSH private key and passphrase. #[tauri::command] pub fn decrypt_ssh_key(ssh_key_id: i64, state: State<'_, AppState>) -> Result<(String, String), String> { let guard = require_unlocked!(state); guard.as_ref().unwrap().decrypt_ssh_key(ssh_key_id) }