diff options
-rw-r--r-- | Cargo.lock | 368 | ||||
-rw-r--r-- | app/Cargo.toml | 9 | ||||
-rw-r--r-- | app/Tauri.toml | 5 | ||||
-rw-r--r-- | app/src/lib.rs | 186 | ||||
-rw-r--r-- | app/src/state.rs | 46 | ||||
-rw-r--r-- | ui/src/root.tsx | 26 |
6 files changed, 614 insertions, 26 deletions
diff --git a/Cargo.lock b/Cargo.lock index 4d667e3..508cba3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -504,9 +504,9 @@ dependencies = [ "bitflags 2.8.0", "block", "cocoa-foundation", - "core-foundation", + "core-foundation 0.10.0", "core-graphics", - "foreign-types", + "foreign-types 0.5.0", "libc", "objc", ] @@ -519,7 +519,7 @@ checksum = "e14045fb83be07b5acf1c0884b2180461635b433455fa35d1cd6f17f1450679d" dependencies = [ "bitflags 2.8.0", "block", - "core-foundation", + "core-foundation 0.10.0", "core-graphics-types", "libc", "objc", @@ -545,6 +545,26 @@ dependencies = [ ] [[package]] +name = "const-random" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359" +dependencies = [ + "const-random-macro", +] + +[[package]] +name = "const-random-macro" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" +dependencies = [ + "getrandom 0.2.15", + "once_cell", + "tiny-keccak", +] + +[[package]] name = "convert_case" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -562,6 +582,16 @@ dependencies = [ [[package]] name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b55271e5c8c478ad3f38ad24ef34923091e0548492a266d19b3c0b4d82574c63" @@ -583,9 +613,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa95a34622365fa5bbf40b20b75dba8dfa8c94c734aea8ac9a5ca38af14316f1" dependencies = [ "bitflags 2.8.0", - "core-foundation", + "core-foundation 0.10.0", "core-graphics-types", - "foreign-types", + "foreign-types 0.5.0", "libc", ] @@ -596,7 +626,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" dependencies = [ "bitflags 2.8.0", - "core-foundation", + "core-foundation 0.10.0", "libc", ] @@ -634,6 +664,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] +name = "crunchy" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929" + +[[package]] name = "crypto-common" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -831,6 +867,15 @@ dependencies = [ ] [[package]] +name = "dlv-list" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "442039f5147480ba31067cb00ada1adae6892028e40e45fc5de7b7df6dcc1b5f" +dependencies = [ + "const-random", +] + +[[package]] name = "dpi" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -887,6 +932,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ef6b89e5b37196644d8796de5268852ff179b44e96276cf4290264843743bb7" [[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + +[[package]] name = "endi" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1003,12 +1057,21 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared 0.1.1", +] + +[[package]] +name = "foreign-types" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" dependencies = [ "foreign-types-macros", - "foreign-types-shared", + "foreign-types-shared 0.3.1", ] [[package]] @@ -1024,6 +1087,12 @@ dependencies = [ [[package]] name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "foreign-types-shared" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" @@ -1041,11 +1110,16 @@ dependencies = [ name = "foxfleet" version = "0.1.0" dependencies = [ + "reqwest", "serde", "serde_json", "tauri", "tauri-build", + "tauri-plugin-deep-link", "tauri-plugin-opener", + "tauri-plugin-single-instance", + "tokio", + "url", ] [[package]] @@ -1456,6 +1530,25 @@ dependencies = [ ] [[package]] +name = "h2" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccae279728d634d083c00f6099cb58f01cc99c145b84b8be2f6c74618d79922e" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http", + "indexmap 2.7.1", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] name = "hashbrown" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1463,6 +1556,12 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + +[[package]] +name = "hashbrown" version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" @@ -1554,6 +1653,7 @@ dependencies = [ "bytes", "futures-channel", "futures-util", + "h2", "http", "http-body", "httparse", @@ -1583,6 +1683,22 @@ dependencies = [ ] [[package]] +name = "hyper-tls" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +dependencies = [ + "bytes", + "http-body-util", + "hyper", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", +] + +[[package]] name = "hyper-util" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2130,6 +2246,23 @@ dependencies = [ ] [[package]] +name = "native-tls" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dab59f8e050d5df8e4dd87d9206fb6f65a483e20ac9fda365ade4fab353196c" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] name = "ndk" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2475,12 +2608,66 @@ dependencies = [ ] [[package]] +name = "openssl" +version = "0.10.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61cfb4e166a8bb8c9b55c500bc2308550148ece889be90f609377e58140f42c6" +dependencies = [ + "bitflags 2.8.0", + "cfg-if", + "foreign-types 0.3.2", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.98", +] + +[[package]] +name = "openssl-probe" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" + +[[package]] +name = "openssl-sys" +version = "0.9.105" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b22d5b84be05a8d6947c7cb71f7c849aa0f112acd4bf51c2a7c1c988ac0a9dc" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] name = "option-ext" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" [[package]] +name = "ordered-multimap" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49203cdcae0030493bad186b28da2fa25645fa276a51b6fec8010d281e02ef79" +dependencies = [ + "dlv-list", + "hashbrown 0.14.5", +] + +[[package]] name = "ordered-stream" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3073,18 +3260,22 @@ checksum = "43e734407157c3c2034e0258f5e4473ddb361b1e85f95a66690d67264d7cd1da" dependencies = [ "base64 0.22.1", "bytes", + "encoding_rs", "futures-core", "futures-util", + "h2", "http", "http-body", "http-body-util", "hyper", "hyper-rustls", + "hyper-tls", "hyper-util", "ipnet", "js-sys", "log", "mime", + "native-tls", "once_cell", "percent-encoding", "pin-project-lite", @@ -3096,7 +3287,9 @@ dependencies = [ "serde_json", "serde_urlencoded", "sync_wrapper", + "system-configuration", "tokio", + "tokio-native-tls", "tokio-rustls", "tokio-util", "tower", @@ -3107,7 +3300,7 @@ dependencies = [ "wasm-streams", "web-sys", "webpki-roots", - "windows-registry", + "windows-registry 0.2.0", ] [[package]] @@ -3125,6 +3318,17 @@ dependencies = [ ] [[package]] +name = "rust-ini" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e310ef0e1b6eeb79169a1171daf9abcb87a2e17c03bee2c4bb100b55c75409f" +dependencies = [ + "cfg-if", + "ordered-multimap", + "trim-in-place", +] + +[[package]] name = "rustc-demangle" version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3223,6 +3427,15 @@ dependencies = [ ] [[package]] +name = "schannel" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] name = "schemars" version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3256,6 +3469,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] +name = "security-framework" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +dependencies = [ + "bitflags 2.8.0", + "core-foundation 0.9.4", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] name = "selectors" version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3510,7 +3746,7 @@ dependencies = [ "bytemuck", "cfg_aliases", "core-graphics", - "foreign-types", + "foreign-types 0.5.0", "js-sys", "log", "objc2", @@ -3652,6 +3888,27 @@ dependencies = [ ] [[package]] +name = "system-configuration" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" +dependencies = [ + "bitflags 2.8.0", + "core-foundation 0.9.4", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] name = "system-deps" version = "6.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3672,7 +3929,7 @@ checksum = "3731d04d4ac210cd5f344087733943b9bfb1a32654387dad4d1c70de21aee2c9" dependencies = [ "bitflags 2.8.0", "cocoa", - "core-foundation", + "core-foundation 0.10.0", "core-graphics", "crossbeam-channel", "dispatch", @@ -3850,6 +4107,26 @@ dependencies = [ ] [[package]] +name = "tauri-plugin-deep-link" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35d51ffd286073414d26353bcfc9e83e3cd63f96fa7f7a912f92f2118e5de5a6" +dependencies = [ + "dunce", + "rust-ini", + "serde", + "serde_json", + "tauri", + "tauri-plugin", + "tauri-utils", + "thiserror 2.0.11", + "tracing", + "url", + "windows-registry 0.3.0", + "windows-result", +] + +[[package]] name = "tauri-plugin-opener" version = "2.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3872,6 +4149,22 @@ dependencies = [ ] [[package]] +name = "tauri-plugin-single-instance" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47c387d4d96690131dc46d1d2827df5c222b896a2bfeb15a16267229a55c50b5" +dependencies = [ + "serde", + "serde_json", + "tauri", + "tauri-plugin-deep-link", + "thiserror 2.0.11", + "tracing", + "windows-sys 0.59.0", + "zbus", +] + +[[package]] name = "tauri-runtime" version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -4066,6 +4359,15 @@ dependencies = [ ] [[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + +[[package]] name = "tinystr" version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -4106,6 +4408,16 @@ dependencies = [ ] [[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] name = "tokio-rustls" version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -4278,6 +4590,12 @@ dependencies = [ ] [[package]] +name = "trim-in-place" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "343e926fc669bc8cde4fa3129ab681c63671bae288b1f1081ceee6d9d37904fc" + +[[package]] name = "try-lock" version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -4418,6 +4736,12 @@ dependencies = [ ] [[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] name = "version-compare" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -4755,7 +5079,7 @@ dependencies = [ "windows-implement", "windows-interface", "windows-result", - "windows-strings", + "windows-strings 0.1.0", "windows-targets 0.52.6", ] @@ -4788,7 +5112,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" dependencies = [ "windows-result", - "windows-strings", + "windows-strings 0.1.0", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-registry" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bafa604f2104cf5ae2cc2db1dee84b7e6a5d11b05f737b60def0ffdc398cbc0a" +dependencies = [ + "windows-result", + "windows-strings 0.2.0", "windows-targets 0.52.6", ] @@ -4812,6 +5147,15 @@ dependencies = [ ] [[package]] +name = "windows-strings" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978d65aedf914c664c510d9de43c8fd85ca745eaff1ed53edf409b479e441663" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] name = "windows-sys" version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" diff --git a/app/Cargo.toml b/app/Cargo.toml index 45b24b1..f11ae44 100644 --- a/app/Cargo.toml +++ b/app/Cargo.toml @@ -11,7 +11,12 @@ crate-type = ["staticlib", "cdylib", "rlib"] tauri-build = { version = "2", features = [] } [dependencies] -tauri = { version = "2", features = ["config-toml"] } -tauri-plugin-opener = "2" +reqwest = { version = "0.12.12", features = ["json"] } serde = { version = "1", features = ["derive"] } serde_json = "1" +tauri = { version = "2", features = ["config-toml"] } +tauri-plugin-opener = "2" +tauri-plugin-deep-link = "2" +tauri-plugin-single-instance = {version = "2", features = ["deep-link"] } +tokio = "1.43.0" +url = "2.5.4" diff --git a/app/Tauri.toml b/app/Tauri.toml index ad2aa7f..7617202 100644 --- a/app/Tauri.toml +++ b/app/Tauri.toml @@ -17,3 +17,8 @@ height = 600 [bundle] active = true + +[plugins.deep-link.desktop] +schemes = [ + "dev.tempest.foxfleet" +] diff --git a/app/src/lib.rs b/app/src/lib.rs index d9ad08c..ae80c24 100644 --- a/app/src/lib.rs +++ b/app/src/lib.rs @@ -1,14 +1,194 @@ +mod state; +use state::AppState; + +use tauri_plugin_deep_link::DeepLinkExt; +use tauri_plugin_opener::OpenerExt; + +use std::collections::HashMap; +use tauri::{Manager, State, AppHandle}; +use url::Host; +use serde::Deserialize; +use tokio::sync::Mutex; +use tokio::sync::mpsc::channel; + +const OAUTH_CLIENT_NAME: &'static str = "foxfleet_test"; + #[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()) - .invoke_handler(tauri::generate_handler![greet]) + .setup(|app| { + app.manage(Mutex::new(state::AppState::default())); + + #[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() { + if domain == "oauth-response" { + return true; + } + } + false + }) { + let mut query = oauth_callback.query_pairs(); + if let Some(code) = query.find(|(key, _value)| key == "code") { + let app_handle = app_handle.clone(); + let code = code.1.to_string(); + tauri::async_runtime::spawn(async move { + let app_state = app_handle.state::<Mutex<AppState>>(); + app_state.lock().await.accounts.iter_mut().for_each(|account| { + // TODO: handle if there's multiple of these that match + if let state::ApiCredential::Pending(sender) = &account.api_credential { + let sender = sender.clone(); + let code = code.clone(); + tauri::async_runtime::spawn(async move { + let _ = sender.send(state::AuthCode(code)).await; + }); + } + }); + }); + } else { + println!("No code in oauth callback"); + return + } + } + }); + Ok(()) + }) + .invoke_handler(tauri::generate_handler![start_account_auth, get_self]) .run(tauri::generate_context!()) .expect("Error starting") } +#[derive(Deserialize)] +struct RegistrationResponse { + id: String, + name: String, + client_id: String, + client_secret: String, +} + +#[derive(Deserialize)] +struct TokenResponse { + access_token: String, + created_at: u32, + scope: String, + token_type: String, +} + #[tauri::command] -fn greet(name: &str) -> String { - format!("Hello, {}!", name) +async fn start_account_auth(app_handle: AppHandle, state: State<'_, Mutex<AppState>>, instance_domain: &str) -> Result<(), ()> { + println!("Starting account auth"); + let registration_endpoint = format!("https://{instance_domain}/api/v1/apps"); + let token_endpoint = format!("https://{instance_domain}/oauth/token"); + let client = reqwest::Client::builder().user_agent("Foxfleet v0.0.1").build().expect("Could not construct client"); + println!("Registering client"); + let registration_response : RegistrationResponse = client.post(registration_endpoint) + .json(&HashMap::from([ + ("client_name", OAUTH_CLIENT_NAME), + ("redirect_uris", "dev.tempest.foxfleet://oauth-response"), + ("scopes", "read write"), + ])).send().await.expect("Could not send client registration") + .json().await.expect("Could not parse client registration response"); + + // Make channel for awaiting + let (sender, mut receiver) = channel::<state::AuthCode>(1); + + println!("Saving registration"); + { state.lock().await.accounts.push(state::Account { + server_domain: instance_domain.to_string(), + handle_domain: None, + client_credential: state::ClientCredential { + client_name: OAUTH_CLIENT_NAME.to_string(), + client_id: registration_response.client_id.clone(), + client_secret: Some(registration_response.client_secret.clone()), + }, + api_credential: state::ApiCredential::Pending(sender), + }) } + + // Open browser to auth page + println!("Opening authentication page"); + let client_id = registration_response.client_id.clone(); + let auth_page = format!("https://{instance_domain}/oauth/authorize?client_id={client_id}&redirect_uri=dev.tempest.foxfleet://oauth-response&response_type=code&scope=read+write"); + let opener = app_handle.opener(); + if let Err(_) = opener.open_url(auth_page, None::<&str>) { + println!("Could not open authentication page"); + return Err(()) + } + + + // Wait for resolution of the credential + let auth_code = receiver.recv().await; + + if auth_code.is_none() { + return Err(()) + } + + let auth_code = auth_code.unwrap(); + println!("Exchanging auth code for API token"); + + // Get long-lived credential + let token_response : TokenResponse = client.post(token_endpoint) + .json(&HashMap::from([ + ("redirect_uri", "dev.tempest.foxfleet://oauth-response"), + ("client_id", registration_response.client_id.as_str()), + ("client_secret", registration_response.client_secret.as_str()), + ("grant_type", "authorization_code"), + ("code", auth_code.0.as_str()), + ])).send().await.expect("Could not get API token") + .json().await.expect("Could not parse client registration response"); + + println!("Successfully exchanged for credential"); + + // Save credential + { state.lock().await.accounts.iter_mut().for_each(|account| { + if account.server_domain == instance_domain { + account.api_credential = state::ApiCredential::Some { + token: token_response.access_token.clone(), + refresh: None, + } + } + }) }; + + println!("Saved credential"); + + Ok(()) } +#[tauri::command] +async fn get_self(state: State<'_, Mutex<AppState>>) -> Result<String, String> { + let client = reqwest::Client::builder().user_agent("Foxfleet v0.0.1").build().expect("Could not construct client"); + + let accounts = { state.lock().await.accounts.clone() }; + let account = accounts.iter().find(|account| { + if let state::ApiCredential::Some {token: _, refresh: _} = account.api_credential { + true + } else { + false + } + }); + + if let Some(account) = account { + if let state::ApiCredential::Some {token, refresh: _} = &account.api_credential { + if let Ok(result) = client.get("https://social.tempest.dev/api/v1/accounts/verify_credentials") + .bearer_auth(token) + .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()); + } + } + } + + return Err("No logged in account".to_string()); +} diff --git a/app/src/state.rs b/app/src/state.rs new file mode 100644 index 0000000..44e74ed --- /dev/null +++ b/app/src/state.rs @@ -0,0 +1,46 @@ +use tokio::sync::mpsc::Sender; + +#[derive(Clone)] +pub struct AppState { + pub preferences: (), + pub accounts: Vec<Account>, +} + +impl AppState { + pub fn default() -> Self { + Self { + preferences: (), + accounts: Vec::new(), + + } + } +} + +#[derive(Clone)] +pub struct Account { + pub server_domain: String, + pub handle_domain: Option<String>, + pub client_credential: ClientCredential, + pub api_credential: ApiCredential, +} + +#[derive(Clone)] +pub struct ClientCredential { + pub client_name: String, + pub client_id: String, + pub client_secret: Option<String>, +} + +#[derive(Clone)] +pub struct AuthCode (pub String); + +#[derive(Clone)] +pub enum ApiCredential { + None, + Pending(Sender<AuthCode>), + Some { + token: String, + refresh: Option<String> + } +} + diff --git a/ui/src/root.tsx b/ui/src/root.tsx index 8db8960..bc87cd9 100644 --- a/ui/src/root.tsx +++ b/ui/src/root.tsx @@ -2,20 +2,28 @@ import { useState } from 'react'; import { invoke } from '@tauri-apps/api/core'; export default function Root() { - const [rustResult, setRustResult] = useState('') + const [signedIn, setSignedIn] = useState(false) + const [accountData, setAccountData] = useState('') - async function callRust() { - const result : string = await invoke('greet', {name: 'ashe'}) - setRustResult(result) + async function signIn() { + await invoke('start_account_auth', {instanceDomain: 'social.tempest.dev'}) + setSignedIn(true) + } + + async function getSelf() { + let result = await invoke('get_self') as string + setAccountData(JSON.parse(result)) } return ( <> - <p>Now we have React</p> - {rustResult - ? <p>Result from rust: <pre><code>{rustResult}</code></pre></p> - : <button onClick={callRust}>Call rust</button> - } + {!signedIn ? ( + <button onClick={signIn}>Sign in</button> + ) : (!accountData ? ( + <button onClick={getSelf}>Retrieve account data</button> + ):( + <p>Result from rust: <pre style={{whiteSpace:'pre'}}>{JSON.stringify(accountData, null, 2)}</pre></p> + ))} </> ) } |