mod oauth; use oauth::OAuthController; use tauri_plugin_deep_link::DeepLinkExt; use tauri_plugin_opener::OpenerExt; use tauri::{Manager, State, AppHandle}; use url::Host; use uuid::Uuid; pub const OAUTH_CLIENT_NAME: &'static str = "foxfleet_test"; struct AppState { oauth_controller: OAuthController } #[cfg_attr(mobile, tauri::mobile_entry_point)] pub fn run() { tauri::Builder::default() .plugin(tauri_plugin_single_instance::init(|app, argv, cwd| { println!("{}, {argv:?}, {cwd}", app.package_info().name); })) .plugin(tauri_plugin_deep_link::init()) .plugin(tauri_plugin_opener::init()) .setup(|app| { app.manage(AppState { oauth_controller: OAuthController::new(tauri::async_runtime::handle()) }); #[cfg(any(target_os = "linux", all(debug_assertions, windows)))] app.deep_link().register_all()?; let app_handle = app.handle().clone(); app.deep_link().on_open_url(move |event| { if let Some(oauth_callback) = event.urls().iter().find(|url| { if let Some(Host::Domain(domain)) = url.host() { domain == "oauth-response" } else { false } }) { let mut query = oauth_callback.query_pairs(); let query = ( query.find(|(key, _)| key == "code").map(|(_, value)| value ), query.find(|(key, _)| key == "state").map(|(_, value)| value ) ); let query = match query { (Some(code), Some(state)) => Some((code, state)), _ => None, }; if let Some((auth_code, state)) = query { if let Ok(state) = Uuid::try_parse(&state) { let state = state.clone(); let auth_code = auth_code.to_string().clone(); let oauth_controller = &app_handle.state::().oauth_controller; oauth_controller.resolve_code(state, auth_code); } else { println!("Invalid UUID format: {state}"); return } } else { println!("Missing either state or code in oauth callback"); return } } }); Ok(()) }) .invoke_handler(tauri::generate_handler![start_account_auth, get_self]) .run(tauri::generate_context!()) .expect("Error starting") } #[tauri::command] async fn start_account_auth(app_handle: AppHandle, state: State<'_, AppState>, instance_domain: &str) -> Result, ()> { let add_result = state.oauth_controller.add_server(instance_domain).await; let state_nonce = match add_result { Ok(result) => { let opener = app_handle.opener(); if let Err(_) = opener.open_url(result.auth_url, None::<&str>) { println!("Could not open authentication page"); return Err(()) } result.auth_state } Err(err) => { println!("Error adding server: {err}"); return Err(()) } }; let signin_result = state.oauth_controller.finish_signin(state_nonce).await; match signin_result { Ok((server_domain, username)) => { println!("Signed in successfully"); Ok(vec!(server_domain, username)) } Err(err) => { println!("Error completing signin: {err}"); Err(()) } } } #[tauri::command] async fn get_self(state: State<'_, AppState>, server_domain: String, username: String) -> Result { let client = reqwest::Client::builder().user_agent("Foxfleet v0.0.1").build().expect("Could not construct client"); let api_key = state.oauth_controller.get_api_token(server_domain, username).await; match api_key { Err(err) => { println!("Error getting API token: {err}"); return Err(err) } Ok(api_key) => { if let Ok(result) = client.get("https://social.tempest.dev/api/v1/accounts/verify_credentials") .bearer_auth(api_key) .send().await { if let Ok(result) = result.text().await { return Ok(result) } else { return Err("Error decoding response".to_string()); } } else { return Err("Error fetching account".to_string()); } } } }