diff --git a/Cargo.lock b/Cargo.lock index f550fea..ad2491e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2797,14 +2797,13 @@ checksum = "f850fafca79ebacd70eab9d80cb75a33aeda38bde8f3dd784c1837cdf0bde631" [[package]] name = "json-patch" -version = "1.0.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f54898088ccb91df1b492cc80029a6fdf1c48ca0db7c6822a8babad69c94658" +checksum = "ec9ad60d674508f3ca8f380a928cfe7b096bc729c4e2dbfe3852bc45da3ab30b" dependencies = [ "serde", "serde_json", "thiserror", - "treediff", ] [[package]] @@ -2942,9 +2941,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.19" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" dependencies = [ "value-bag", ] @@ -3264,6 +3263,7 @@ dependencies = [ "async-trait", "base64-url", "futures", + "lazy_static", "ng-client-ws", "ng-net", "ng-repo", @@ -3271,7 +3271,9 @@ dependencies = [ "ng-verifier", "ng-wallet", "once_cell", + "qrcode", "serde_bare", + "serde_bytes", "serde_json", "web-time", "zeroize", @@ -3293,7 +3295,9 @@ dependencies = [ "sys-locale", "tauri", "tauri-build", + "tauri-plugin-barcode-scanner", "tauri-plugin-window", + "tauri-utils", ] [[package]] @@ -3547,6 +3551,7 @@ dependencies = [ "aes-gcm-siv", "argon2", "async-std", + "base64-url", "blake3", "chacha20poly1305", "crypto_box", @@ -4436,6 +4441,12 @@ dependencies = [ "bytemuck", ] +[[package]] +name = "qrcode" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d68782463e408eb1e668cf6152704bd856c78c5b6417adaee3203d8f4c1fc9ec" + [[package]] name = "quick-xml" version = "0.28.2" @@ -5575,6 +5586,20 @@ dependencies = [ "tauri-utils", ] +[[package]] +name = "tauri-plugin-barcode-scanner" +version = "2.0.0-alpha.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "058922dd9cafc89a865593c99507177c4cdbdad9d22a911ac41872ed7dbf0348" +dependencies = [ + "log", + "serde", + "serde_json", + "tauri", + "tauri-build", + "thiserror", +] + [[package]] name = "tauri-plugin-window" version = "2.0.0-alpha.1" @@ -6043,15 +6068,6 @@ dependencies = [ "windows-sys 0.48.0", ] -[[package]] -name = "treediff" -version = "4.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52984d277bdf2a751072b5df30ec0377febdb02f7696d64c2d7d54630bac4303" -dependencies = [ - "serde_json", -] - [[package]] name = "try-lock" version = "0.2.4" @@ -6198,9 +6214,9 @@ checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" [[package]] name = "value-bag" -version = "1.4.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4d330786735ea358f3bc09eea4caa098569c1c93f342d9aca0514915022fe7e" +checksum = "5a84c137d37ab0142f0f2ddfe332651fdbf252e7b7dbb4e67b6c1f1b2e925101" [[package]] name = "vcpkg" diff --git a/README.md b/README.md index a1a8a3a..e8d9f17 100644 --- a/README.md +++ b/README.md @@ -43,9 +43,9 @@ Read our [getting started guide](https://docs.nextgraph.org/en/getting-started/) ## For contributors -- [Install Rust](https://www.rust-lang.org/tools/install) minimum required MSRV 1.74.0 -- [Install Nodejs](https://nodejs.org/en/download/) -- [Install LLVM](https://rust-lang.github.io/rust-bindgen/requirements.html) +- [Install Rust](https://www.rust-lang.org/tools/install) minimum required MSRV 1.74.0 +- [Install Nodejs](https://nodejs.org/en/download/) +- [Install LLVM](https://rust-lang.github.io/rust-bindgen/requirements.html) On openbsd, for LLVM you need to choose llvm-17. @@ -57,6 +57,14 @@ cargo install wasm-pack --git https://github.com/rustwasm/wasm-pack.git --rev c2 then : +create a file called `nextgraph/src/local_broker_dev_env.rs` with the content : + +``` +pub const PEER_ID: &str = "FtdzuDYGewfXWdoPuXIPb0wnd0SAg1WoA2B14S7jW3MA"; +``` + +once your ngd server will run in your dev env, replace the above string with the actual PEER ID of your ngd server. + ``` cargo install cargo-watch // optionally, if you want a Rust REPL: cargo install evcxr_repl @@ -70,20 +78,20 @@ cargo build The crates are organized as follow : -- [nextgraph](nextgraph/README.md) : Client library. Use this crate to embed NextGraph client in your Rust application -- [ngcli](ngcli/README.md) : CLI tool to manipulate the local documents and repos and administrate the server -- [ngd](ngd/README.md) : binary executable of the daemon (that can run a broker, verifier and/or Rust services) -- [ng-app](ng-app/README.md) : all the native apps, based on Tauri, and the official web app. -- [ng-sdk-js](ng-sdk-js/DEV.md) : contains the JS SDK, with example for: web app, react app, or node service. -- ng-repo : Repositories common library -- ng-net : Network common library -- ng-verifier : Verifier library, that exposes the document API to the app -- ng-wallet : keeps the secret keys of all identities of the user in a safe wallet -- ng-broker : Core and Server Broker library -- ng-client-ws : Websocket client library -- ng-storage-rocksdb : RocksDB backed stores. see also dependency [repo here](https://git.nextgraph.org/NextGraph/rust-rocksdb) -- ngone : server for nextgraph.one. helps user bootstrap into the right app. Not useful to you. Published here for transparency -- ngaccount : server for nextgraph's Broker Service Provider account manager. Not useful to you. Published here for transparency +- [nextgraph](nextgraph/README.md) : Client library. Use this crate to embed NextGraph client in your Rust application +- [ngcli](ngcli/README.md) : CLI tool to manipulate the local documents and repos and administrate the server +- [ngd](ngd/README.md) : binary executable of the daemon (that can run a broker, verifier and/or Rust services) +- [ng-app](ng-app/README.md) : all the native apps, based on Tauri, and the official web app. +- [ng-sdk-js](ng-sdk-js/DEV.md) : contains the JS SDK, with example for: web app, react app, or node service. +- ng-repo : Repositories common library +- ng-net : Network common library +- ng-verifier : Verifier library, that exposes the document API to the app +- ng-wallet : keeps the secret keys of all identities of the user in a safe wallet +- ng-broker : Core and Server Broker library +- ng-client-ws : Websocket client library +- ng-storage-rocksdb : RocksDB backed stores. see also dependency [repo here](https://git.nextgraph.org/NextGraph/rust-rocksdb) +- ngone : server for nextgraph.one. helps user bootstrap into the right app. Not useful to you. Published here for transparency +- ngaccount : server for nextgraph's Broker Service Provider account manager. Not useful to you. Published here for transparency ### Run @@ -224,9 +232,9 @@ additional terms or conditions. Licensed under either of -- Apache License, Version 2.0 ([LICENSE-APACHE2](LICENSE-APACHE2) or http://www.apache.org/licenses/LICENSE-2.0) -- MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) - at your option. +- Apache License, Version 2.0 ([LICENSE-APACHE2](LICENSE-APACHE2) or http://www.apache.org/licenses/LICENSE-2.0) +- MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) + at your option. `SPDX-License-Identifier: Apache-2.0 OR MIT` diff --git a/nextgraph/.gitignore b/nextgraph/.gitignore index 3598c30..01f9583 100644 --- a/nextgraph/.gitignore +++ b/nextgraph/.gitignore @@ -1 +1,2 @@ -tests \ No newline at end of file +tests +local_broker_dev_env_peer_id.rs \ No newline at end of file diff --git a/nextgraph/Cargo.toml b/nextgraph/Cargo.toml index a160823..e9ee4b7 100644 --- a/nextgraph/Cargo.toml +++ b/nextgraph/Cargo.toml @@ -18,6 +18,7 @@ maintenance = { status = "actively-developed" } [dependencies] serde_bare = "0.5.0" serde_json = "1.0" +serde_bytes = "0.11.7" base64-url = "2.0.0" once_cell = "1.17.1" zeroize = { version = "1.7.0", features = ["zeroize_derive"] } @@ -25,7 +26,9 @@ futures = "0.3.24" async-std = { version = "1.12.0", features = [ "attributes", "unstable" ] } async-trait = "0.1.64" async-once-cell = "0.5.3" +lazy_static = "1.4.0" web-time = "0.2.0" +qrcode = { version = "0.14.1", default-features = false, features = ["svg"] } ng-repo = { path = "../ng-repo", version = "0.1.0-preview.1" } ng-net = { path = "../ng-net", version = "0.1.0-preview.1" } ng-wallet = { path = "../ng-wallet", version = "0.1.0-preview.5" } diff --git a/nextgraph/src/lib.rs b/nextgraph/src/lib.rs index 9dbf3a0..9eca8d7 100644 --- a/nextgraph/src/lib.rs +++ b/nextgraph/src/lib.rs @@ -98,3 +98,6 @@ pub mod verifier { pub mod wallet { pub use ng_wallet::*; } + +#[cfg(debug_assertions)] +mod local_broker_dev_env; diff --git a/nextgraph/src/local_broker.rs b/nextgraph/src/local_broker.rs index edf7e60..2a7584d 100644 --- a/nextgraph/src/local_broker.rs +++ b/nextgraph/src/local_broker.rs @@ -20,6 +20,8 @@ use once_cell::sync::Lazy; use serde_bare::to_vec; use serde_json::json; use zeroize::Zeroize; +use qrcode::{render::svg, QrCode}; +use lazy_static::lazy_static; use ng_repo::block_storage::BlockStorage; use ng_repo::block_storage::HashMapBlockStorage; @@ -27,7 +29,7 @@ use ng_repo::errors::{NgError, ProtocolError}; use ng_repo::log::*; use ng_repo::os_info::get_os_info; use ng_repo::types::*; -use ng_repo::utils::{derive_key, generate_keypair}; +use ng_repo::utils::{derive_key, generate_keypair, encrypt_in_place}; use ng_net::{actor::*,actors::admin::*}; use ng_net::broker::*; @@ -1492,6 +1494,238 @@ pub async fn wallet_add(lws: LocalWalletStorageV0) -> Result<(), NgError> { } Ok(()) } +#[cfg(debug_assertions)] +lazy_static! { + + static ref NEXTGRAPH_EU: BrokerServerV0 = BrokerServerV0 { + server_type: BrokerServerTypeV0::Localhost(14400), + can_verify: false, + can_forward: false, + peer_id: ng_repo::utils::decode_key({use crate::local_broker_dev_env::PEER_ID; PEER_ID}).unwrap(), + }; +} + +#[cfg(not(debug_assertions))] +lazy_static! { + static ref NEXTGRAPH_EU: BrokerServerV0 = BrokerServerV0 { + server_type: BrokerServerTypeV0::Domain("nextgraph.eu".to_string()), + can_verify: false, + can_forward: false, + peer_id: ng_repo::utils::decode_key("FtdzuDYGewfXWdoPuXIPb0wnd0SAg1WoA2B14S7jW3MA").unwrap(), + }; + +} + +/// Obtains a Wallet object from a QRCode or a TextCode. +/// +/// The returned object can be used to import the wallet into a new Device +/// with the help of the function [wallet_open_with_pazzle_words] +/// followed by [wallet_import] +pub async fn wallet_import_from_code(code: String) -> Result { + + let qr = NgQRCode::from_code(code)?; + match qr { + NgQRCode::V0(NgQRCodeV0{broker, rendezvous, secret_key, is_rendezvous}) => { + let wallet: ExportedWallet = do_ext_call( + &broker, + ExtWalletGetExportV0 { + id:rendezvous, + is_rendezvous + }).await?; + + let mut buf = wallet.0.into_vec(); + encrypt_in_place(&mut buf,*secret_key.slice(), [0;12]); + let wallet: Wallet = serde_bare::from_slice(&buf)?; + + let broker = match LOCAL_BROKER.get() { + None | Some(Err(_)) => return Err(NgError::LocalBrokerNotInitialized), + Some(Ok(broker)) => broker.read().await, + }; + // check that the wallet is not already present in local_broker + let wallet_name = wallet.name(); + if broker.wallets.get(&wallet_name).is_none() { + Ok(wallet) + } else { + Err(NgError::WalletAlreadyAdded) + } + + } + } +} + +/// Starts a rendez-vous to obtain a wallet from other device. +/// +/// A rendezvous is used when the device that is importing, doesn't have a camera. +/// The QRCode is displayed on that device, and another device (with camera, and with the wallet) will scan it. +/// +/// Returns the QRcode in SVG format, and the code (a string) to be used with [wallet_import_from_code] +pub async fn wallet_import_rendezvous(size: u32) -> Result<(String,String), NgError> { + let code = NgQRCode::V0(NgQRCodeV0 { + broker: NEXTGRAPH_EU.clone(), + rendezvous: SymKey::random(), + secret_key: SymKey::random(), + is_rendezvous: true + }); + let code_string = code.to_code(); + + let code_svg = match QrCode::with_error_correction_level(&code_string, qrcode::EcLevel::M) { + Ok(qr) => { + let svg = qr + .render() + .max_dimensions(size, size) + .dark_color(svg::Color("#000000")) + .light_color(svg::Color("#ffffff")) + .build(); + svg + }, + Err(_e) => {return Err(NgError::BrokerError)} + }; + + Ok((code_svg,code_string)) +} + +/// Gets the TextCode to display in order to export the wallet of the current session ID +/// +/// The ExportedWallet is valid for 5 min. +/// +/// Returns the TextCode +pub async fn wallet_export_get_textcode(session_id: u64) -> Result { + + let broker = match LOCAL_BROKER.get() { + None | Some(Err(_)) => return Err(NgError::LocalBrokerNotInitialized), + Some(Ok(broker)) => broker.read().await, + }; + + match &broker.config { + LocalBrokerConfig::Headless(_) => { + return Err(NgError::LocalBrokerIsHeadless) + }, + _ => { + let (real_session_id, is_remote) = broker.get_real_session_id_for_mut(session_id)?; + + if is_remote { + return Err(NgError::NotImplemented); + } else { + let session = broker.opened_sessions_list[real_session_id].as_ref() + .ok_or(NgError::SessionNotFound)?; + let wallet_name = session.config.wallet_name(); + + match broker.wallets.get(&wallet_name) { + None => Err(NgError::WalletNotFound), + Some(lws) => { + let broker = lws.bootstrap.servers().first().unwrap(); + let wallet = &lws.wallet; + + let secret_key = SymKey::random(); + let rendezvous = SymKey::random(); + let code = NgQRCode::V0(NgQRCodeV0 { + broker: broker.clone(), + rendezvous: rendezvous.clone(), + secret_key: secret_key.clone(), + is_rendezvous: false + }); + let code_string = code.to_code(); + + let mut wallet_ser = serde_bare::to_vec(wallet)?; + encrypt_in_place(&mut wallet_ser,*secret_key.slice(), [0;12]); + let exported_wallet = ExportedWallet(serde_bytes::ByteBuf::from(wallet_ser)); + + match session.verifier.client_request::(WalletPutExport::V0(WalletPutExportV0{wallet:exported_wallet, rendezvous_id:rendezvous, is_rendezvous:false})).await { + Err(e) => Err(e), + Ok(SoS::Stream(_)) => Err(NgError::InvalidResponse), + Ok(SoS::Single(_)) => Ok(code_string) + } + } + } + } + } + } +} + +/// Gets the QRcode to display in order to export a wallet of the current session ID +/// +/// The ExportedWallet is valid for 5 min. +/// +/// Returns the QRcode in SVG format +pub async fn wallet_export_get_qrcode(session_id: u64, size: u32) -> Result { + + let code_string = wallet_export_get_textcode(session_id).await?; + + let code_svg = match QrCode::with_error_correction_level(&code_string, qrcode::EcLevel::M) { + Ok(qr) => { + let svg = qr + .render() + .max_dimensions(size, size) + .dark_color(svg::Color("#000000")) + .light_color(svg::Color("#ffffff")) + .build(); + svg + }, + Err(_e) => {return Err(NgError::BrokerError)} + }; + + Ok(code_svg) + +} + +/// Puts the Wallet to the rendezvous ID that was scanned +/// +/// The rendezvous ID is valid for 5 min. +pub async fn wallet_export_rendezvous(session_id: u64, code: String) -> Result<(), NgError> { + + let qr = NgQRCode::from_code(code)?; + match qr { + NgQRCode::V0(NgQRCodeV0{broker: _, rendezvous, secret_key, is_rendezvous}) => { + + if !is_rendezvous { + return Err(NgError::NotARendezVous); + } + + let broker = match LOCAL_BROKER.get() { + None | Some(Err(_)) => return Err(NgError::LocalBrokerNotInitialized), + Some(Ok(broker)) => broker.read().await, + }; + + match &broker.config { + LocalBrokerConfig::Headless(_) => { + return Err(NgError::LocalBrokerIsHeadless) + }, + _ => { + let (real_session_id, is_remote) = broker.get_real_session_id_for_mut(session_id)?; + + if is_remote { + return Err(NgError::NotImplemented); + } else { + let session = broker.opened_sessions_list[real_session_id].as_ref() + .ok_or(NgError::SessionNotFound)?; + let wallet_name = session.config.wallet_name(); + + match broker.wallets.get(&wallet_name) { + None => Err(NgError::WalletNotFound), + Some(lws) => { + //let broker = lws.bootstrap.servers().first().unwrap(); + let wallet = &lws.wallet; + + let mut wallet_ser = serde_bare::to_vec(wallet)?; + encrypt_in_place(&mut wallet_ser,*secret_key.slice(), [0;12]); + let exported_wallet = ExportedWallet(serde_bytes::ByteBuf::from(wallet_ser)); + + // TODO: send the WalletPutExport client request to the broker received from QRcode. for now it is cheer luck that all clients are connected to nextgraph.eu. + // if the user doesn't have an account with nextgraph.eu, their broker should relay the request (core protocol ?) + + match session.verifier.client_request::(WalletPutExport::V0(WalletPutExportV0{wallet:exported_wallet, rendezvous_id:rendezvous, is_rendezvous:true})).await { + Err(e) => Err(e), + Ok(SoS::Stream(_)) => Err(NgError::InvalidResponse), + Ok(SoS::Single(_)) => Ok(()) + } + } + } + } + } + } + } + } +} /// Reads a binary Wallet File and decodes it to a Wallet object. /// @@ -2126,7 +2360,7 @@ pub async fn app_request(request: AppRequest) -> Result { let session = broker.opened_sessions_list[real_session_id] .as_mut() .ok_or(NgError::SessionNotFound)?; - session.verifier.app_request(request).await + session.verifier.app_request(request).await } } } @@ -2214,6 +2448,29 @@ async fn do_admin_call< .await } +async fn do_ext_call< + A: Into + Into + std::fmt::Debug + Sync + Send + 'static, + B: TryFrom + + std::fmt::Debug + + Sync + + Send + + 'static, +>( + broker_server: &BrokerServerV0, + cmd: A, +) -> Result { + let (peer_privk, peer_pubk) = generate_keypair(); + Broker::ext( + Box::new(ConnectionWebSocket {}), + peer_privk, + peer_pubk, + broker_server.peer_id, + broker_server.get_ws_url(&None).await.unwrap().0, // for now we are only connecting to NextGraph SaaS cloud (nextgraph.eu) so it is safe. + cmd, + ) + .await +} + #[doc(hidden)] pub async fn admin_create_user(server_peer_id: DirectPeerId, admin_user_key: PrivKey, server_addr: BindAddress) -> Result { diff --git a/nextgraph/src/local_broker_dev_env.rs b/nextgraph/src/local_broker_dev_env.rs new file mode 100644 index 0000000..1ddaafe --- /dev/null +++ b/nextgraph/src/local_broker_dev_env.rs @@ -0,0 +1 @@ +pub const PEER_ID: &str = "FtdzuDYGewfXWdoPuXIPb0wnd0SAg1WoA2B14S7jW3MA"; diff --git a/ng-app/package.json b/ng-app/package.json index af43b6d..87d425e 100644 --- a/ng-app/package.json +++ b/ng-app/package.json @@ -18,6 +18,7 @@ "dependencies": { "@popperjs/core": "^2.11.8", "@tauri-apps/api": "2.0.0-alpha.8", + "@tauri-apps/plugin-barcode-scanner": "2.0.0-alpha.0", "@tauri-apps/plugin-window": "2.0.0-alpha.1", "async-proxy": "^0.4.1", "classnames": "^2.3.2", diff --git a/ng-app/src-tauri/Cargo.toml b/ng-app/src-tauri/Cargo.toml index 0c9ba1a..89b0715 100644 --- a/ng-app/src-tauri/Cargo.toml +++ b/ng-app/src-tauri/Cargo.toml @@ -21,7 +21,7 @@ crate-type = ["staticlib", "cdylib", "rlib"] tauri-build = { version = "2.0.0-alpha.8", features = [] } # tauri-macros = { version = "=2.0.0-alpha.6" } # tauri-codegen = { version = "=2.0.0-alpha.6" } -# tauri-utils = { version = "=2.0.0-alpha.6" } +tauri-utils = { version = "=2.0.0-alpha.7" } [dependencies] serde = { version = "1.0", features = ["derive"] } @@ -32,6 +32,7 @@ sys-locale = { version = "0.3.1" } ng-async-tungstenite = { git = "https://git.nextgraph.org/NextGraph/async-tungstenite.git", branch = "nextgraph", features = ["async-std-runtime", "async-native-tls"] } tauri = { version = "2.0.0-alpha.14", features = [] } tauri-plugin-window = "2.0.0-alpha.1" +tauri-plugin-barcode-scanner = "=2.0.0-alpha.0" # tauri-plugin-window = { git = "https://git.nextgraph.org/NextGraph/plugins-workspace.git", branch="window-alpha.1-nextgraph" } # tauri = { git = "https://git.nextgraph.org/NextGraph/tauri.git", branch="alpha.11-nextgraph", features = ["no-ipc-custom-protocol"] } # tauri = { git = "https://github.com/simonhyll/tauri.git", branch="fix/ipc-mixup", features = [] } diff --git a/ng-app/src-tauri/gen/android/app/src/main/AndroidManifest.xml b/ng-app/src-tauri/gen/android/app/src/main/AndroidManifest.xml index a057f34..493b196 100644 --- a/ng-app/src-tauri/gen/android/app/src/main/AndroidManifest.xml +++ b/ng-app/src-tauri/gen/android/app/src/main/AndroidManifest.xml @@ -1,6 +1,7 @@ + Result { - let wallet = nextgraph::local_broker::wallet_open_with_mnemonic_words(&wallet, &mnemonic_words, pin) - .map_err(|e| e.to_string())?; + let wallet = + nextgraph::local_broker::wallet_open_with_mnemonic_words(&wallet, &mnemonic_words, pin) + .map_err(|e| e.to_string())?; Ok(wallet) } @@ -205,6 +206,55 @@ async fn wallet_import( .map_err(|e: NgError| e.to_string()) } +#[tauri::command(rename_all = "snake_case")] +async fn wallet_export_rendezvous( + session_id: u64, + code: String, + _app: tauri::AppHandle, +) -> Result<(), String> { + nextgraph::local_broker::wallet_export_rendezvous(session_id, code) + .await + .map_err(|e: NgError| e.to_string()) +} + +#[tauri::command(rename_all = "snake_case")] +async fn wallet_export_get_qrcode( + session_id: u64, + size: u32, + _app: tauri::AppHandle, +) -> Result { + nextgraph::local_broker::wallet_export_get_qrcode(session_id, size) + .await + .map_err(|e: NgError| e.to_string()) +} + +#[tauri::command(rename_all = "snake_case")] +async fn wallet_export_get_textcode( + session_id: u64, + _app: tauri::AppHandle, +) -> Result { + nextgraph::local_broker::wallet_export_get_textcode(session_id) + .await + .map_err(|e: NgError| e.to_string()) +} + +#[tauri::command(rename_all = "snake_case")] +async fn wallet_import_rendezvous( + size: u32, + _app: tauri::AppHandle, +) -> Result<(String, String), String> { + nextgraph::local_broker::wallet_import_rendezvous(size) + .await + .map_err(|e: NgError| e.to_string()) +} + +#[tauri::command(rename_all = "snake_case")] +async fn wallet_import_from_code(code: String, _app: tauri::AppHandle) -> Result { + nextgraph::local_broker::wallet_import_from_code(code) + .await + .map_err(|e: NgError| e.to_string()) +} + #[tauri::command(rename_all = "snake_case")] async fn get_wallets( app: tauri::AppHandle, @@ -517,7 +567,7 @@ impl AppBuilder { pub fn run(self) { let setup = self.setup; - tauri::Builder::default() + let builder = tauri::Builder::default() .setup(move |app| { if let Some(setup) = setup { (setup)(app)?; @@ -533,7 +583,14 @@ impl AppBuilder { } Ok(()) }) - .plugin(tauri_plugin_window::init()) + .plugin(tauri_plugin_window::init()); + + #[cfg(mobile)] + { + let builder = builder.plugin(tauri_plugin_barcode_scanner::init()); + } + + builder .invoke_handler(tauri::generate_handler![ test, locales, @@ -547,6 +604,11 @@ impl AppBuilder { wallet_read_file, wallet_get_file, wallet_import, + wallet_export_rendezvous, + wallet_export_get_qrcode, + wallet_export_get_textcode, + wallet_import_rendezvous, + wallet_import_from_code, wallet_close, encode_create_account, session_start, diff --git a/ng-app/src/App.svelte b/ng-app/src/App.svelte index 3efb223..ee84e85 100644 --- a/ng-app/src/App.svelte +++ b/ng-app/src/App.svelte @@ -35,6 +35,7 @@ import User from "./routes/User.svelte"; import UserRegistered from "./routes/UserRegistered.svelte"; import Install from "./routes/Install.svelte"; + import ScanQR from "./routes/ScanQR.svelte"; import ng from "./api"; import AccountInfo from "./routes/AccountInfo.svelte"; @@ -49,6 +50,7 @@ routes.set("/user/registered", UserRegistered); routes.set("/wallet", WalletInfo); routes.set("/user/accounts", AccountInfo); + routes.set("/wallet/scanqr", ScanQR); if (import.meta.env.NG_APP_WEB) routes.set("/install", Install); routes.set(/^\/did:ng(.*)/i, NURI); routes.set("*", NotFound); diff --git a/ng-app/src/api.ts b/ng-app/src/api.ts index 91732c0..369c731 100644 --- a/ng-app/src/api.ts +++ b/ng-app/src/api.ts @@ -22,6 +22,11 @@ const mapping = { "wallet_read_file": ["file"], "wallet_get_file": ["wallet_name"], "wallet_import": ["encrypted_wallet","opened_wallet","in_memory"], + "wallet_export_rendezvous": ["session_id", "code"], + "wallet_export_get_qrcode": ["session_id", "size"], + "wallet_export_get_textcode": ["session_id"], + "wallet_import_rendezvous": ["size"], + "wallet_import_from_code": ["code"], "wallet_close": ["wallet_name"], "encode_create_account": ["payload"], "session_start": ["wallet_name","user"], diff --git a/ng-app/src/lib/Login.svelte b/ng-app/src/lib/Login.svelte index 765df6b..6b83d11 100644 --- a/ng-app/src/lib/Login.svelte +++ b/ng-app/src/lib/Login.svelte @@ -33,6 +33,7 @@ } from "svelte-heros-v2"; import PasswordInput from "./components/PasswordInput.svelte"; import Spinner from "./components/Spinner.svelte"; + import { display_error } from "../store"; //import Worker from "../worker.js?worker&inline"; export let wallet; export let for_import = false; @@ -649,7 +650,7 @@ /> - {$t("errors." + error)} + {display_error(error)}
diff --git a/ng-app/src/locales/en.json b/ng-app/src/locales/en.json index 5e60d09..b15a24f 100644 --- a/ng-app/src/locales/en.json +++ b/ng-app/src/locales/en.json @@ -296,18 +296,19 @@ "VerifierError": "Error during verification.", "SiteNotFoundOnBroker": "The site cannot be found on the broker", "BrokerConfigErrorStr": "{error}", - "BrokerConfigError": "Error in the broker configuration", + "BrokerConfigError": "Error in the broker configuration.", "MalformedEvent": "The event has an invalid format.", "InvalidPayload": "The payload is invalid.", "WrongUploadId": "The upload ID is incorrect.", "FileError": "Error with file.", "InternalError": "Internal Error", "OxiGraphError": "Error in OxiGraph database.", - "ConfigError": "Error in configuration", + "ConfigError": "Error in configuration.", "LocalBrokerIsHeadless": "The local broker is headless.", "LocalBrokerIsNotHeadless": "The local broker is not headless.", - "InvalidNuri": "Invalid NextGraph URI", - "InvalidTarget": "Cannot resolve target" + "InvalidNuri": "Invalid NextGraph URI.", + "InvalidTarget": "Cannot resolve target.", + "ExportWalletTimeOut": "Export of wallet has expired." }, "connectivity": { "stopped": "Stopped", diff --git a/ng-app/src/routes/AccountInfo.svelte b/ng-app/src/routes/AccountInfo.svelte index 2423ab8..37061a0 100644 --- a/ng-app/src/routes/AccountInfo.svelte +++ b/ng-app/src/routes/AccountInfo.svelte @@ -22,7 +22,7 @@ import { onMount, tick } from "svelte"; import { Sidebar, SidebarGroup, SidebarWrapper } from "flowbite-svelte"; import { t } from "svelte-i18n"; - import { active_session, active_wallet, connections } from "../store"; + import { active_session, active_wallet, connections, display_error } from "../store"; import { default as ng } from "../api"; import DeviceIcon from "../lib/components/DeviceIcon.svelte"; @@ -343,7 +343,7 @@ {:else}

{@html $t("errors.error_occurred", { - values: { message: $t("errors." + error) }, + values: { message: display_error(error) }, })}

diff --git a/ng-app/src/routes/ScanQR.svelte b/ng-app/src/routes/ScanQR.svelte new file mode 100644 index 0000000..7d8f393 --- /dev/null +++ b/ng-app/src/routes/ScanQR.svelte @@ -0,0 +1,51 @@ + + + + + + +
+ + Scanning the QRcode +
\ No newline at end of file diff --git a/ng-app/src/routes/User.svelte b/ng-app/src/routes/User.svelte index f10e73e..678ff1c 100644 --- a/ng-app/src/routes/User.svelte +++ b/ng-app/src/routes/User.svelte @@ -365,7 +365,7 @@ {:else}

{@html $t("errors.error_occurred", { - values: { message: $t("errors." + error) }, + values: { message: display_error(error) }, })}

diff --git a/ng-app/src/routes/UserRegistered.svelte b/ng-app/src/routes/UserRegistered.svelte index 522a94b..235f6db 100644 --- a/ng-app/src/routes/UserRegistered.svelte +++ b/ng-app/src/routes/UserRegistered.svelte @@ -19,6 +19,7 @@ import { onMount, tick } from "svelte"; import { default as ng } from "../api"; + import { display_error } from "../store"; const param = new URLSearchParams($querystring); @@ -79,7 +80,7 @@ {:else}

{@html $t("errors.error_occurred", { - values: { message: $t("errors." + error) }, + values: { message: display_error(error) }, })}

diff --git a/ng-app/src/routes/WalletCreate.svelte b/ng-app/src/routes/WalletCreate.svelte index f2628d5..6e07ec6 100644 --- a/ng-app/src/routes/WalletCreate.svelte +++ b/ng-app/src/routes/WalletCreate.svelte @@ -44,7 +44,7 @@ } from "../wallet_emojis"; import { onMount, onDestroy, tick } from "svelte"; - import { wallets, set_active_session, has_wallets } from "../store"; + import { wallets, set_active_session, has_wallets, display_error } from "../store"; import Spinner from "../lib/components/Spinner.svelte"; const param = new URLSearchParams($querystring); @@ -316,7 +316,7 @@ unsub_register_accepted = undefined; }; - onDestroy(() => { + onDestroy(async () => { unsub_register(); }); @@ -570,7 +570,7 @@ {:else}

{@html $t("errors.error_occurred", { - values: { message: $t("errors." + registration_error) }, + values: { message: display_error(registration_error) }, })}

@@ -1708,7 +1708,7 @@ /> - {$t("errors." + error)} + {display_error(error)}
{/each} + +
+ {#if qrcode} + {@html qrcode[0]} + {/if} +
+
+ {#if qrcode} + {qrcode[1]} + {/if} +
+
- {#if $has_wallets}

+ {#if $has_wallets} +

{$t("pages.wallet_login.with_another_wallet")}

- {:else}

{$t("pages.wallet_login.import_wallet")}

+ {:else} +

+ {$t("pages.wallet_login.import_wallet")} +

{/if} {$t("pages.wallet_login.import_file")} - - +
+ +