+
# nextgraph-rs
+![MSRV][rustc-image]
+[![Apache 2.0 Licensed][license-image]][license-link]
+[![MIT Licensed][license-image2]][license-link2]
+[![project chat](https://img.shields.io/badge/zulip-join_chat-brightgreen.svg)](https://forum.nextgraph.org)
+
Rust implementation of NextGraph
This repository is in active development at [https://git.nextgraph.org/NextGraph/nextgraph-rs](https://git.nextgraph.org/NextGraph/nextgraph-rs), a Gitea instance. For bug reports, issues, merge requests, and in order to join the dev team, please visit the link above and create an account (you can do so with a github account). The [github repo](https://github.com/nextgraph-org/nextgraph-rs) is just a read-only mirror that does not accept issues.
@@ -8,7 +17,7 @@ This repository is in active development at [https://git.nextgraph.org/NextGraph
> NextGraph brings about the convergence between P2P and Semantic Web technologies, towards a decentralized, secure and privacy-preserving cloud, based on CRDTs.
>
-> This open source ecosystem provides solutions for end-users and software developers alike, wishing to use or create **decentralized** apps featuring: **live collaboration** on rich-text documents, peer to peer communication with end-to-end encryption, offline-first, **local-first**, portable and interoperable data, total ownership of data and software, security and privacy. Centered on repositories containing **semantic data** (RDF), **rich text**, and structured data formats like **JSON**, synced between peers belonging to permissioned groups of users, it offers strong eventual consistency, thanks to the use of **CRDTs**. Documents can be linked together, signed, shared securely, queried using the **SPARQL** language and organized into sites and containers.
+> This open source ecosystem provides solutions for end-users and software developers alike, wishing to use or create **decentralized** apps featuring: **live collaboration** on rich-text documents, peer to peer communication with **end-to-end encryption**, offline-first, **local-first**, portable and interoperable data, total ownership of data and software, security and privacy. Centered on repositories containing **semantic data** (RDF), **rich text**, and structured data formats like **JSON**, synced between peers belonging to permissioned groups of users, it offers strong eventual consistency, thanks to the use of **CRDTs**. Documents can be linked together, signed, shared securely, queried using the **SPARQL** language and organized into sites and containers.
>
> More info here [https://nextgraph.org](https://nextgraph.org)
@@ -18,6 +27,9 @@ Documentation can be found here [https://docs.nextgraph.org](https://docs.nextgr
And our community forum where you can ask questions is here [https://forum.nextgraph.org](https://forum.nextgraph.org)
+[![Mastodon](https://img.shields.io/badge/-MASTODON-%232B90D9?style=for-the-badge&logo=mastodon&logoColor=white)](https://mastodon.lescommuns.org/@nextgraph)
+[![Twitter URL](https://img.shields.io/twitter/url?style=social&url=https%3A%2F%2Ftwitter.com%2Fnextgraph)](https://twitter.com/nextgraph)
+
## How to use NextGraph
NextGraph is not ready yet. You can subscribe to [our newsletter](https://list.nextgraph.org/subscription/form) to get updates, and support us with a [donation](https://nextgraph.org/donate/).
@@ -48,19 +60,20 @@ cargo build
The crates are organized as follow :
-- p2p-repo : NextGraph repositories common library
-- p2p-net : P2P network common library
-- p2p-broker : the broker code (as server and core node)
-- p2p-client-ws : the client connecting to a broker with WebSocket, used by the apps and verifier
-- p2p-verifier : the code of the verifier
-- stores-rocksdb : RocksDB backed stores. see [repo here](https://git.nextgraph.org/NextGraph/rust-rocksdb)
-- ngcli : CLI tool to manipulate the repos and administrate the server
-- ngd : binary executable of the daemon (that can run a broker, verifier and/or Rust services)
+- [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-repo : Repositories common library
+- ng-net : Network common library
+- ng-broker : Core and Server Broker library
+- ng-client-ws : Websocket client library
+- ng-verifier : Verifier library, that exposes the document API to the app
+- ng-stores-rocksdb : RocksDB backed stores. see also dependency [repo here](https://git.nextgraph.org/NextGraph/rust-rocksdb)
- ng-wallet : keeps the secret keys of all identities of the user in a safe wallet
- [ng-sdk-js](ng-sdk-js/README.md) : contains the JS SDK, with example apps: web app, react app, or node service.
- [ng-app](ng-app/README.md) : all the native apps, based on Tauri, and the web app.
-- [ngone](ngone/README.md) : server for nextgraph.one (helps user bootstrap into the right app)
-- [ngaccount](ngaccount/README.md) : server for nextgraph's Broker Service Provider account manager.
+- 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
@@ -87,7 +100,7 @@ cargo test --all --verbose -- --show-output --nocapture
Test a single module:
```
-cargo test --package p2p-repo --lib -- branch::test --show-output --nocapture
+cargo test --package ng-repo --lib -- branch::test --show-output --nocapture
```
Test end-to-end client and server:
@@ -106,7 +119,7 @@ wasm-pack test --chrome --headless
Test Rust websocket
```
-cargo test --package p2p-client-ws --lib -- remote_ws::test::test_ws --show-output --nocapture
+cargo test --package ng-client-ws --lib -- remote_ws::test::test_ws --show-output --nocapture
```
### Build release binaries
@@ -155,7 +168,7 @@ For building the apps, see this [documentation](ng-app/README.md).
#### OpenBSD
On OpenBSD, a conflict between the installed LibreSSL library and the reqwest crate, needs a bit of attention.
-Before compiling the daemon for OpenBSD, please comment out lines 32-33 of `p2p-net/Cargo.toml`. This will be solved soon in a more appropriate way.
+Before compiling the daemon for OpenBSD, please comment out lines 32-33 of `ng-net/Cargo.toml`. This will be solved soon in a more appropriate way.
```
#[target.'cfg(target_arch = "wasm32")'.dependencies]
@@ -203,3 +216,9 @@ Licensed under either of
---
NextGraph received funding through the [NGI Assure Fund](https://nlnet.nl/project/NextGraph/index.html), a fund established by [NLnet](https://nlnet.nl/) with financial support from the European Commission's [Next Generation Internet](https://ngi.eu/) programme, under the aegis of DG Communications Networks, Content and Technology under grant agreement No 957073.
+
+[rustc-image]: https://img.shields.io/badge/rustc-1.64+-blue.svg
+[license-image]: https://img.shields.io/badge/license-Apache2.0-blue.svg
+[license-link]: https://git.nextgraph.org/NextGraph/nextgraph-rs/raw/branch/master/LICENSE-APACHE2
+[license-image2]: https://img.shields.io/badge/license-MIT-blue.svg
+[license-link2]: https://git.nextgraph.org/NextGraph/nextgraph-rs/src/branch/master/LICENSE-MIT
diff --git a/nextgraph/Cargo.toml b/nextgraph/Cargo.toml
new file mode 100644
index 0000000..f838918
--- /dev/null
+++ b/nextgraph/Cargo.toml
@@ -0,0 +1,32 @@
+[package]
+name = "nextgraph"
+description = "NextGraph client library. Nextgraph is a decentralized, secure and local-first web 3.0 ecosystem based on Semantic Web and CRDTs"
+categories = ["asynchronous","text-editors","web-programming","development-tools","database-implementations"]
+version.workspace = true
+edition.workspace = true
+license.workspace = true
+authors.workspace = true
+repository.workspace = true
+homepage.workspace = true
+keywords.workspace = true
+documentation.workspace = true
+rust-version.workspace = true
+
+[badges]
+maintenance = { status = "actively-developed" }
+
+[dependencies]
+ng-repo = { path = "../ng-repo", version = "0.1.0" }
+ng-net = { path = "../ng-net", version = "0.1.0" }
+ng-wallet = { path = "../ng-wallet", version = "0.1.0" }
+ng-client-ws = { path = "../ng-client-ws", version = "0.1.0" }
+ng-verifier = { path = "../ng-verifier", version = "0.1.0" }
+async-once-cell = "0.5.3"
+once_cell = "1.17.1"
+serde = { version = "1.0", features = ["derive"] }
+serde_bare = "0.5.0"
+serde_bytes = "0.11.7"
+base64-url = "2.0.0"
+web-time = "0.2.0"
+async-std = { version = "1.12.0", features = ["attributes","unstable"] }
+zeroize = { version = "1.6.0", features = ["zeroize_derive"] }
diff --git a/nextgraph/README.md b/nextgraph/README.md
new file mode 100644
index 0000000..e4471ec
--- /dev/null
+++ b/nextgraph/README.md
@@ -0,0 +1,57 @@
+
+
+
+
+# nextgraph
+
+![MSRV][rustc-image]
+[![Apache 2.0 Licensed][license-image]][license-link]
+[![MIT Licensed][license-image2]][license-link2]
+
+Rust client library of NextGraph
+
+This repository is in active development at [https://git.nextgraph.org/NextGraph/nextgraph-rs](https://git.nextgraph.org/NextGraph/nextgraph-rs), a Gitea instance. For bug reports, issues, merge requests, and in order to join the dev team, please visit the link above and create an account (you can do so with a github account). The [github repo](https://github.com/nextgraph-org/nextgraph-rs) is just a read-only mirror that does not accept issues.
+
+## NextGraph
+
+> NextGraph brings about the convergence between P2P and Semantic Web technologies, towards a decentralized, secure and privacy-preserving cloud, based on CRDTs.
+>
+> This open source ecosystem provides solutions for end-users and software developers alike, wishing to use or create **decentralized** apps featuring: **live collaboration** on rich-text documents, peer to peer communication with **end-to-end encryption**, offline-first, **local-first**, portable and interoperable data, total ownership of data and software, security and privacy. Centered on repositories containing **semantic data** (RDF), **rich text**, and structured data formats like **JSON**, synced between peers belonging to permissioned groups of users, it offers strong eventual consistency, thanks to the use of **CRDTs**. Documents can be linked together, signed, shared securely, queried using the **SPARQL** language and organized into sites and containers.
+>
+> More info here [https://nextgraph.org](https://nextgraph.org)
+
+## Support
+
+Documentation can be found here [https://docs.nextgraph.org](https://docs.nextgraph.org)
+
+And our community forum where you can ask questions is here [https://forum.nextgraph.org](https://forum.nextgraph.org)
+
+## How to use the library
+
+NextGraph is not ready yet. You can subscribe to [our newsletter](https://list.nextgraph.org/subscription/form) to get updates, and support us with a [donation](https://nextgraph.org/donate/).
+
+## License
+
+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.
+
+`SPDX-License-Identifier: Apache-2.0 OR MIT`
+
+### Contributions license
+
+Unless you explicitly state otherwise, any contribution intentionally submitted
+for inclusion in the work by you shall be dual licensed as below, without any
+additional terms or conditions.
+
+---
+
+NextGraph received funding through the [NGI Assure Fund](https://nlnet.nl/project/NextGraph/index.html), a fund established by [NLnet](https://nlnet.nl/) with financial support from the European Commission's [Next Generation Internet](https://ngi.eu/) programme, under the aegis of DG Communications Networks, Content and Technology under grant agreement No 957073.
+
+[rustc-image]: https://img.shields.io/badge/rustc-1.64+-blue.svg
+[license-image]: https://img.shields.io/badge/license-Apache2.0-blue.svg
+[license-link]: https://git.nextgraph.org/NextGraph/nextgraph-rs/raw/branch/master/LICENSE-APACHE2
+[license-image2]: https://img.shields.io/badge/license-MIT-blue.svg
+[license-link2]: https://git.nextgraph.org/NextGraph/nextgraph-rs/src/branch/master/LICENSE-MIT
diff --git a/nextgraph/src/lib.rs b/nextgraph/src/lib.rs
new file mode 100644
index 0000000..4df2a90
--- /dev/null
+++ b/nextgraph/src/lib.rs
@@ -0,0 +1 @@
+pub mod local_broker;
diff --git a/nextgraph/src/local_broker.rs b/nextgraph/src/local_broker.rs
new file mode 100644
index 0000000..ca1589d
--- /dev/null
+++ b/nextgraph/src/local_broker.rs
@@ -0,0 +1,751 @@
+// Copyright (c) 2022-2024 Niko Bonnieure, Par le Peuple, NextGraph.org developers
+// All rights reserved.
+// Licensed under the Apache License, Version 2.0
+//
+// or the MIT license ,
+// at your option. All files in the project carrying such
+// notice may not be copied, modified, or distributed except
+// according to those terms.
+
+use async_once_cell::OnceCell;
+use async_std::sync::{Arc, RwLock};
+use core::fmt;
+use ng_net::connection::{ClientConfig, IConnect, StartConfig};
+use ng_net::types::ClientInfo;
+use once_cell::sync::Lazy;
+use serde_bare::to_vec;
+use std::collections::HashMap;
+use std::fs::{read, write, File, OpenOptions};
+use std::path::PathBuf;
+use zeroize::{Zeroize, ZeroizeOnDrop};
+
+use ng_net::broker::*;
+use ng_repo::errors::NgError;
+use ng_repo::log::*;
+use ng_repo::types::*;
+use ng_wallet::{create_wallet_v0, types::*};
+
+#[cfg(not(target_arch = "wasm32"))]
+use ng_client_ws::remote_ws::ConnectionWebSocket;
+#[cfg(target_arch = "wasm32")]
+use ng_client_ws::remote_ws_wasm::ConnectionWebSocket;
+
+type JsStorageReadFn = dyn Fn(String) -> Result + 'static + Sync + Send;
+type JsStorageWriteFn = dyn Fn(String, String) -> Result<(), NgError> + 'static + Sync + Send;
+type JsCallback = dyn Fn() + 'static + Sync + Send;
+
+pub struct JsStorageConfig {
+ pub local_read: Box,
+ pub local_write: Box,
+ pub session_read: Box,
+ pub session_write: Box,
+}
+
+impl fmt::Debug for JsStorageConfig {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "JsStorageConfig")
+ }
+}
+
+#[derive(Debug)]
+pub enum LocalBrokerConfig {
+ InMemory,
+ BasePath(PathBuf),
+ JsStorage(JsStorageConfig),
+}
+
+impl LocalBrokerConfig {
+ pub fn is_in_memory(&self) -> bool {
+ match self {
+ Self::InMemory => true,
+ _ => false,
+ }
+ }
+ pub fn is_js(&self) -> bool {
+ match self {
+ Self::JsStorage(_) => true,
+ _ => false,
+ }
+ }
+ pub fn js_config(&self) -> Option<&JsStorageConfig> {
+ match self {
+ Self::JsStorage(c) => Some(c),
+ _ => None,
+ }
+ }
+}
+
+//type LastSeqFn = fn(PubKey, u16) -> Result;
+pub type LastSeqFn = dyn Fn(PubKey, u16) -> Result + 'static + Sync + Send;
+
+/// peer_id: PubKey, seq_num:u64, event_ser: vec,
+pub type OutboxWriteFn =
+ dyn Fn(PubKey, u64, Vec) -> Result<(), NgError> + 'static + Sync + Send;
+
+/// peer_id: PubKey,
+pub type OutboxReadFn = dyn Fn(PubKey) -> Result>, NgError> + 'static + Sync + Send;
+
+/// Session Config to initiate a session at a local broker V0
+pub struct SessionConfigV0 {
+ pub user_id: UserId,
+ pub wallet_name: String,
+ // pub last_seq_function: Box,
+ // pub outbox_write_function: Box,
+ // pub outbox_read_function: Box,
+}
+
+/// Session Config to initiate a session at a local broker
+pub enum SessionConfig {
+ V0(SessionConfigV0),
+}
+
+#[derive(Debug)]
+struct Session {
+ config: SessionConfig,
+ peer_key: PrivKey,
+ last_wallet_nonce: u64,
+ //verifier,
+}
+
+impl SessionConfig {
+ pub fn user_id(&self) -> UserId {
+ match self {
+ Self::V0(v0) => v0.user_id,
+ }
+ }
+ pub fn wallet_name(&self) -> String {
+ match self {
+ Self::V0(v0) => v0.wallet_name.clone(),
+ }
+ }
+}
+
+impl fmt::Debug for SessionConfigV0 {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(
+ f,
+ "SessionConfigV0 user={} wallet={}",
+ self.user_id, self.wallet_name
+ )
+ }
+}
+
+impl fmt::Debug for SessionConfig {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ SessionConfig::V0(v0) => v0.fmt(f),
+ }
+ }
+}
+
+#[derive(Debug)]
+struct LocalBroker {
+ pub config: LocalBrokerConfig,
+
+ pub wallets: HashMap,
+
+ pub opened_wallets: HashMap,
+
+ pub sessions: HashMap,
+
+ pub opened_sessions: HashMap,
+}
+
+impl ILocalBroker for LocalBroker {}
+
+impl LocalBroker {
+ fn get_wallet_for_session(&self, config: &SessionConfig) -> Result<&SensitiveWallet, NgError> {
+ match config {
+ SessionConfig::V0(v0) => self
+ .opened_wallets
+ .get(&v0.wallet_name)
+ .ok_or(NgError::WalletNotFound),
+ }
+ }
+}
+
+//static LOCAL_BROKER: OnceCell> = OnceCell::new();
+
+static LOCAL_BROKER: OnceCell>, NgError>> = OnceCell::new(); //Lazy::new(|| Arc::new(RwLock::new(Broker::new())));
+
+pub type ConfigInitFn = dyn Fn() -> LocalBrokerConfig + 'static + Sync + Send;
+
+async fn init_(config: LocalBrokerConfig) -> Result>, NgError> {
+ let wallets = match &config {
+ LocalBrokerConfig::InMemory => HashMap::new(),
+ LocalBrokerConfig::BasePath(base_path) => {
+ // load the wallets and sessions from disk
+ let mut path = base_path.clone();
+ path.push("wallets");
+ let map_ser = read(path);
+ if map_ser.is_ok() {
+ let wallets = LocalWalletStorage::v0_from_vec(&map_ser.unwrap())?;
+ let LocalWalletStorage::V0(wallets) = wallets;
+ wallets
+ } else {
+ HashMap::new()
+ }
+ }
+ LocalBrokerConfig::JsStorage(js_storage_config) => {
+ // load the wallets from JsStorage
+ match (js_storage_config.local_read)("ng_wallets".to_string()) {
+ Err(_) => HashMap::new(),
+ Ok(wallets_string) => {
+ let map_ser = base64_url::decode(&wallets_string)
+ .map_err(|_| NgError::SerializationError)?;
+ let wallets: LocalWalletStorage = serde_bare::from_slice(&map_ser)?;
+ let LocalWalletStorage::V0(v0) = wallets;
+ v0
+ }
+ }
+ }
+ };
+
+ let local_broker = LocalBroker {
+ config,
+ wallets,
+ opened_wallets: HashMap::new(),
+ sessions: HashMap::new(),
+ opened_sessions: HashMap::new(),
+ };
+ //log_debug!("{:?}", &local_broker);
+
+ Ok(Arc::new(RwLock::new(local_broker)))
+}
+
+//-> &'static Result>, NgError>
+pub async fn init_local_broker_with_lazy(config_fn: &Lazy>) {
+ LOCAL_BROKER
+ .get_or_init(async {
+ let config = (&*config_fn)();
+ init_(config).await
+ })
+ .await;
+}
+
+pub async fn init_local_broker(config_fn: Box) {
+ LOCAL_BROKER
+ .get_or_init(async {
+ let config = (config_fn)();
+ init_(config).await
+ })
+ .await;
+}
+
+pub async fn get_all_wallets() -> Result, NgError> {
+ let broker = match LOCAL_BROKER.get() {
+ Some(Err(e)) => {
+ log_err!("LocalBrokerNotInitialized: {}", e);
+ return Err(NgError::LocalBrokerNotInitialized);
+ }
+ None => {
+ log_err!("Not initialized");
+ return Err(NgError::LocalBrokerNotInitialized);
+ }
+ Some(Ok(broker)) => broker.read().await,
+ };
+ Ok(broker.wallets.clone())
+}
+
+pub async fn wallet_create_v0(params: CreateWalletV0) -> Result {
+ let res = create_wallet_v0(params)?;
+ let lws: LocalWalletStorageV0 = (&res).into();
+ wallet_add(lws).await?;
+ Ok(res)
+}
+
+pub async fn reload_wallets() -> Result<(), NgError> {
+ let mut broker = match LOCAL_BROKER.get() {
+ None | Some(Err(_)) => return Err(NgError::LocalBrokerNotInitialized),
+ Some(Ok(broker)) => broker.write().await,
+ };
+ match &broker.config {
+ LocalBrokerConfig::JsStorage(js_config) => {
+ // load the wallets from JsStorage
+ let wallets_string = (js_config.local_read)("ng_wallets".to_string())?;
+ let map_ser =
+ base64_url::decode(&wallets_string).map_err(|_| NgError::SerializationError)?;
+ let wallets: LocalWalletStorage = serde_bare::from_slice(&map_ser)?;
+ let LocalWalletStorage::V0(v0) = wallets;
+ broker.wallets.extend(v0);
+ }
+ _ => {}
+ }
+ Ok(())
+}
+
+pub async fn wallet_add(lws: LocalWalletStorageV0) -> Result<(), NgError> {
+ let mut broker = match LOCAL_BROKER.get() {
+ None | Some(Err(_)) => return Err(NgError::LocalBrokerNotInitialized),
+ Some(Ok(broker)) => broker.write().await,
+ };
+ if !lws.in_memory && broker.config.is_in_memory() {
+ return Err(NgError::CannotSaveWhenInMemoryConfig);
+ }
+ if broker.wallets.get(&lws.wallet.name()).is_some() {
+ return Err(NgError::WalletAlreadyAdded);
+ }
+ let in_memory = lws.in_memory;
+ broker.wallets.insert(lws.wallet.name(), lws);
+ if in_memory {
+ // if broker.config.is_js() {
+ // (broker.config.js_config().unwrap().wallets_in_mem_changed)();
+ // }
+ } else {
+ match &broker.config {
+ LocalBrokerConfig::JsStorage(js_config) => {
+ // JS save
+ let lws_ser = LocalWalletStorage::v0_to_vec(&broker.wallets);
+ let encoded = base64_url::encode(&lws_ser);
+ (js_config.local_write)("ng_wallets".to_string(), encoded)?;
+ }
+ LocalBrokerConfig::BasePath(base_path) => {
+ // save on disk
+ // TODO: use https://lib.rs/crates/keyring instead of AppLocalData on Tauri apps
+ let mut path = base_path.clone();
+ std::fs::create_dir_all(path.clone()).unwrap();
+ path.push("wallets");
+
+ let lws_ser = LocalWalletStorage::v0_to_vec(&broker.wallets);
+ let r = write(path.clone(), &lws_ser);
+ if r.is_err() {
+ log_debug!("write {:?} {}", path, r.unwrap_err());
+ return Err(NgError::IoError);
+ }
+ }
+ _ => panic!("wrong LocalBrokerConfig"),
+ }
+ }
+ Ok(())
+}
+
+pub async fn wallet_read_file(file: Vec) -> Result {
+ let ngf: NgFile = file.try_into()?;
+ if let NgFile::V0(NgFileV0::Wallet(wallet)) = ngf {
+ 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)
+ }
+ } else {
+ Err(NgError::InvalidFileFormat)
+ }
+}
+
+pub async fn wallet_download_file(wallet_name: &String) -> Result, NgError> {
+ let broker = match LOCAL_BROKER.get() {
+ None | Some(Err(_)) => return Err(NgError::LocalBrokerNotInitialized),
+ Some(Ok(broker)) => broker.read().await,
+ };
+ // check that the wallet exists
+ match broker.wallets.get(wallet_name) {
+ None => Err(NgError::WalletNotFound),
+ Some(lws) => Ok(to_vec(&NgFile::V0(NgFileV0::Wallet(lws.wallet.clone()))).unwrap()),
+ }
+}
+
+pub fn wallet_open_with_pazzle(
+ wallet: Wallet,
+ pazzle: Vec,
+ pin: [u8; 4],
+) -> Result {
+ let opened_wallet = ng_wallet::open_wallet_with_pazzle(wallet, pazzle, pin)?;
+
+ Ok(opened_wallet)
+}
+
+pub async fn wallet_import(
+ encrypted_wallet: Wallet,
+ mut opened_wallet: SensitiveWallet,
+ in_memory: bool,
+) -> Result {
+ {
+ // in a block to release lock before calling wallet_add
+ let broker = match LOCAL_BROKER.get() {
+ None | Some(Err(_)) => return Err(NgError::LocalBrokerNotInitialized),
+ Some(Ok(broker)) => broker.read().await,
+ };
+
+ let wallet_name = encrypted_wallet.name();
+ if broker.wallets.get(&wallet_name).is_some() {
+ return Err(NgError::WalletAlreadyOpened);
+ }
+ }
+
+ let lws = opened_wallet.import_v0(encrypted_wallet, in_memory)?;
+
+ wallet_add(lws).await?;
+
+ wallet_was_opened(opened_wallet).await
+}
+
+pub async fn wallet_was_opened(mut wallet: SensitiveWallet) -> Result {
+ let mut broker = match LOCAL_BROKER.get() {
+ None | Some(Err(_)) => return Err(NgError::LocalBrokerNotInitialized),
+ Some(Ok(broker)) => broker.write().await,
+ };
+
+ if broker.opened_wallets.get(&wallet.id()).is_some() {
+ return Err(NgError::WalletAlreadyOpened);
+ }
+
+ match broker.wallets.get(&(wallet.id())) {
+ Some(lws) => {
+ if wallet.client().is_none() {
+ // this case happens when the wallet is opened and not when it is imported (as the client is already there)
+ wallet.set_client(lws.to_client_v0(wallet.privkey())?);
+ }
+ }
+ None => {
+ return Err(NgError::WalletNotFound);
+ }
+ }
+ let client = wallet.client().as_ref().unwrap().clone();
+ broker.opened_wallets.insert(wallet.id(), wallet);
+ Ok(client)
+}
+
+pub async fn session_start(config: SessionConfig) -> Result {
+ let mut broker = match LOCAL_BROKER.get() {
+ None | Some(Err(_)) => return Err(NgError::LocalBrokerNotInitialized),
+ Some(Ok(broker)) => broker.write().await,
+ };
+
+ let wallet_name = config.wallet_name();
+ let wallet_id: PubKey = (*wallet_name).try_into()?;
+ let user_id = config.user_id();
+
+ match broker.opened_wallets.get(&wallet_name) {
+ None => return Err(NgError::WalletNotFound),
+ Some(wallet) => {
+ if !wallet.has_user(&user_id) {
+ return Err(NgError::NotFound);
+ }
+
+ let session = match broker.sessions.get(&user_id) {
+ Some(session) => session,
+ None => {
+ // creating the session now
+ let closed_wallet = broker.wallets.get(&wallet_name).unwrap();
+ if closed_wallet.in_memory {
+ let session = SessionPeerStorageV0::new(user_id);
+ broker.sessions.insert(user_id, session);
+ broker.sessions.get(&user_id).unwrap()
+ } else {
+ // first check if there is a saved SessionWalletStorage
+ let mut sws = match &broker.config {
+ LocalBrokerConfig::InMemory => panic!("cannot open saved session"),
+ LocalBrokerConfig::JsStorage(js_config) => {
+ // read session wallet storage from JsStorage
+ let res =
+ (js_config.session_read)(format!("ng_wallet@{}", wallet_name));
+ match res {
+ Ok(string) => {
+ let decoded = base64_url::decode(&string)
+ .map_err(|_| NgError::SerializationError)?;
+ Some(SessionWalletStorageV0::dec_session(
+ wallet.privkey(),
+ &decoded,
+ )?)
+ }
+ Err(_) => None,
+ }
+ }
+ LocalBrokerConfig::BasePath(base_path) => {
+ // read session wallet storage from disk
+ let mut path = base_path.clone();
+ path.push("sessions");
+ path.push(wallet_name.clone());
+ let res = read(path);
+ if res.is_ok() {
+ Some(SessionWalletStorageV0::dec_session(
+ wallet.privkey(),
+ &res.unwrap(),
+ )?)
+ } else {
+ None
+ }
+ }
+ };
+ let (session, new_sws) = match &mut sws {
+ None => {
+ let (s, sws_ser) = SessionWalletStorageV0::create_new_session(
+ &wallet_id, user_id,
+ )?;
+ broker.sessions.insert(user_id, s);
+ (broker.sessions.get(&user_id).unwrap(), sws_ser)
+ }
+ Some(sws) => {
+ match sws.users.get(&user_id.to_string()) {
+ Some(sps) => {
+ broker.sessions.insert(user_id, sps.clone());
+ (broker.sessions.get(&user_id).unwrap(), vec![])
+ }
+ None => {
+ // the user was not found in the SWS. we need to create a SPS, add it, encrypt and serialize the new SWS,
+ // add the SPS to broker.sessions, and return the newly created SPS and the new SWS encryped serialization
+ let sps = SessionPeerStorageV0::new(user_id);
+ sws.users.insert(user_id.to_string(), sps.clone());
+ let encrypted = sws.enc_session(&wallet_id)?;
+ broker.sessions.insert(user_id, sps);
+ (broker.sessions.get(&user_id).unwrap(), encrypted)
+ }
+ }
+ }
+ };
+ // save the new sws
+ if new_sws.len() > 0 {
+ match &broker.config {
+ LocalBrokerConfig::InMemory => {
+ panic!("cannot save session when InMemory mode")
+ }
+ LocalBrokerConfig::JsStorage(js_config) => {
+ // save session wallet storage to JsStorage
+ let encoded = base64_url::encode(&new_sws);
+ (js_config.session_write)(
+ format!("ng_wallet@{}", wallet_name),
+ encoded,
+ )?;
+ }
+ LocalBrokerConfig::BasePath(base_path) => {
+ // save session wallet storage to disk
+ let mut path = base_path.clone();
+ path.push("sessions");
+ std::fs::create_dir_all(path.clone()).unwrap();
+ path.push(wallet_name);
+ //log_debug!("{}", path.clone().display());
+ write(path.clone(), &new_sws).map_err(|_| NgError::IoError)?;
+ }
+ }
+ }
+ session
+ }
+ }
+ };
+ let session = session.clone();
+ broker.opened_sessions.insert(
+ user_id,
+ Session {
+ config,
+ peer_key: session.peer_key.clone(),
+ last_wallet_nonce: session.last_wallet_nonce,
+ },
+ );
+ // FIXME: is this return value useful ?
+ Ok(session)
+ }
+ }
+}
+
+use web_time::SystemTime;
+fn get_unix_time() -> f64 {
+ SystemTime::now()
+ .duration_since(SystemTime::UNIX_EPOCH)
+ .unwrap()
+ .as_millis() as f64
+}
+
+/// Result is a list of (user_id, server_id, server_ip, error, since_date)
+pub async fn user_connect(
+ info: ClientInfo,
+ user_id: UserId,
+ location: Option,
+) -> Result, f64)>, NgError> {
+ let local_broker = match LOCAL_BROKER.get() {
+ None | Some(Err(_)) => return Err(NgError::LocalBrokerNotInitialized),
+ Some(Ok(broker)) => broker.read().await,
+ };
+
+ let session = local_broker
+ .opened_sessions
+ .get(&user_id)
+ .ok_or(NgError::SessionNotFound)?;
+ let wallet = local_broker.get_wallet_for_session(&session.config)?;
+
+ let mut result: Vec<(String, String, String, Option, f64)> = Vec::new();
+ let arc_cnx: Arc> = Arc::new(Box::new(ConnectionWebSocket {}));
+
+ match wallet {
+ SensitiveWallet::V0(wallet) => {
+ let client = wallet.client.as_ref().unwrap();
+ let client_id = client.id;
+ let client_priv = &client.sensitive_client_storage.priv_key;
+ let client_name = &client.name;
+ let auto_open = &client.auto_open;
+ // log_info!(
+ // "XXXX {} name={:?} auto_open={:?} {:?}",
+ // client_id.to_string(),
+ // client_name,
+ // auto_open,
+ // wallet
+ // );
+ for user in auto_open {
+ let user_id = user.to_string();
+ let peer_key = &session.peer_key;
+ let peer_id = peer_key.to_pub();
+ let site = wallet.sites.get(&user_id);
+ if site.is_none() {
+ result.push((
+ user_id,
+ "".into(),
+ "".into(),
+ Some("Site is missing".into()),
+ get_unix_time(),
+ ));
+ continue;
+ }
+ let site = site.unwrap();
+ let user_priv = site.get_individual_user_priv_key().unwrap();
+ let core = site.cores[0]; //TODO: cycle the other cores if failure to connect (failover)
+ let server_key = core.0;
+ let broker = wallet.brokers.get(&core.0.to_string());
+ if broker.is_none() {
+ result.push((
+ user_id,
+ core.0.to_string(),
+ "".into(),
+ Some("Broker is missing".into()),
+ get_unix_time(),
+ ));
+ continue;
+ }
+ let brokers = broker.unwrap();
+ let mut tried: Option<(String, String, String, Option, f64)> = None;
+ //TODO: on tauri (or forward in local broker, or CLI), prefer a Public to a Domain. Domain always comes first though, so we need to reorder the list
+ //TODO: use site.bootstraps to order the list of brokerInfo.
+ for broker_info in brokers {
+ match broker_info {
+ BrokerInfoV0::ServerV0(server) => {
+ let url = server.get_ws_url(&location).await;
+ log_debug!("URL {:?}", url);
+ //Option<(String, Vec)>
+ if url.is_some() {
+ let url = url.unwrap();
+ if url.1.len() == 0 {
+ // TODO deal with Box(Dyn)Public -> tunnel, and on tauri/forward/CLIs, deal with all Box -> direct connections (when url.1.len is > 0)
+ let res = BROKER
+ .write()
+ .await
+ .connect(
+ arc_cnx.clone(),
+ peer_key.clone(),
+ peer_id,
+ server_key,
+ StartConfig::Client(ClientConfig {
+ url: url.0.clone(),
+ name: client_name.clone(),
+ user_priv: user_priv.clone(),
+ client_priv: client_priv.clone(),
+ info: info.clone(),
+ registration: Some(core.1),
+ }),
+ )
+ .await;
+ log_debug!("broker.connect : {:?}", res);
+
+ tried = Some((
+ user_id.clone(),
+ core.0.to_string(),
+ url.0.into(),
+ match &res {
+ Ok(_) => None,
+ Err(e) => Some(e.to_string()),
+ },
+ get_unix_time(),
+ ));
+ }
+ if tried.is_some() && tried.as_ref().unwrap().3.is_none() {
+ // successful. we can stop here
+ break;
+ } else {
+ log_debug!("Failed connection {:?}", tried);
+ }
+ }
+ }
+ // Core information is discarded
+ _ => {}
+ }
+ }
+ if tried.is_none() {
+ tried = Some((
+ user_id,
+ core.0.to_string(),
+ "".into(),
+ Some("No broker found".into()),
+ get_unix_time(),
+ ));
+ }
+ result.push(tried.unwrap());
+ }
+ }
+ }
+ Ok(result)
+}
+
+pub async fn session_stop(user_id: UserId) -> Result<(), NgError> {
+ let mut broker = match LOCAL_BROKER.get() {
+ None | Some(Err(_)) => return Err(NgError::LocalBrokerNotInitialized),
+ Some(Ok(broker)) => broker.write().await,
+ };
+
+ if broker.opened_sessions.remove(&user_id).is_some() {
+ // TODO: change the logic here once it will be possible to have several users connected at the same time
+ Broker::close_all_connections().await;
+ }
+
+ Ok(())
+}
+
+pub async fn user_disconnect(user_id: UserId) -> Result<(), NgError> {
+ let broker = match LOCAL_BROKER.get() {
+ None | Some(Err(_)) => return Err(NgError::LocalBrokerNotInitialized),
+ Some(Ok(broker)) => broker.read().await,
+ };
+
+ if broker.opened_sessions.get(&user_id).is_some() {
+ // TODO: change the logic here once it will be possible to have several users connected at the same time
+ Broker::close_all_connections().await;
+ }
+
+ Ok(())
+}
+
+pub async fn wallet_close(wallet_name: String) -> Result<(), NgError> {
+ let mut broker = match LOCAL_BROKER.get() {
+ None | Some(Err(_)) => return Err(NgError::LocalBrokerNotInitialized),
+ Some(Ok(broker)) => broker.write().await,
+ };
+
+ match broker.opened_wallets.remove(&wallet_name) {
+ Some(mut wallet) => {
+ for user in wallet.sites() {
+ let key: PubKey = (user.as_str()).try_into().unwrap();
+ broker.opened_sessions.remove(&key);
+ }
+ wallet.zeroize();
+ }
+ None => return Err(NgError::WalletNotFound),
+ }
+
+ Broker::close_all_connections().await;
+
+ Ok(())
+}
+
+pub async fn wallet_remove(wallet_name: String) -> Result<(), NgError> {
+ let mut broker = match LOCAL_BROKER.get() {
+ None | Some(Err(_)) => return Err(NgError::LocalBrokerNotInitialized),
+ Some(Ok(broker)) => broker.write().await,
+ };
+
+ todo!();
+ // should close the wallet, then remove all the saved sessions and remove the wallet
+
+ Ok(())
+}
diff --git a/ng-app/src-tauri/Cargo.toml b/ng-app/src-tauri/Cargo.toml
index 9c84936..fb238f7 100644
--- a/ng-app/src-tauri/Cargo.toml
+++ b/ng-app/src-tauri/Cargo.toml
@@ -1,18 +1,22 @@
[package]
name = "ng-app"
-version = "0.1.0"
+# version = "0.1.0"
description = "NextGraph App"
-authors = ["Niko PLP "]
-license = "MIT/Apache-2.0"
-repository = "https://git.nextgraph.org/NextGraph/nextgraph-rs"
-edition = "2021"
+publish = false
+version.workspace = true
+edition.workspace = true
+license.workspace = true
+authors.workspace = true
+repository.workspace = true
+homepage.workspace = true
+keywords.workspace = true
+documentation.workspace = true
+rust-version.workspace = true
[lib]
name = "nativelib"
crate-type = ["staticlib", "cdylib", "rlib"]
-# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
-
[build-dependencies]
tauri-build = { version = "2.0.0-alpha.8", features = [] }
# tauri-macros = { version = "=2.0.0-alpha.6" }
@@ -25,10 +29,11 @@ tauri = { version = "2.0.0-alpha.14", features = [] }
# tauri = { git = "https://github.com/simonhyll/tauri.git", branch="fix/ipc-mixup", features = [] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
-p2p-repo = { path = "../../p2p-repo" }
-p2p-net = { path = "../../p2p-net" }
-p2p-client-ws = { path = "../../p2p-client-ws" }
+ng-repo = { path = "../../ng-repo" }
+ng-net = { path = "../../ng-net" }
+ng-client-ws = { path = "../../ng-client-ws" }
ng-wallet = { path = "../../ng-wallet" }
+nextgraph = { path = "../../nextgraph" }
async-std = { version = "1.12.0", features = ["attributes", "unstable"] }
# tauri-plugin-window = { git = "https://git.nextgraph.org/NextGraph/plugins-workspace.git", branch="window-alpha.1-nextgraph" }
tauri-plugin-window = "2.0.0-alpha.1"
diff --git a/ng-app/src-tauri/src/lib.rs b/ng-app/src-tauri/src/lib.rs
index 933369e..9fb7e10 100644
--- a/ng-app/src-tauri/src/lib.rs
+++ b/ng-app/src-tauri/src/lib.rs
@@ -7,18 +7,20 @@
// notice may not be copied, modified, or distributed except
// according to those terms.
use async_std::stream::StreamExt;
+use nextgraph::local_broker::*;
+use ng_net::broker::*;
+use ng_net::types::{ClientInfo, CreateAccountBSP, Invitation};
+use ng_net::utils::{decode_invitation_string, spawn_and_log_error, Receiver, ResultSend};
+use ng_repo::errors::NgError;
+use ng_repo::log::*;
+use ng_repo::types::*;
use ng_wallet::types::*;
use ng_wallet::*;
-use p2p_client_ws::remote_ws::ConnectionWebSocket;
-use p2p_net::broker::*;
-use p2p_net::types::{ClientInfo, CreateAccountBSP, Invitation};
-use p2p_net::utils::{decode_invitation_string, spawn_and_log_error, Receiver, ResultSend};
-use p2p_repo::errors::NgError;
-use p2p_repo::log::*;
-use p2p_repo::types::*;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
-use std::fs::{read, write};
+use std::fs::{read, write, File, OpenOptions};
+use std::io::Write;
+use std::path::PathBuf;
use tauri::scope::ipc::RemoteDomainAccessScope;
use tauri::utils::config::WindowConfig;
use tauri::{path::BaseDirectory, App, Manager, Window};
@@ -30,15 +32,27 @@ pub use mobile::*;
pub type SetupHook = Box Result<(), Box> + Send>;
-// Learn more about Tauri commands at https://tauri.app/v1/guides/features/command
-// #[tauri::command(rename_all = "snake_case")]
-// fn greet(name: &str) -> String {
-// format!("Hello, {}! You've been greeted from Rust!", name)
-// }
-
#[tauri::command(rename_all = "snake_case")]
async fn test(app: tauri::AppHandle) -> Result<(), ()> {
- log_debug!("test is {}", BROKER.read().await.test());
+ let path = app
+ .path()
+ .resolve("", BaseDirectory::AppLocalData)
+ .map_err(|_| NgError::SerializationError)
+ .unwrap();
+ init_local_broker(Box::new(move || LocalBrokerConfig::BasePath(path.clone()))).await;
+
+ // init_local_broker(&Lazy::new(|| {
+ // Box::new(move || LocalBrokerConfig::BasePath(path.clone()))
+ // }))
+ // .await;
+ //pub type LastSeqFn = dyn Fn(PubKey, u16) -> Result + 'static + Sync + Send;
+
+ // let test: Box = Box::new(move |peer_id: PubKey, qty: u16| {
+ // take_some_peer_last_seq_numbers(peer_id, qty, path.clone())
+ // });
+ //pub type ConfigInitFn = dyn Fn() -> LocalBrokerConfig + 'static + Sync + Send;
+
+ //log_debug!("test is {}", BROKER.read().await.test());
let path = app
.path()
.resolve("storage", BaseDirectory::AppLocalData)
@@ -51,227 +65,270 @@ async fn test(app: tauri::AppHandle) -> Result<(), ()> {
#[tauri::command(rename_all = "snake_case")]
async fn wallet_gen_shuffle_for_pazzle_opening(pazzle_length: u8) -> Result {
- log_debug!(
- "wallet_gen_shuffle_for_pazzle_opening from rust {}",
- pazzle_length
- );
+ // log_debug!(
+ // "wallet_gen_shuffle_for_pazzle_opening from rust {}",
+ // pazzle_length
+ // );
Ok(gen_shuffle_for_pazzle_opening(pazzle_length))
}
#[tauri::command(rename_all = "snake_case")]
async fn wallet_gen_shuffle_for_pin() -> Result, ()> {
- log_debug!("wallet_gen_shuffle_for_pin from rust");
+ //log_debug!("wallet_gen_shuffle_for_pin from rust");
Ok(gen_shuffle_for_pin())
}
#[tauri::command(rename_all = "snake_case")]
-async fn wallet_open_wallet_with_pazzle(
+async fn wallet_open_with_pazzle(
wallet: Wallet,
pazzle: Vec,
pin: [u8; 4],
-) -> Result {
- log_debug!("wallet_open_wallet_with_pazzle from rust {:?}", pazzle);
- open_wallet_with_pazzle(wallet, pazzle, pin).map_err(|e| e.to_string())
+ app: tauri::AppHandle,
+) -> Result {
+ //log_debug!("wallet_open_with_pazzle from rust {:?}", pazzle);
+ let wallet = nextgraph::local_broker::wallet_open_with_pazzle(wallet, pazzle, pin)
+ .map_err(|e| e.to_string())?;
+ Ok(wallet)
}
#[tauri::command(rename_all = "snake_case")]
-async fn wallet_create_wallet(
+async fn wallet_download_file(wallet_name: String, app: tauri::AppHandle) -> Result<(), String> {
+ let ser = nextgraph::local_broker::wallet_download_file(&wallet_name)
+ .await
+ .map_err(|e| e.to_string())?;
+
+ // save wallet file to Downloads folder
+ let path = app
+ .path()
+ .resolve(
+ format!("wallet-{}.ngw", wallet_name),
+ BaseDirectory::Download,
+ )
+ .unwrap();
+ write(path, &ser).map_err(|e| e.to_string())?;
+ Ok(())
+}
+
+#[tauri::command(rename_all = "snake_case")]
+async fn wallet_create(
mut params: CreateWalletV0,
app: tauri::AppHandle,
-) -> Result<(CreateWalletResultV0, Option), String> {
- //log_debug!("wallet_create_wallet from rust {:?}", params);
+) -> Result {
+ //log_debug!("wallet_create from rust {:?}", params);
params.result_with_wallet_file = !params.local_save;
let local_save = params.local_save;
- let res = create_wallet_v0(params).await.map_err(|e| e.to_string());
- if res.is_ok() {
- let mut cwr = res.unwrap();
- if local_save {
- // save in local store
-
- let session = save_wallet_locally(&cwr, app).await;
- if session.is_err() {
- return Err("Cannot save wallet locally".to_string());
- }
- return Ok((cwr, Some(session.unwrap())));
- } else {
- // save wallet file to Downloads folder
- let path = app
- .path()
- .resolve(
- format!("wallet-{}.ngw", cwr.wallet_name),
- BaseDirectory::Download,
- )
- .unwrap();
- let _r = write(path, &cwr.wallet_file);
- cwr.wallet_file = vec![];
- return Ok((cwr, None));
- }
+ let mut cwr = nextgraph::local_broker::wallet_create_v0(params)
+ .await
+ .map_err(|e| e.to_string())?;
+ if !local_save {
+ // save wallet file to Downloads folder
+ let path = app
+ .path()
+ .resolve(
+ format!("wallet-{}.ngw", cwr.wallet_name),
+ BaseDirectory::Download,
+ )
+ .unwrap();
+ let _r = write(path, &cwr.wallet_file);
+ cwr.wallet_file = vec![];
}
- Err(res.unwrap_err())
+ Ok(cwr)
}
-async fn save_wallet_locally(
- res: &CreateWalletResultV0,
- app: tauri::AppHandle,
-) -> Result {
- let path = app
- .path()
- .resolve("wallets", BaseDirectory::AppLocalData)
- .map_err(|_| ())?;
- let mut wallets: HashMap =
- get_wallets_from_localstorage(app.clone())
- .await
- .unwrap_or(Some(HashMap::new()))
- .unwrap_or(HashMap::new());
- // check that the wallet is not already present in localStorage
- if wallets.get(&res.wallet_name).is_none() {
- let lws: LocalWalletStorageV0 = res.into();
- wallets.insert(res.wallet_name.clone(), lws);
- let lws_ser = LocalWalletStorage::v0_to_vec(wallets);
- let r = write(path.clone(), &lws_ser);
- if r.is_err() {
- log_debug!("write {:?} {}", path, r.unwrap_err());
- return Err(());
+// // TODO: use https://lib.rs/crates/keyring instead of AppLocalData
+// async fn save_wallet_locally(
+// res: &CreateWalletResultV0,
+// app: tauri::AppHandle,
+// ) -> Result {
+// let path = app
+// .path()
+// .resolve("wallets", BaseDirectory::AppLocalData)
+// .map_err(|_| ())?;
+// let mut wallets: HashMap =
+// get_all_wallets().unwrap_or(HashMap::new());
+// // check that the wallet is not already present in localStorage
+// if wallets.get(&res.wallet_name).is_none() {
+// let lws: LocalWalletStorageV0 = res.into();
+// wallets.insert(res.wallet_name.clone(), lws);
+// let lws_ser = LocalWalletStorage::v0_to_vec(&wallets);
+// let r = write(path.clone(), &lws_ser);
+// if r.is_err() {
+// log_debug!("write {:?} {}", path, r.unwrap_err());
+// return Err(());
+// }
+// let sws = save_new_session(&res.wallet_name, res.wallet.id(), res.user, app)?;
+// Ok(sws)
+// } else {
+// Err(())
+// }
+// }
+
+fn take_some_peer_last_seq_numbers(
+ peer_id: PubKey,
+ qty: u16,
+ mut path: PathBuf,
+) -> Result {
+ std::fs::create_dir_all(path.clone()).unwrap();
+ path.push(peer_id.to_string());
+ log_debug!("{}", path.display());
+
+ let file = read(path.clone());
+ let (mut file_save, val) = match file {
+ Ok(ser) => {
+ let old_val = match SessionPeerLastSeq::deser(&ser)? {
+ SessionPeerLastSeq::V0(v) => v,
+ _ => unimplemented!(),
+ };
+ (
+ OpenOptions::new()
+ .write(true)
+ .open(path)
+ .map_err(|_| NgError::SerializationError)?,
+ old_val,
+ )
}
- let sws = save_new_session(&res.wallet_name, res.wallet.id(), res.user, app)?;
- Ok(sws)
- } else {
- Err(())
- }
+ Err(_) => (
+ File::create(path).map_err(|_| NgError::SerializationError)?,
+ 0,
+ ),
+ };
+ let new_val = val + qty as u64;
+ let spls = SessionPeerLastSeq::V0(new_val);
+ let ser = spls.ser()?;
+ file_save
+ .write_all(&ser)
+ .map_err(|_| NgError::SerializationError)?;
+
+ file_save
+ .sync_data()
+ .map_err(|_| NgError::SerializationError)?;
+
+ Ok(val)
}
#[tauri::command(rename_all = "snake_case")]
-async fn wallet_open_file(file: Vec, app: tauri::AppHandle) -> Result {
- let ngf: NgFile = file.try_into().map_err(|e: NgError| e.to_string())?;
- if let NgFile::V0(NgFileV0::Wallet(wallet)) = ngf {
- let mut wallets: HashMap =
- get_wallets_from_localstorage(app.clone())
- .await
- .unwrap_or(Some(HashMap::new()))
- .unwrap_or(HashMap::new());
- // check that the wallet is not already present in localStorage
- let wallet_name = wallet.name();
- if wallets.get(&wallet_name).is_none() {
- Ok(wallet)
- } else {
- Err("Wallet already present on this device".to_string())
- }
- } else {
- Err("File does not contain a wallet".to_string())
- }
+async fn wallet_read_file(file: Vec, app: tauri::AppHandle) -> Result {
+ nextgraph::local_broker::wallet_read_file(file)
+ .await
+ .map_err(|e: NgError| e.to_string())
+}
+
+#[tauri::command(rename_all = "snake_case")]
+async fn wallet_was_opened(
+ opened_wallet: SensitiveWallet,
+ app: tauri::AppHandle,
+) -> Result {
+ nextgraph::local_broker::wallet_was_opened(opened_wallet)
+ .await
+ .map_err(|e: NgError| e.to_string())
}
#[tauri::command(rename_all = "snake_case")]
async fn wallet_import(
- previous_wallet: Wallet,
- opened_wallet: EncryptedWallet,
+ encrypted_wallet: Wallet,
+ opened_wallet: SensitiveWallet,
+ in_memory: bool,
app: tauri::AppHandle,
-) -> Result<(String, ClientV0), String> {
- let path = app
- .path()
- .resolve("wallets", BaseDirectory::AppLocalData)
- .map_err(|_| "wallet directory error".to_string())?;
- let mut wallets: HashMap =
- get_wallets_from_localstorage(app.clone())
- .await
- .unwrap_or(Some(HashMap::new()))
- .unwrap_or(HashMap::new());
- // check that the wallet is not already present in localStorage
- let EncryptedWallet::V0(mut opened_wallet_v0) = opened_wallet;
- let wallet_name = opened_wallet_v0.wallet_id.clone();
- if wallets.get(&wallet_name).is_none() {
- let session = save_new_session(
- &wallet_name,
- opened_wallet_v0.wallet_privkey.to_pub(),
- opened_wallet_v0.personal_site,
- app,
- )
- .map_err(|_| "Cannot create new session".to_string())?;
- let (wallet, client_id, client) = opened_wallet_v0
- .import(previous_wallet, session)
- .map_err(|e| e.to_string())?;
- let lws = LocalWalletStorageV0::new(wallet, &client);
-
- wallets.insert(wallet_name, lws);
- let lws_ser = LocalWalletStorage::v0_to_vec(wallets);
- let r = write(path.clone(), &lws_ser);
- if r.is_err() {
- log_debug!("write {:?} {}", path, r.unwrap_err());
- Err("Write error".to_string())
- } else {
- Ok((client_id, client))
- }
- } else {
- Err("Already present on this device".to_string())
- }
+) -> Result {
+ nextgraph::local_broker::wallet_import(encrypted_wallet, opened_wallet, in_memory)
+ .await
+ .map_err(|e: NgError| e.to_string())
+
+ // let path = app
+ // .path()
+ // .resolve("wallets", BaseDirectory::AppLocalData)
+ // .map_err(|_| "wallet directory error".to_string())?;
+ // let mut wallets: HashMap =
+ // get_all_wallets().unwrap_or(HashMap::new());
+ // // check that the wallet is not already present in localStorage
+ // let SensitiveWallet::V0(mut opened_wallet_v0) = opened_wallet;
+ // let wallet_name = opened_wallet_v0.wallet_id.clone();
+ // if wallets.get(&wallet_name).is_none() {
+ // let session = save_new_session(
+ // &wallet_name,
+ // opened_wallet_v0.wallet_privkey.to_pub(),
+ // opened_wallet_v0.personal_site,
+ // app,
+ // )
+ // .map_err(|_| "Cannot create new session".to_string())?;
+ // let lws = opened_wallet_v0
+ // .import(previous_wallet)
+ // .map_err(|e| e.to_string())?;
+ // //let lws = LocalWalletStorageV0::new(wallet, &client);
+
+ // wallets.insert(wallet_name, lws);
+ // let lws_ser = LocalWalletStorage::v0_to_vec(&wallets);
+ // let r = write(path.clone(), &lws_ser);
+ // if r.is_err() {
+ // log_debug!("write {:?} {}", path, r.unwrap_err());
+ // Err("Write error".to_string())
+ // } else {
+ // Ok(client)
+ // }
+ // } else {
+ // Err("Already present on this device".to_string())
+ // }
}
#[tauri::command(rename_all = "snake_case")]
-async fn get_wallets_from_localstorage(
+async fn get_wallets(
app: tauri::AppHandle,
-) -> Result
- For each category of images, you will be presented with the 15 possible
- image choices. The categories are shuffled at every login. They will not
- always appear in the same order.
+ For each one of the 9 categories of images, you will be presented with
+ the 15 possible image choices. The categories are shuffled at every
+ login. They will not always appear in the same order.
At each category, only one of the 15 displayed choices is the correct
@@ -230,7 +255,7 @@
The 15 images are shuffled too, they will not appear at the same
position at each login. On a computer, you can also use the tab key on
your keyboard to move to the desired item on the screen, then press the
- space bar.
+ space bar to select each one.
Once you completed the last category, you will be presented with all the
@@ -254,11 +279,11 @@
-
+
{#if !loaded}
- Loading...
+ Loading pazzle...
+ {#if for_import}
+
+ Do you trust this device?
+
+ Save my wallet on this device
+
+
+ If you do, if this device is yours or is used by few trusted persons of
+ your family or workplace, and you would like to login again from this
+ device in the future, then you can save your wallet on this device. To
+ the contrary, if this device is public and shared by strangers, do not
+ save your wallet here. {#if !tauri_platform}By selecting this option,
+ you agree to save some cookies on your browser.{/if}
+