sync creds now

This commit is contained in:
Brandon Thomas
2025-05-01 14:55:48 -04:00
parent a60e20c0b4
commit 65c8ddfa91
4 changed files with 57 additions and 56 deletions
@@ -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;
}
}
@@ -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::<Http>();
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(&current_webview_credentials) {
if old_credentials.equals(&current_http_plugin_credentials) {
replace_credentials = false;
}
}
if replace_credentials {
info!("Writing ArtCraft credentials to disk...");
storyteller_credential_manager.set_credentials(&current_webview_credentials)?;
storyteller_credential_manager.persist_all_to_disk()?;
info!("Syncing ArtCraft credentials ...");
storyteller_credential_manager.set_credentials(&current_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<StorytellerCredentialSet> {
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<StorytellerCredentialSet> {
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) {
+8 -6
View File
@@ -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<crate::reqwest_cookie_store::CookieStoreMutex>,
pub cookies_jar: std::sync::Arc<crate::reqwest_cookie_store::CookieStoreMutex>,
}
pub fn init<R: Runtime>() -> TauriPlugin<R> {
@@ -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<HeaderValue> {
#[derive(Debug)]
pub struct CookieStoreMutex {
pub path: PathBuf,
store: Mutex<CookieStore>,
save_task: Mutex<Option<CancellableTask>>,
// NB(bt): We've made both of these public so we can access them.
pub store: Mutex<CookieStore>,
pub save_task: Mutex<Option<CancellableTask>>,
}
impl CookieStoreMutex {