From df6b362a31829ce51402d5afc6b8316352a1e0ff Mon Sep 17 00:00:00 2001 From: Joseph Montanaro Date: Fri, 23 Dec 2022 11:34:17 -0800 Subject: [PATCH] return structured errors from commands (wip) --- src-tauri/src/config.rs | 63 +++++++++++++------------------ src-tauri/src/errors.rs | 45 +++++++++++++++++----- src-tauri/src/ipc.rs | 9 +++-- src-tauri/src/main.rs | 11 +++++- src-tauri/src/state.rs | 10 ++--- src/views/EnterCredentials.svelte | 1 + src/views/Home.svelte | 4 -- src/views/Unlock.svelte | 1 + 8 files changed, 82 insertions(+), 62 deletions(-) diff --git a/src-tauri/src/config.rs b/src-tauri/src/config.rs index 54882d1..b9a45c8 100644 --- a/src-tauri/src/config.rs +++ b/src-tauri/src/config.rs @@ -9,40 +9,24 @@ use crate::errors::*; #[derive(Clone, Debug, Serialize, Deserialize)] pub struct AppConfig { - pub db_path: PathBuf, + #[serde(default = "default_listen_addr")] pub listen_addr: Ipv4Addr, + #[serde(default = "default_listen_port")] pub listen_port: u16, + #[serde(default = "default_rehide_ms")] pub rehide_ms: u64, -} - - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct DbAppConfig { - listen_addr: Option, - listen_port: Option, - rehide_ms: Option, + #[serde(default = "default_start_minimized")] + pub start_minimized: bool, } impl Default for AppConfig { fn default() -> Self { AppConfig { - db_path: get_or_create_db_path(), - listen_addr: Ipv4Addr::LOCALHOST, - listen_port: listen_port(), - rehide_ms: 1000, - } - } -} - - -impl From for AppConfig { - fn from(db_config: DbAppConfig) -> Self { - AppConfig { - db_path: get_or_create_db_path(), - listen_addr: db_config.listen_addr.unwrap_or(Ipv4Addr::LOCALHOST), - listen_port: db_config.listen_port.unwrap_or_else(|| listen_port()), - rehide_ms: db_config.rehide_ms.unwrap_or(1000), + listen_addr: default_listen_addr(), + listen_port: default_listen_port(), + rehide_ms: default_rehide_ms(), + start_minimized: default_start_minimized(), } } } @@ -58,18 +42,7 @@ pub async fn load(pool: &SqlitePool) -> Result { None => return Ok(AppConfig::default()), }; - let db_config: DbAppConfig = serde_json::from_str(&row.data)?; - Ok(AppConfig::from(db_config)) -} - - -fn listen_port() -> u16 { - if cfg!(debug_assertions) { - 12_345 - } - else { - 19_923 - } + Ok(serde_json::from_str(&row.data)?) } @@ -89,3 +62,19 @@ pub fn get_or_create_db_path() -> PathBuf { parent.push("creddy.db"); parent } + + +fn default_listen_port() -> u16 { + if cfg!(debug_assertions) { + 12_345 + } + else { + 19_923 + } +} + +fn default_listen_addr() -> Ipv4Addr { Ipv4Addr::LOCALHOST } + +fn default_rehide_ms() -> u64 { 1000 } + +fn default_start_minimized() -> bool { true } diff --git a/src-tauri/src/errors.rs b/src-tauri/src/errors.rs index e883a96..f8e6315 100644 --- a/src-tauri/src/errors.rs +++ b/src-tauri/src/errors.rs @@ -1,4 +1,4 @@ -use thiserror::Error; +use thiserror::Error as ThisError; use aws_sdk_sts::{ types::SdkError as AwsSdkError, @@ -9,9 +9,34 @@ use sqlx::{ migrate::MigrateError, }; +use serde::{Serialize, Serializer, ser::SerializeMap}; + + +pub struct SerializeError { + pub err: E +} + +impl Serialize for SerializeError { + fn serialize(&self, serializer: S) -> Result { + let mut map = serializer.serialize_map(None)?; + map.serialize_entry("msg", &format!("{}", self.err))?; + if let Some(src) = self.err.source() { + let ser_src = SerializeError { err: src }; + map.serialize_entry("source", &ser_src)?; + } + map.end() + } +} + +impl From for SerializeError { + fn from(err: E) -> Self { + SerializeError { err } + } +} + // error during initial setup (primarily loading state from db) -#[derive(Debug, Error)] +#[derive(Debug, ThisError)] pub enum SetupError { #[error("Invalid database record")] InvalidRecord, // e.g. wrong size blob for nonce or salt @@ -25,7 +50,7 @@ pub enum SetupError { // error when attempting to tell a request handler whether to release or deny credentials -#[derive(Debug, Error)] +#[derive(Debug, ThisError)] pub enum SendResponseError { #[error("The specified credentials request was not found")] NotFound, // no request with the given id @@ -35,12 +60,12 @@ pub enum SendResponseError { // errors encountered while handling an HTTP request -#[derive(Debug, Error)] +#[derive(Debug, ThisError)] pub enum RequestError { #[error("Error writing to stream: {0}")] StreamIOError(#[from] std::io::Error), - #[error("Received invalid UTF-8 in request")] - InvalidUtf8, + // #[error("Received invalid UTF-8 in request")] + // InvalidUtf8, // MalformedHttpRequest, #[error("HTTP request too large")] RequestTooLarge, @@ -55,7 +80,7 @@ pub enum RequestError { } -#[derive(Debug, Error)] +#[derive(Debug, ThisError)] pub enum GetCredentialsError { #[error("Credentials are currently locked")] Locked, @@ -64,7 +89,7 @@ pub enum GetCredentialsError { } -#[derive(Debug, Error)] +#[derive(Debug, ThisError)] pub enum GetSessionError { #[error("Request completed successfully but no credentials were returned")] NoCredentials, // SDK returned successfully but credentials are None @@ -73,7 +98,7 @@ pub enum GetSessionError { } -#[derive(Debug, Error)] +#[derive(Debug, ThisError)] pub enum UnlockError { #[error("App is not locked")] NotLocked, @@ -91,7 +116,7 @@ pub enum UnlockError { // Errors encountered while trying to figure out who's on the other end of a request -#[derive(Debug, Error)] +#[derive(Debug, ThisError)] pub enum ClientInfoError { #[error("Found PID for client socket, but no corresponding process")] ProcessNotFound, diff --git a/src-tauri/src/ipc.rs b/src-tauri/src/ipc.rs index fa3d165..0bff596 100644 --- a/src-tauri/src/ipc.rs +++ b/src-tauri/src/ipc.rs @@ -1,6 +1,7 @@ use serde::{Serialize, Deserialize}; use tauri::State; +use crate::errors::*; use crate::config::AppConfig; use crate::clientinfo::Client; use crate::state::{AppState, Session, Credentials}; @@ -35,10 +36,10 @@ pub fn respond(response: RequestResponse, app_state: State<'_, AppState>) -> Res #[tauri::command] -pub async fn unlock(passphrase: String, app_state: State<'_, AppState>) -> Result<(), String> { +pub async fn unlock(passphrase: String, app_state: State<'_, AppState>) -> Result<(), SerializeError> { app_state.decrypt(&passphrase) .await - .map_err(|e| e.to_string()) + .map_err(|e| SerializeError::from(e)) } @@ -58,10 +59,10 @@ pub async fn save_credentials( credentials: Credentials, passphrase: String, app_state: State<'_, AppState> -) -> Result<(), String> { +) -> Result<(), SerializeError> { app_state.save_creds(credentials, &passphrase) .await - .map_err(|e| {eprintln!("{e:?}"); e.to_string()}) + .map_err(|e| SerializeError::from(e)) } diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 3f783c5..f35bd39 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -3,7 +3,7 @@ windows_subsystem = "windows" )] -use tauri::{AppHandle, Manager}; +use tauri::{AppHandle, Manager, async_runtime as rt}; use once_cell::sync::OnceCell; mod config; @@ -14,13 +14,14 @@ mod state; mod server; mod tray; +use crate::errors::*; use state::AppState; pub static APP: OnceCell = OnceCell::new(); fn main() { - let initial_state = match state::AppState::new() { + let initial_state = match rt::block_on(state::AppState::load()) { Ok(state) => state, Err(e) => {eprintln!("{}", e); return;} }; @@ -42,6 +43,12 @@ fn main() { let config = state.config.read().unwrap(); let addr = std::net::SocketAddrV4::new(config.listen_addr, config.listen_port); tauri::async_runtime::spawn(server::serve(addr, app.handle())); + + if !config.start_minimized { + app.get_window("main") + .ok_or(RequestError::NoMainWindow)? + .show()?; + } Ok(()) }) .build(tauri::generate_context!()) diff --git a/src-tauri/src/state.rs b/src-tauri/src/state.rs index 455a427..093a876 100644 --- a/src-tauri/src/state.rs +++ b/src-tauri/src/state.rs @@ -67,16 +67,16 @@ pub struct AppState { } impl AppState { - pub fn new() -> Result { + pub async fn load() -> Result { let conn_opts = SqliteConnectOptions::new() .filename(config::get_or_create_db_path()) .create_if_missing(true); let pool_opts = SqlitePoolOptions::new(); - let pool: SqlitePool = runtime::block_on(pool_opts.connect_with(conn_opts))?; - runtime::block_on(sqlx::migrate!().run(&pool))?; - let creds = runtime::block_on(Self::load_creds(&pool))?; - let conf = runtime::block_on(config::load(&pool))?; + let pool: SqlitePool = pool_opts.connect_with(conn_opts).await?; + sqlx::migrate!().run(&pool).await?; + let creds = Self::load_creds(&pool).await?; + let conf = config::load(&pool).await?; let state = AppState { config: RwLock::new(conf), diff --git a/src/views/EnterCredentials.svelte b/src/views/EnterCredentials.svelte index fe4eec0..f3f2fe0 100644 --- a/src/views/EnterCredentials.svelte +++ b/src/views/EnterCredentials.svelte @@ -24,6 +24,7 @@ } catch (e) { error = e; + window.error = e; } } diff --git a/src/views/Home.svelte b/src/views/Home.svelte index 4137361..dcf560b 100644 --- a/src/views/Home.svelte +++ b/src/views/Home.svelte @@ -19,10 +19,6 @@ onMount(async() => { status = await invoke('get_session_status'); }) - - function blah() { - console.log('blah'); - }

Creddy

diff --git a/src/views/Unlock.svelte b/src/views/Unlock.svelte index 0db38ba..5171d46 100644 --- a/src/views/Unlock.svelte +++ b/src/views/Unlock.svelte @@ -24,6 +24,7 @@ } catch (e) { error = e; + window.error = e; } }