return structured errors from commands (wip)

This commit is contained in:
Joseph Montanaro 2022-12-23 11:34:17 -08:00
parent 2943634248
commit df6b362a31
8 changed files with 82 additions and 62 deletions

View File

@ -9,40 +9,24 @@ use crate::errors::*;
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct AppConfig { pub struct AppConfig {
pub db_path: PathBuf, #[serde(default = "default_listen_addr")]
pub listen_addr: Ipv4Addr, pub listen_addr: Ipv4Addr,
#[serde(default = "default_listen_port")]
pub listen_port: u16, pub listen_port: u16,
#[serde(default = "default_rehide_ms")]
pub rehide_ms: u64, pub rehide_ms: u64,
} #[serde(default = "default_start_minimized")]
pub start_minimized: bool,
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct DbAppConfig {
listen_addr: Option<Ipv4Addr>,
listen_port: Option<u16>,
rehide_ms: Option<u64>,
} }
impl Default for AppConfig { impl Default for AppConfig {
fn default() -> Self { fn default() -> Self {
AppConfig { AppConfig {
db_path: get_or_create_db_path(), listen_addr: default_listen_addr(),
listen_addr: Ipv4Addr::LOCALHOST, listen_port: default_listen_port(),
listen_port: listen_port(), rehide_ms: default_rehide_ms(),
rehide_ms: 1000, start_minimized: default_start_minimized(),
}
}
}
impl From<DbAppConfig> 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),
} }
} }
} }
@ -58,18 +42,7 @@ pub async fn load(pool: &SqlitePool) -> Result<AppConfig, SetupError> {
None => return Ok(AppConfig::default()), None => return Ok(AppConfig::default()),
}; };
let db_config: DbAppConfig = serde_json::from_str(&row.data)?; Ok(serde_json::from_str(&row.data)?)
Ok(AppConfig::from(db_config))
}
fn listen_port() -> u16 {
if cfg!(debug_assertions) {
12_345
}
else {
19_923
}
} }
@ -89,3 +62,19 @@ pub fn get_or_create_db_path() -> PathBuf {
parent.push("creddy.db"); parent.push("creddy.db");
parent 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 }

View File

