diff --git a/pages/error.vue b/pages/error.vue new file mode 100644 index 0000000..d50e70c --- /dev/null +++ b/pages/error.vue @@ -0,0 +1,66 @@ + + + diff --git a/plugins/global-error-handler.ts b/plugins/global-error-handler.ts new file mode 100644 index 0000000..1ad9b59 --- /dev/null +++ b/plugins/global-error-handler.ts @@ -0,0 +1,11 @@ +export default defineNuxtPlugin((nuxtApp) => { + nuxtApp.vueApp.config.errorHandler = (error, instance, info) => { + // handle error, e.g. report to a service + }; + + // Also possible + nuxtApp.hook("vue:error", (error, instance, info) => { + const router = useRouter(); + router.replace("/error"); + }); +}); diff --git a/src-tauri/src/auth.rs b/src-tauri/src/auth.rs index a8bf764..7d6f4aa 100644 --- a/src-tauri/src/auth.rs +++ b/src-tauri/src/auth.rs @@ -13,6 +13,7 @@ use url::{ParseError, Url}; use crate::{ db::{DatabaseAuth, DatabaseImpls}, + remote::RemoteAccessError, AppState, AppStatus, User, DB, }; @@ -68,43 +69,6 @@ pub fn generate_authorization_header() -> String { format!("Nonce {} {} {}", certs.client_id, nonce, signature) } -#[derive(Debug)] -pub enum RemoteAccessError { - FetchError(reqwest::Error), - ParsingError(ParseError), - GenericErrror(String), -} - -impl Display for RemoteAccessError { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - match self { - RemoteAccessError::FetchError(error) => write!(f, "{}", error), - RemoteAccessError::GenericErrror(error) => write!(f, "{}", error), - RemoteAccessError::ParsingError(parse_error) => { - write!(f, "{}", parse_error) - } - } - } -} - -impl From for RemoteAccessError { - fn from(err: reqwest::Error) -> Self { - RemoteAccessError::FetchError(err) - } -} -impl From for RemoteAccessError { - fn from(err: String) -> Self { - RemoteAccessError::GenericErrror(err) - } -} -impl From for RemoteAccessError { - fn from(err: ParseError) -> Self { - RemoteAccessError::ParsingError(err) - } -} - -impl std::error::Error for RemoteAccessError {} - pub fn fetch_user() -> Result { let base_url = DB.fetch_base_url(); @@ -118,7 +82,7 @@ pub fn fetch_user() -> Result { .send()?; if response.status() != 200 { - return Err(format!("Failed to fetch user: {}", response.status()).into()); + return Err(response.status().as_u16().into()); } let user = response.json::()?; diff --git a/src-tauri/src/library.rs b/src-tauri/src/library.rs index 1c81870..80e4134 100644 --- a/src-tauri/src/library.rs +++ b/src-tauri/src/library.rs @@ -4,9 +4,10 @@ use serde::{Deserialize, Serialize}; use serde_json::json; use tauri::{AppHandle, Manager}; -use crate::{auth::generate_authorization_header, AppState, DB}; -use crate::db::DatabaseImpls; use crate::db::DatabaseGameStatus; +use crate::db::DatabaseImpls; +use crate::remote::RemoteAccessError; +use crate::{auth::generate_authorization_header, AppState, DB}; #[derive(serde::Serialize)] struct FetchGameStruct { @@ -29,10 +30,9 @@ pub struct Game { m_image_library: Vec, } -#[tauri::command] -pub fn fetch_library(app: AppHandle) -> Result { +fn fetch_library_logic(app: AppHandle) -> Result { let base_url = DB.fetch_base_url(); - let library_url = base_url.join("/api/v1/client/user/library").unwrap(); + let library_url = base_url.join("/api/v1/client/user/library")?; let header = generate_authorization_header(); @@ -40,18 +40,13 @@ pub fn fetch_library(app: AppHandle) -> Result { let response = client .get(library_url.to_string()) .header("Authorization", header) - .send() - .unwrap(); + .send()?; if response.status() != 200 { - return Err(format!( - "Library fetch request failed with {}", - response.status() - )); + return Err(response.status().as_u16().into()); } - // Keep as string - let games = response.json::>().unwrap(); + let games = response.json::>()?; let state = app.state::>(); let mut handle = state.lock().unwrap(); @@ -74,20 +69,48 @@ pub fn fetch_library(app: AppHandle) -> Result { } #[tauri::command] -pub fn fetch_game(id: String, app: tauri::AppHandle) -> Result { +pub fn fetch_library(app: AppHandle) -> Result { + let result = fetch_library_logic(app); + + if result.is_err() { + return Err(result.err().unwrap().to_string()); + } + + return Ok(result.unwrap()); +} + +fn fetch_game_logic(id: String, app: tauri::AppHandle) -> Result { let state = app.state::>(); let handle = state.lock().unwrap(); + let game = handle.games.get(&id); if let Some(game) = game { let db_handle = DB.borrow_data().unwrap(); let data = FetchGameStruct { game: game.clone(), - status: db_handle.games.games_statuses.get(&game.id).unwrap().clone(), + status: db_handle + .games + .games_statuses + .get(&game.id) + .unwrap() + .clone(), }; return Ok(json!(data).to_string()); } + // TODO request games that aren't found from remote server - Err("".to_string()) + Err("".to_string().into()) +} + +#[tauri::command] +pub fn fetch_game(id: String, app: tauri::AppHandle) -> Result { + let result = fetch_game_logic(id, app); + + if result.is_err() { + return Err(result.err().unwrap().to_string()); + } + + Ok(result.unwrap()) } diff --git a/src-tauri/src/remote.rs b/src-tauri/src/remote.rs index fdf6a73..5691ac4 100644 --- a/src-tauri/src/remote.rs +++ b/src-tauri/src/remote.rs @@ -1,48 +1,80 @@ -use std::sync::Mutex; +use std::{ + fmt::{write, Display, Formatter}, + sync::Mutex, +}; use log::{info, warn}; use serde::Deserialize; -use url::Url; +use url::{ParseError, Url}; use crate::{AppState, AppStatus, DB}; -macro_rules! unwrap_or_return { - ( $e:expr ) => { - match $e { - Ok(x) => x, - Err(e) => { - return Err(format!( - "Invalid URL or Drop is inaccessible ({})", - e.to_string() - )) - } - } - }; +#[derive(Debug)] +pub enum RemoteAccessError { + FetchError(reqwest::Error), + ParsingError(ParseError), + InvalidCodeError(u16), + GenericErrror(String), } +impl Display for RemoteAccessError { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + RemoteAccessError::FetchError(error) => write!(f, "{}", error), + RemoteAccessError::GenericErrror(error) => write!(f, "{}", error), + RemoteAccessError::ParsingError(parse_error) => { + write!(f, "{}", parse_error) + } + RemoteAccessError::InvalidCodeError(error) => write!(f, "HTTP {}", error), + } + } +} + +impl From for RemoteAccessError { + fn from(err: reqwest::Error) -> Self { + RemoteAccessError::FetchError(err) + } +} +impl From for RemoteAccessError { + fn from(err: String) -> Self { + RemoteAccessError::GenericErrror(err) + } +} +impl From for RemoteAccessError { + fn from(err: ParseError) -> Self { + RemoteAccessError::ParsingError(err) + } +} +impl From for RemoteAccessError { + fn from(err: u16) -> Self { + RemoteAccessError::InvalidCodeError(err) + } +} + +impl std::error::Error for RemoteAccessError {} + #[derive(Deserialize)] -#[serde(rename_all="camelCase")] +#[serde(rename_all = "camelCase")] struct DropHealthcheck { app_name: String, } -#[tauri::command] -pub async fn use_remote<'a>( +async fn use_remote_logic<'a>( url: String, state: tauri::State<'_, Mutex>, -) -> Result<(), String> { +) -> Result<(), RemoteAccessError> { info!("connecting to url {}", url); - let base_url = unwrap_or_return!(Url::parse(&url)); + let base_url = Url::parse(&url)?; // Test Drop url - let test_endpoint = base_url.join("/api/v1").unwrap(); - let response = unwrap_or_return!(reqwest::get(test_endpoint.to_string()).await); + let test_endpoint = base_url.join("/api/v1")?; + let response = reqwest::get(test_endpoint.to_string()).await?; - let result = response.json::().await.unwrap(); + let result = response.json::().await?; if result.app_name != "Drop" { warn!("user entered drop endpoint that connected, but wasn't identified as Drop"); - return Err("Not a valid Drop endpoint".to_string()); + return Err("Not a valid Drop endpoint".to_string().into()); } let mut app_state = state.lock().unwrap(); @@ -58,6 +90,20 @@ pub async fn use_remote<'a>( Ok(()) } +#[tauri::command] +pub async fn use_remote<'a>( + url: String, + state: tauri::State<'_, Mutex>, +) -> Result<(), String> { + let result = use_remote_logic(url, state).await; + + if result.is_err() { + return Err(result.err().unwrap().to_string()); + } + + Ok(()) +} + #[tauri::command] pub fn gen_drop_url(path: String) -> Result { let base_url = {