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)]
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<Ipv4Addr>,
listen_port: Option<u16>,
rehide_ms: Option<u64>,
#[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<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),
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<AppConfig, SetupError> {
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 }

View File

@ -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<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)
#[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,

View File

@ -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<UnlockError>> {
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<UnlockError>> {
app_state.save_creds(credentials, &passphrase)
.await
.map_err(|e| {eprintln!("{e:?}"); e.to_string()})
.map_err(|e| SerializeError::from(e))
}

View File

@ -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<AppHandle> = 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!())

View File

@ -67,16 +67,16 @@ pub struct AppState {
}
impl AppState {
pub fn new() -> Result<Self, SetupError> {
pub async fn load() -> Result<Self, SetupError> {
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),

View File

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

View File

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

View File

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