@ -1,4 +1,4 @@
use thiserror::Error; use thiserror::Error as ThisError;
use aws_sdk_sts::{ use aws_sdk_sts::{
types::SdkError as AwsSdkError, types::SdkError as AwsSdkError,
@ -9,9 +9,34 @@ use sqlx::{
migrate::MigrateError, migrate::MigrateError,
}; };
use serde::{Serialize, Serializer, ser::SerializeMap};
pub struct SerializeError<E> {
pub err: E
}
impl<E: std::error::Error> Serialize for SerializeError<E> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
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<E: std::error::Error> From<E> for SerializeError<E> {
fn from(err: E) -> Self {
SerializeError { err }
}
}
// error during initial setup (primarily loading state from db) // error during initial setup (primarily loading state from db)
#[derive(Debug, Error)] #[derive(Debug, ThisError)]
pub enum SetupError { pub enum SetupError {
#[error("Invalid database record")] #[error("Invalid database record")]
InvalidRecord, // e.g. wrong size blob for nonce or salt 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 // error when attempting to tell a request handler whether to release or deny credentials
#[derive(Debug, Error)] #[derive(Debug, ThisError)]
pub enum SendResponseError { pub enum SendResponseError {
#[error("The specified credentials request was not found")] #[error("The specified credentials request was not found")]
NotFound, // no request with the given id NotFound, // no request with the given id
@ -35,12 +60,12 @@ pub enum SendResponseError {
// errors encountered while handling an HTTP request // errors encountered while handling an HTTP request
#[derive(Debug, Error)] #[derive(Debug, ThisError)]
pub enum RequestError { pub enum RequestError {
#[error("Error writing to stream: {0}")] #[error("Error writing to stream: {0}")]
StreamIOError(#[from] std::io::Error), StreamIOError(#[from] std::io::Error),
#[error("Received invalid UTF-8 in request")] // #[error("Received invalid UTF-8 in request")]
InvalidUtf8, // InvalidUtf8,
// MalformedHttpRequest, // MalformedHttpRequest,
#[error("HTTP request too large")] #[error("HTTP request too large")]
RequestTooLarge, RequestTooLarge,
@ -55,7 +80,7 @@ pub enum RequestError {
} }
#[derive(Debug, Error)] #[derive(Debug, ThisError)]
pub enum GetCredentialsError { pub enum GetCredentialsError {
#[error("Credentials are currently locked")] #[error("Credentials are currently locked")]
Locked, Locked,
@ -64,7 +89,7 @@ pub enum GetCredentialsError {
} }
#[derive(Debug, Error)] #[derive(Debug, ThisError)]
pub enum GetSessionError { pub enum GetSessionError {
#[error("Request completed successfully but no credentials were returned")] #[error("Request completed successfully but no credentials were returned")]
NoCredentials, // SDK returned successfully but credentials are None NoCredentials, // SDK returned successfully but credentials are None
@ -73,7 +98,7 @@ pub enum GetSessionError {
} }
#[derive(Debug, Error)] #[derive(Debug, ThisError)]
pub enum UnlockError { pub enum UnlockError {
#[error("App is not locked")] #[error("App is not locked")]
NotLocked, NotLocked,
@ -91,7 +116,7 @@ pub enum UnlockError {
// Errors encountered while trying to figure out who's on the other end of a request // 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 { pub enum ClientInfoError {
#[error("Found PID for client socket, but no corresponding process")] #[error("Found PID for client socket, but no corresponding process")]
ProcessNotFound, ProcessNotFound,

View File

@ -1,6 +1,7 @@
use serde::{Serialize, Deserialize}; use serde::{Serialize, Deserialize};
use tauri::State; use tauri::State;
use crate::errors::*;
use crate::config::AppConfig; use crate::config::AppConfig;
use crate::clientinfo::Client; use crate::clientinfo::Client;
use crate::state::{AppState, Session, Credentials}; use crate::state::{AppState, Session, Credentials};
@ -35,10 +36,10 @@ pub fn respond(response: RequestResponse, app_state: State<'_, AppState>) -> Res
#[tauri::command] #[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<UnlockError>> {
app_state.decrypt(&passphrase) app_state.decrypt(&passphrase)
.await .await
.map_err(|e| e.to_string()) .map_err(|e| SerializeError::from(e))
} }
@ -58,10 +59,10 @@ pub async fn save_credentials(
credentials: Credentials, credentials: Credentials,
passphrase: String, passphrase: String,
app_state: State<'_, AppState> app_state: State<'_, AppState>
) -> Result<(), String> { ) -> Result<(), SerializeError<UnlockError>> {
app_state.save_creds(credentials, &passphrase) app_state.save_creds(credentials, &passphrase)
.await .await
.map_err(|e| {eprintln!("{e:?}"); e.to_string()}) .map_err(|e| SerializeError::from(e))
} }

View File

@ -3,7 +3,7 @@
windows_subsystem = "windows" windows_subsystem = "windows"
)] )]
use tauri::{AppHandle, Manager}; use tauri::{AppHandle, Manager, async_runtime as rt};
use once_cell::sync::OnceCell; use once_cell::sync::OnceCell;
mod config; mod config;
@ -14,13 +14,14 @@ mod state;
mod server; mod server;
mod tray; mod tray;
use crate::errors::*;
use state::AppState; use state::AppState;
pub static APP: OnceCell<AppHandle> = OnceCell::new(); pub static APP: OnceCell<AppHandle> = OnceCell::new();
fn main() { fn main() {
let initial_state = match state::AppState::new() { let initial_state = match rt::block_on(state::AppState::load()) {
Ok(state) => state, Ok(state) => state,
Err(e) => {eprintln!("{}", e); return;} Err(e) => {eprintln!("{}", e); return;}
}; };
@ -42,6 +43,12 @@ fn main() {
let config = state.config.read().unwrap(); let config = state.config.read().unwrap();
let addr = std::net::SocketAddrV4::new(config.listen_addr, config.listen_port); let addr = std::net::SocketAddrV4::new(config.listen_addr, config.listen_port);
tauri::async_runtime::spawn(server::serve(addr, app.handle())); tauri::async_runtime::spawn(server::serve(addr, app.handle()));
if !config.start_minimized {
app.get_window("main")
.ok_or(RequestError::NoMainWindow)?
.show()?;
}
Ok(()) Ok(())
}) })
.build(tauri::generate_context!()) .build(tauri::generate_context!())

View File

@ -67,16 +67,16 @@ pub struct AppState {
} }
impl AppState { impl AppState {
pub fn new() -> Result<Self, SetupError> { pub async fn load() -> Result<Self, SetupError> {
let conn_opts = SqliteConnectOptions::new() let conn_opts = SqliteConnectOptions::new()
.filename(config::get_or_create_db_path()) .filename(config::get_or_create_db_path())
.create_if_missing(true); .create_if_missing(true);
let pool_opts = SqlitePoolOptions::new(); let pool_opts = SqlitePoolOptions::new();
let pool: SqlitePool = runtime::block_on(pool_opts.connect_with(conn_opts))?; let pool: SqlitePool = pool_opts.connect_with(conn_opts).await?;
runtime::block_on(sqlx::migrate!().run(&pool))?; sqlx::migrate!().run(&pool).await?;
let creds = runtime::block_on(Self::load_creds(&pool))?; let creds = Self::load_creds(&pool).await?;
let conf = runtime::block_on(config::load(&pool))?; let conf = config::load(&pool).await?;
let state = AppState { let state = AppState {
config: RwLock::new(conf), config: RwLock::new(conf),

View File

@ -24,6 +24,7 @@
} }
catch (e) { catch (e) {
error = e; error = e;
window.error = e;
} }
} }

View File

@ -19,10 +19,6 @@
onMount(async() => { onMount(async() => {
status = await invoke('get_session_status'); status = await invoke('get_session_status');
}) })
function blah() {
console.log('blah');
}
</script> </script>
<h1 class="text-4xl text-gray-300">Creddy</h1> <h1 class="text-4xl text-gray-300">Creddy</h1>

View File

@ -24,6 +24,7 @@
} }
catch (e) { catch (e) {
error = e; error = e;
window.error = e;
} }
} }
</script> </script>