diff --git a/crates/desktop/tauri-artcraft/src/threads/main_window_thread/main_window_thread.rs b/crates/desktop/tauri-artcraft/src/threads/main_window_thread/main_window_thread.rs index ac6a9e0b76..c66b7cccaa 100644 --- a/crates/desktop/tauri-artcraft/src/threads/main_window_thread/main_window_thread.rs +++ b/crates/desktop/tauri-artcraft/src/threads/main_window_thread/main_window_thread.rs @@ -24,6 +24,7 @@ pub async fn main_window_thread( for (window_name, window) in app.windows() { if window_name == MAIN_WINDOW_NAME { let result = handle_main_window( + &app, &window, &app_data_root, &storyteller_creds_manager, @@ -40,6 +41,7 @@ pub async fn main_window_thread( } pub async fn handle_main_window( + app: &AppHandle, window: &Window, app_data_root: &AppDataRoot, storyteller_creds_manager: &StorytellerCredentialManager, @@ -48,7 +50,7 @@ pub async fn handle_main_window( ) -> AnyhowResult<()> { loop { log_errors(persist_window_resize_task(window, app_data_root, window_size_slot).await); - //log_errors(persist_storyteller_cookies_task(window, app_data_root, storyteller_creds_manager, app_startup_time).await); + log_errors(persist_storyteller_cookies_task(app, storyteller_creds_manager, app_startup_time).await); tokio::time::sleep(std::time::Duration::from_millis(1_000)).await; } } diff --git a/crates/desktop/tauri-artcraft/src/threads/main_window_thread/persist_storyteller_cookies_task.rs b/crates/desktop/tauri-artcraft/src/threads/main_window_thread/persist_storyteller_cookies_task.rs index 94ab66c450..066a1f294c 100644 --- a/crates/desktop/tauri-artcraft/src/threads/main_window_thread/persist_storyteller_cookies_task.rs +++ b/crates/desktop/tauri-artcraft/src/threads/main_window_thread/persist_storyteller_cookies_task.rs @@ -1,18 +1,21 @@ +use crate::state::app_startup_time::AppStartupTime; use crate::state::data_dir::app_data_root::AppDataRoot; use crate::state::storyteller::storyteller_credential_manager::StorytellerCredentialManager; use crate::threads::sora_session_login_thread::LOGIN_WINDOW_NAME; use anyhow::anyhow; +use chrono::TimeDelta; use errors::AnyhowResult; use log::{debug, error, info}; use once_cell::sync::Lazy; use reqwest::Url; use std::fs; -use chrono::TimeDelta; +use std::sync::LockResult; use storyteller_client::credentials::storyteller_avt_cookie::StorytellerAvtCookie; use storyteller_client::credentials::storyteller_credential_set::StorytellerCredentialSet; use storyteller_client::credentials::storyteller_session_cookie::StorytellerSessionCookie; use tauri::{AppHandle, Manager, Webview, Window}; -use crate::state::app_startup_time::AppStartupTime; +use tauri_plugin_http::reqwest_cookie_store::CookieStore; +use tauri_plugin_http::Http; const MAIN_WEBVIEW_NAME: &str = "main"; @@ -29,82 +32,74 @@ const SESSION_COOKIE_NAME : &str = "session"; const RACE_CONDITION_WAIT_TIME: TimeDelta = TimeDelta::milliseconds(5000); pub async fn persist_storyteller_cookies_task( - window: &Window, - app_data_root: &AppDataRoot, + app: &AppHandle, storyteller_credential_manager: &StorytellerCredentialManager, app_startup_time: &AppStartupTime, ) -> AnyhowResult<()> { - if app_startup_time.time_delta_since() < RACE_CONDITION_WAIT_TIME { - // NB: There's an issue when the "main window thread" inquires about the - // webview cookies shortly after app startup. When it attempts to dump the - // cookies within a few milliseconds of app launch, it deadlocks and the - // app never launches correctly. The entire webview goes blank and the app - // freezes. This hack seems to fix the problem. - return Ok(()) - } + //if app_startup_time.time_delta_since() < RACE_CONDITION_WAIT_TIME { + // // NB: There's an issue when the "main window thread" inquires about the + // // webview cookies shortly after app startup. When it attempts to dump the + // // cookies within a few milliseconds of app launch, it deadlocks and the + // // app never launches correctly. The entire webview goes blank and the app + // // freezes. This hack seems to fix the problem. + // return Ok(()) + //} - for webview in window.webviews() { - let label = webview.label(); - if label == MAIN_WEBVIEW_NAME { - persist_webview_cookies(&webview, app_data_root, storyteller_credential_manager).await?; - break; + let maybe_http = app.try_state::(); + + let http = match maybe_http { + None => { + error!("No HTTP plugin found"); + return Err(anyhow!("No HTTP plugin found")); + } + Some(http) => http, + }; + + match http.cookies_jar.store.lock() { + Err(err) => { + error!("Failed to lock cookie jar: {:?}", err); + return Err(anyhow!("Failed to lock cookie jar")); + } + Ok(cookie_jar) => { + sync_tauri_credentials(&cookie_jar, storyteller_credential_manager)?; } } Ok(()) } -async fn persist_webview_cookies( - webview: &Webview, - app_data_root: &AppDataRoot, +fn sync_tauri_credentials( + cookie_store: &CookieStore, storyteller_credential_manager: &StorytellerCredentialManager, ) -> AnyhowResult<()> { - let current_webview_credentials = get_storyteller_cookies(webview).await?; + let current_http_plugin_credentials = get_credentials_from_cookie_store(cookie_store)?; - if current_webview_credentials.is_empty() { - // TODO: handle logout / cookie deletion - return Ok(()); - } - let mut replace_credentials = true; let maybe_old_credentials = storyteller_credential_manager.get_credentials()?; if let Some(old_credentials) = maybe_old_credentials { - if old_credentials.equals(¤t_webview_credentials) { + if old_credentials.equals(¤t_http_plugin_credentials) { replace_credentials = false; } } if replace_credentials { - info!("Writing ArtCraft credentials to disk..."); - storyteller_credential_manager.set_credentials(¤t_webview_credentials)?; - storyteller_credential_manager.persist_all_to_disk()?; + info!("Syncing ArtCraft credentials ..."); + storyteller_credential_manager.set_credentials(¤t_http_plugin_credentials)?; + // NB: tauri-plugin-http stores the credentials on disk, so we can defer to that for now. + //storyteller_credential_manager.persist_all_to_disk()?; } Ok(()) } -pub async fn get_storyteller_cookies(webview: &Webview) -> AnyhowResult { - debug!("Getting storyteller cookies..."); - - //// FIXME: THIS IS A TOTAL HACK. / DO NOT REMOVE. - //// NB: If we don't call a method on the webview, the very next call to get cookies will deadlock. - //if let Ok(url) = webview.url() { - // debug!("For url: {:?}", url); - //} - - let cookies = webview.cookies_for_url(STORYTELLER_ROOT_COOKIE_URL.clone())?; - - info!("Got storyteller cookies: {:?}", cookies); - info!("Got storyteller cookies: {:?}", cookies.len()); - info!("Got storyteller location: {:?}", webview.url()); - +pub fn get_credentials_from_cookie_store(cookie_jar: &CookieStore) -> AnyhowResult { let mut avt_cookie = None; let mut session_cookie = None; - for cookie in cookies.into_iter() { + for cookie in cookie_jar.iter_unexpired() { if let Some(avt) = StorytellerAvtCookie::maybe_from_cookie(&cookie) { avt_cookie = Some(avt); } else if let Some(session) = StorytellerSessionCookie::maybe_from_cookie(&cookie) { diff --git a/crates/vendor/tauri-plugin-http/src/lib.rs b/crates/vendor/tauri-plugin-http/src/lib.rs index 5acc2b474a..07731d67c9 100644 --- a/crates/vendor/tauri-plugin-http/src/lib.rs +++ b/crates/vendor/tauri-plugin-http/src/lib.rs @@ -12,18 +12,20 @@ use tauri::{ pub use error::{Error, Result}; -mod commands; -mod error; +// NB(bt): We're making everything public so we can access it. +pub mod commands; +pub mod error; #[cfg(feature = "cookies")] -mod reqwest_cookie_store; -mod scope; +pub mod reqwest_cookie_store; +pub mod scope; #[cfg(feature = "cookies")] const COOKIES_FILENAME: &str = ".cookies"; -pub(crate) struct Http { +// NB(bt): We've vendored in this library so we can poke at the cookie data store. +pub struct Http { #[cfg(feature = "cookies")] - cookies_jar: std::sync::Arc, + pub cookies_jar: std::sync::Arc, } pub fn init() -> TauriPlugin { diff --git a/crates/vendor/tauri-plugin-http/src/reqwest_cookie_store.rs b/crates/vendor/tauri-plugin-http/src/reqwest_cookie_store.rs index 6a7c01861c..be4215ca1a 100644 --- a/crates/vendor/tauri-plugin-http/src/reqwest_cookie_store.rs +++ b/crates/vendor/tauri-plugin-http/src/reqwest_cookie_store.rs @@ -9,7 +9,8 @@ use std::{ sync::{mpsc::Receiver, Mutex}, }; -use cookie_store::{CookieStore, RawCookie, RawCookieParseError}; +// NB(bt): Re-export these for reuse +pub use cookie_store::{CookieStore, RawCookie, RawCookieParseError}; use reqwest::header::HeaderValue; fn set_cookies( @@ -46,8 +47,9 @@ fn cookies(cookie_store: &CookieStore, url: &url::Url) -> Option { #[derive(Debug)] pub struct CookieStoreMutex { pub path: PathBuf, - store: Mutex, - save_task: Mutex>, + // NB(bt): We've made both of these public so we can access them. + pub store: Mutex, + pub save_task: Mutex>, } impl CookieStoreMutex {